Содержание статьи
BOM в UTF-8 — три начальных байта EF BB BF. Удаление этой сигнатуры не исправляет неверную кодировку всего файла. BOM допустим в некоторых текстовых форматах, но в PHP до открывающего тега может привести к преждевременному выводу.
Старый скрипт перезаписывал все файлы сайта из браузера. Ниже используется PHP CLI, список текстовых расширений и предварительный просмотр. Работайте с локальной копией или остановите процессы, меняющие файлы; скрипт не размещается в публичной директории.
Файл remove-bom.php
<?php
if (PHP_SAPI !== 'cli') {
http_response_code(403);
exit('Только CLI');
}
$root = realpath($argv[1] ?? '');
if ($root === false || !is_dir($root)) {
exit("Укажите существующий каталог\n");
}
$apply = ($argv[2] ?? '') === '--apply';
$prefix = rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$allowed = ['php', 'css', 'js', 'json', 'html', 'htm', 'txt', 'md', 'xml'];
$iterator = new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS);
$filter = new RecursiveCallbackFilterIterator($iterator,
static function (SplFileInfo $file): bool {
if ($file->isLink()) return false;
return !$file->isDir() || !in_array($file->getFilename(),
['.git', 'node_modules', 'vendor', 'backups'], true);
}
);
foreach (new RecursiveIteratorIterator($filter) as $file) {
if (!$file->isFile() || $file->isLink() || $file->getSize() > 10 * 1024 * 1024) continue;
if (!in_array(strtolower($file->getExtension()), $allowed, true)) continue;
$path = $file->getRealPath();
if ($path === false || !str_starts_with($path, $prefix)) continue;
$content = file_get_contents($path);
if ($content === false) throw new RuntimeException('Ошибка чтения файла');
if (!str_starts_with($content, "\xEF\xBB\xBF")) continue;
if (preg_match('//u', $content) !== 1) continue;
echo ($apply ? 'Исправление: ' : 'Найден BOM: ') . $path . PHP_EOL;
if (!$apply) continue;
$backup = $path . '.before-bom';
$handle = fopen($backup, 'xb'); // существующий резерв не перезаписывается
if ($handle === false) throw new RuntimeException('Не удалось создать резерв');
$written = fwrite($handle, $content);
fclose($handle);
if ($written !== strlen($content)) throw new RuntimeException('Неполный резерв');
$clean = substr($content, 3);
if (file_put_contents($path, $clean, LOCK_EX) !== strlen($clean)) {
throw new RuntimeException('Ошибка записи; восстановите файл из резерва');
}
}
Запуск и проверка
php remove-bom.php /path/to/site-copy
php remove-bom.php /path/to/site-copy --apply
Проверенные источники
www.php.net, www.php.net. Проверено 10 октября 2026 года.
Комментарии
Загрузка оценок и комментариев…
Комментариев пока нет. Вы можете оставить первый.