If you've written PHP inside a .php file and noticed that omitting the closing ?> tag doesn't break anything, you're not imagining it. The file runs perfectly, output is correct, and PHP doesn't complain. VS Code might not even flag it as an issue.
So what's going on?
First, Understand What ?> Actually Does
The closing tag ?> is an instruction to the PHP parser — it says:
"Stop processing PHP here. Everything after this point is raw HTML/text — send it directly to the output buffer."
So in a mixed HTML + PHP file, it makes perfect sense:
<?php $name = "Gagan"; ?> <h1>Hello, <?php echo $name; ?></h1> <p>Welcome to my site.</p>
Here ?> is necessary because you're switching between PHP mode and HTML mode multiple times. The parser needs to know when to stop treating content as PHP code.
So Why Does a Pure PHP File Work Without It?
When your file contains only PHP — no HTML after the last line — the parser reaches the end of the file and simply stops. There is no HTML waiting to be sent, nothing to switch to. The closing tag's job was already done by the file ending.
PHP's parser behavior is:
"If I reach EOF while still in PHP mode, that's fine. I'll just stop here."
<?php
class UserService { public function getUser(int $id): array { return ['id' => $id, 'name' => 'Gagan']; } }
No ?> at the bottom. PHP reads the opening <?php, processes everything, hits EOF, and exits cleanly. Full success.
The Real Reason You Should OMIT ?>
This is where it gets interesting — and practical.
The Whitespace / Newline Problem
When you add ?> at the end of a file, PHP does something you might not expect. Any whitespace, newline, or blank line that exists after the closing tag gets sent to the output buffer as raw content.
<?php
function add(int $a, int $b): int { return $a + $b; }
?>
See that blank line after ?>? PHP sends that as output. A literal \n character is now part of your response — before any of your actual intended output.
Now imagine this file is included in a larger application:
<?php // some-functions.php is included here require_once 'some-functions.php';
header('Content-Type: application/json'); echo json_encode(['status' => 'ok']);
You'll get this error:
Warning: Cannot modify header information - headers already sent by
(output started at some-functions.php:9)
A single invisible newline broke your headers. This kind of bug is notoriously difficult to track down, especially in large codebases where files are required/included across many layers.
The fix? Never add ?> in the first place.
What PSR-12 Says
PHP-FIG's PSR-12 standard is explicit about this:
"A file SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both. The closing ?> tag MUST be omitted from files containing only PHP."
Laravel, Symfony, WordPress (modern code), and virtually every professional PHP codebase follow this rule. If you open any controller, model, or service class in Laravel, you will never see a closing ?>.
When IS ?> Required?
Only in one scenario — files that intentionally mix PHP and HTML, like template files:
<!DOCTYPE html> <html>
<head> <title><?php echo $pageTitle; ?></title> </head>
<body> <?php if ($isLoggedIn): ?> <p>Welcome, <?php echo $userName; ?></p> <?php else: ?> <p>Please log in.</p> <?php endif; ?> </body>
</html>
Here, ?> is doing real work — it's toggling the parser between PHP mode and HTML output mode. You need it here. But notice — even this file doesn't need a closing ?> at the very end, because there's no PHP block left to close after the final HTML line.
Summary Table
|
File Type |
Closing ?> |
|
Pure PHP
class / service / controller |
Omit — always |
|
Pure PHP
config / constants file |
Omit — always |
|
PHP + HTML
mixed template |
Use it
between blocks, omit at end |
|
PHP file
included via require / include |
Omit —
headers risk |
The Bottom Line
PHP was designed to be embedded inside HTML, which is why ?> exists at all. But when a file is pure PHP, the closing tag is not just unnecessary — it's a silent bug waiting to happen. An accidental newline after it can corrupt headers, break JSON responses, and ruin session handling.
VS Code removing it automatically (or not flagging its absence) isn't a quirk — it's the editor agreeing with PSR-12. Omit the closing tag in every pure PHP file, every time, without exception.
No comments:
Post a Comment