Почему в PHP рекомендуется опускать закрывающий тег `?>` в конце файла?
Проблема: невидимые символы после закрывающего тега
Вы совершенно правы — дело именно в закрывающем теге. Когда PHP-интерпретатор встречает ?>, он считает, что PHP-код закончился, и всё, что идёт дальше (включая пробелы, переводы строк и даже обычный текст), отправляется в выходной буфер как есть. Если после ?> есть хотя бы один пробел или пустая строка, они становятся частью вывода. А функция header() требует, чтобы вывод ещё не начинался.
Как это работает на практике
Рассмотрим ваш пример:
<?php
header('Content-Type: text/html; charset=utf-8');
echo 'Привет!';
?>
<!DOCTYPE html>
После ?> идёт пустая строка (символ перевода строки). PHP отправляет этот перевод строки в буфер вывода. Когда вызывается header(), PHP уже начал вывод (пустая строка — это уже вывод), поэтому возникает ошибка "Cannot modify header information". Уберите ?> — и проблема исчезнет, потому что весь файл останется в режиме PHP, и header() будет вызван до любого вывода.
Безопасность: случайный вывод и утечка данных
Проблема не только в заголовках. Если после ?> окажется случайный пробел, табуляция или перевод строки, они попадут в HTTP-ответ. В большинстве случаев это безобидно, но бывают ситуации, когда это может привести к уязвимостям:
- Утечка чувствительных данных — если в файле с конфигурацией (например,
config.php) после закрывающего тега есть пробел, а файл подключается в начале страницы, этот пробел будет отправлен до начала сессии или куки. - Нарушение работы API — при возврате JSON или XML лишний пробел сделает ответ невалидным.
- Проблемы с кешированием — некоторые прокси-серверы могут неправильно интерпретировать вывод.
Когда можно оставлять закрывающий тег?
Есть всего два легитимных случая:
- Файлы, которые содержат только PHP-код — никогда не оставляйте
?>в конце. Это стандарт PSR-2 и PSR-12, которого придерживаются все современные фреймворки. - Файлы, которые смешивают PHP и HTML — например, шаблоны. Но даже в этом случае лучше использовать короткие блоки PHP и не ставить
?>в конце файла, если после него ничего нет.
Правильный подход
Всегда пишите PHP-файлы без закрывающего тега:
<?php
header('Content-Type: text/html; charset=utf-8');
echo 'Привет!';
// Весь остальной код
// Нет закрывающего тега
Если в файле есть HTML после PHP, используйте конструкцию с открывающим тегом в нужных местах:
<?php
$title = 'Моя страница';
?>
<!DOCTYPE html>
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?php echo 'Привет!'; ?>
</body>
</html>
<!-- Закрывающего тега ?> в конце нет -->
Итог
Опуская ?>, вы:
- Избавляетесь от риска случайного вывода пробелов и ошибок с заголовками
- Следуете современным стандартам кодирования
- Делаете код более безопасным и предсказуемым
Запомните простое правило: если файл заканчивается PHP-кодом — не ставьте ?>. Это спасёт вас от многих часов отладки.
Альтернативный взгляд: проблема с автозагрузкой и include
Помимо проблем с заголовками, есть ещё один важный аспект — работа с автозагрузкой классов и функций include/require.
Представьте, что у вас есть файл config.php:
<?php
return [
'db_host' => 'localhost',
'db_user' => 'root',
];
?>
(пробел после закрывающего тега)
Если вы подключаете его через $config = require 'config.php';, то из-за пробела после ?> возвращаемое значение будет не массивом, а строкой "1" (так как require возвращает 1 при успехе), а массив будет выведен как текст. В итоге $config станет 1, а не массивом с настройками.
Ещё хуже ситуация с классами. Если в файле класса после ?> есть пробел, а затем вызывается new SomeClass(), PHP может выдать фатальную ошибку "Class not found", потому что вывод пробела нарушил работу автозагрузчика.
Поэтому современные PHP-стандарты (PSR-4) требуют, чтобы файлы классов вообще не содержали закрывающего тега. Это гарантирует, что автозагрузка будет работать без сюрпризов.
Рекомендация: настройте свой редактор кода (PhpStorm, VS Code) так, чтобы он автоматически удалял пустые строки в конце файла и не добавлял закрывающий тег. Это избавит вас от 99% проблем.