#Когда возникает ошибка
Наличие каталога не гарантирует, что PHP может записать файл. fwrite() возвращает количество записанных байтов или false, а иногда число меньше размера данных. Причина может быть в режиме открытия потока, правах владельца PHP-FPM, заполненном разделе или квоте, недоступном сетевом хранилище, read-only-разделе и системных ограничениях.
#Причины и признаки
| Возможная причина | Как узнать | Что делать |
|---|---|---|
| Режим fopen или файл недоступенФайл открыт только для чтения, существует с чужими правами либо open_basedir ограничивает путь. | Проверка fopen и предупреждения журнала показывают Permission denied, режим r или запрет доступа. | Проверьте реальный путь, режим открытия и учётную запись PHP. |
| Нет места либо превышена квотаКаталог существует и может быть доступен для чтения, но запись не проходит. | В журнале No space left on device / Disk quota exceeded; df -h показывает загрузку конкретного раздела. | Освободите место штатно после сохранения важных данных или запросите квоту у администратора. |
| Короткая запись в потокНа сетевых или нестандартных потоках fwrite может записать меньше переданного объёма. | Возвращается целое меньше strlen($data) или 0, даже без false. | Проверяйте возвращённое число и останавливайтесь при отсутствии прогресса. |
#Пошаговая диагностика и устранение
- Не запускайте повторную запись в важный файлЕсли ошибка возникла при записи счёта, выгрузки или архива, сделайте копию существующих данных и остановите автоматические повторные попытки. Повторное открытие в режиме
wобнуляет существующий файл. Для диагностики выберите отдельную тестовую директорию и короткую строку без персональных данных. - Проверьте, что сбой действительно на fwriteОткройте закрытый PHP-журнал и найдите предупреждение рядом с
fopen()/fwrite(). В редакторе посмотрите переменные$handleи$path: не перепутан ли режимrсab, открыт ли поток успешно, не было ли ужеfclose(). Приfopen() === falseне вызывайте fwrite вообще — сначала устраните ошибку открытия. - Получите абсолютный путь и пользователя веб-процессаВ тестовой копии логируйте
__DIR__, разрешённый целевой путь и результатis_writable()для существующего файла и каталога, но не выводите их посетителю. Помните:phpиз терминала выполняется под вашим пользователем, а PHP-FPM сайта — часто под другим. Если в CLI запись проходит, а на сайте нет, попросите администратора проверить пользователя пула и доступные каталоги. - Проверьте права и ограничения без опасной смены chmodВ терминале Linux используйте
ls -ld /реальный/каталогиls -l /реальный/каталог/файл, заменив путь, чтобы увидеть владельца и режим. Проверьте права на каждый родительский каталог; для записи нужен доступ к файлу и соответствующие разрешения для создания/замены. Не применяйтеchmod 777и не меняйте владельца всего сайта рекурсивно. На Windows проверьте права учётной записи веб-сервера и запрет записи в защищённые каталоги. - Проверьте место и квоту именно целевого разделаНа Linux выполните
df -h /реальный/каталоги при необходимостиdf -i /реальный/каталог. Даже если корневой диск свободен, отдельная точка монтирования может быть заполнена; квота пользователя также может ограничивать запись. Не удаляйте произвольные файлы и журналы: выясните, кто владелец данных, и согласуйте освобождение места. - Изолируйте ошибку тестовой записьюСоздайте отдельный PHP-тест с путём к заранее созданной временной папке, доступной приложению. Откройте новый тестовый файл через
fopen($path, "xb"), проверьте результат, затем сравните число записанных байтов сstrlen($payload). Если вернулось false или 0 для непустой строки, прекратите повторять вызовы; зафиксируйте предупреждение и попросите администратора проверить файловую систему. - Проверьте результат и аккуратно отмените диагностикуУбедитесь, что тестовый файл содержит ровно ожидаемые байты и
fclose()завершился. Удалите только созданный вами тестовый файл через интерфейс файлового менеджера после проверки его имени. Если проблема решена сменой владельца или путей, восстановите исходные разрешения, если они были временными; затем повторите штатную операцию на копии данных до выхода в рабочий режим.
#Проверяемый пример и команды
fflush проверяется, но надёжная долговременная запись на накопитель зависит и от системы.<?php
$path = __DIR__ . '/write-check.tmp'; // используйте только отдельную тестовую папку
$payload = 'WHELP write test' . PHP_EOL;
$fh = fopen($path, 'xb'); // создать новый файл, не перезаписывать существующий
if ($fh === false) {
throw new RuntimeException('Не удалось создать тестовый файл');
}
$written = fwrite($fh, $payload);
$flushed = fflush($fh);
fclose($fh);
if ($written === false || $written !== strlen($payload) || !$flushed) {
throw new RuntimeException('Запись неполная или не завершена');
}
echo 'Записано байтов: ' . $written . PHP_EOL;df -h /real/pathdf -i /real/pathls -ld /real/pathls -l /real/path/write-check.tmp#Проверка исправления и действия при неудаче
Успех — fwrite возвращает количество байтов, равное длине непустой строки, тестовый файл содержит правильный текст, а приложение ведёт журнал об ошибках записи. Если сохраняется только частичная запись, проверяйте тип потока, сетевой ресурс, квоты и ограничения файловой системы.