Перейти к содержанию

PHP: как дописать строку в конец файла и вести свой журнал

Есть вещи, которые нужны редко, но когда нужны — ищешь их где попало. Одна из них — дописать строку в конец файла на PHP. Чаще всего это самодельный журнал: что пришло в форму, какой ответ вернул чужой API, на каком шаге упал скрипт.

Делается это одной функцией, но у неё есть пара подвохов, на которых я сам спотыкался: файл появляется не там, где ждёшь, и вместо переноса строки в журнале стоят буквы \n. Ниже — команда, подвохи и готовый пример журнала. Всё проверено на PHP 8.5.

Коротко: file_put_contents( __DIR__ . '/app.log', $line . "\n", FILE_APPEND | LOCK_EX ); — путь через __DIR__, перенос строки в двойных кавычках, файл не в публичной папке сайта.

Команда

file_put_contents( $filename, $text, FILE_APPEND | LOCK_EX );
  • $filename — путь к файлу. Если файла нет, PHP создаст его сам. Папку — нет.
  • $text — что дописать.
  • FILE_APPEND — дописать в конец, а не перезаписать файл. Без этого флага каждый вызов стирает всё, что было.
  • LOCK_EX — на время записи заблокировать файл, чтобы два одновременных запроса не перемешали строки. Необязательный, но лишним не будет.

Функция возвращает число записанных байт или false, если записать не удалось.

Готовый пример: журнал в формате JSON

Каждая запись — одна строка JSON. Такой журнал удобно читать глазами и легко разбирать скриптом: построчно, без регулярных выражений.

function app_log( string $message, array $data = [] ): void {
	$line = json_encode(
		[ 'time' => date( 'c' ), 'msg' => $message ] + $data,
		JSON_UNESCAPED_UNICODE
	);
	file_put_contents( __DIR__ . '/../logs/app.log', $line . "\n", FILE_APPEND | LOCK_EX );
}

app_log( 'Заявка с формы', [ 'name' => $_POST['name'] ?? '' ] );

В файле получится:

{"time":"2026-10-25T13:32:16+03:00","msg":"Заявка с формы","name":"Алексей"}

JSON_UNESCAPED_UNICODE нужен, чтобы русские буквы остались буквами, а не превратились в За….

Грабли

Грабли №1: файл появился не там. Относительный путь вроде 'errors.log' считается не от папки скрипта, а от текущей рабочей папки PHP. При запуске из браузера она обычно совпадает с папкой скрипта, но в WordPress, в cron и в консоли — нет. Проверил: скрипт лежит в папке sub, запускаю его из папки уровнем выше — и файл с относительным путём появляется не в sub, а там, откуда запускал. Решение — всегда собирать путь от __DIR__, это папка текущего файла: __DIR__ . '/errors.log'.
Грабли №2: вместо переноса строки — «\n». В одинарных кавычках PHP не обрабатывает \n: 'текст\n' запишет обратную косую черту и букву n, и весь журнал окажется одной строкой. Перенос — только в двойных кавычках: "\n", или константа PHP_EOL.
Грабли №3: папки нет — записи нет. Файл PHP создаст, а папку — нет. Будет предупреждение Failed to open stream: No such file or directory и false вместо числа байт. Если предупреждения скрыты, журнал просто молча не пишется. Создайте папку заранее или в коде: is_dir( $dir ) || mkdir( $dir, 0755, true );
Грабли №4: журнал читают все. Файл в папке сайта открывается по прямой ссылке. В первой версии этого поста был пример журнала IP-адресов посетителей — такой файл в открытом доступе особенно неприятен: IP-адрес вместе со временем визита — персональные данные. Кладите журналы выше папки сайта (__DIR__ . '/../logs/'), а если хостинг не даёт, — закройте папку от веба: в Apache файл .htaccess со строкой Require all denied.

Если нужен просто журнал ошибок

У PHP для этого есть своя функция: error_log( $text . "\n", 3, $filename ). Тройка значит «дописать в указанный файл». Делает то же самое, что file_put_contents с FILE_APPEND, только без блокировки. А в WordPress проще включить встроенный журнал WP_DEBUG_LOG — как это сделать, я писал в посте про почту с сайта.

И не забывайте, что журнал растёт. Для самодельного хватит проверки размера: если filesize() больше нескольких мегабайт, переименовать файл в app.log.1 и начать новый.

Написать комментарий

Адрес почты не публикуется. Обязательные поля отмечены звёздочкой.

Возможно, вы не читали