Практика изменения файлов в PHP
Основные способы изменения файлов
Наиболее эффективный подход: file_put_contents
Функция file_put_contents выполняет запись данных в файл за одно действие. Она открывает файл, записывает строку или массив строк, и закрывает его. В случае успеха возвращает количество записанных байт, иначе false.
$result = file_put_contents('example.txt', 'Новое содержимое');
if ($result === false) {
// ошибка записи
} else {
echo "Записано $result байт";
}
Цель:
быстрая перезапись всего файла новыми данными. Используется для создания конфигурационных файлов, кеша, логов (если не требуется дополнение).Типичные ошибки:
- Отсутствие прав на запись – возвращается false, но исключение не генерируется. Проверять через
is_writable(). - Проблемы с кодировкой – если данные не строка, функция приводит к строке, что может исказить бинарные данные. Использовать флаги
FILE_BINARYдля бинарных файлов (PHP 7+).
Как перезаписать файл с помощью fwrite?
Классический способ с предварительным открытием файла в режиме 'w' (запись с обнулением).
$fp = fopen('example.txt', 'w');
if ($fp) {
fwrite($fp, 'Новая строка');
fclose($fp);
}
Позволяет выполнять несколько операций fwrite подряд, что удобно при построении больших данных. Цель: пошаговая запись, когда данные формируются динамически.
Ошибка: забыть закрыть файл – утечка ресурсов. Всегда используйте fclose() или оборачивайте в try-finally.
Как дописать данные в конец файла без потери предыдущего содержимого?
Режим 'a' (append) у fopen или флаг FILE_APPEND у file_put_contents.
file_put_contents('log.txt', 'Новое сообщение' . PHP_EOL, FILE_APPEND);
// или
$fp = fopen('log.txt', 'a');
fwrite($fp, 'Новое сообщение' . PHP_EOL);
fclose($fp);
Цель: ведение логов, журналов событий, добавление записей без перезаписи.
Проблема: конкурентный доступ – при параллельных запросах данные могут перемешаться. Решение: использовать блокировку flock($fp, LOCK_EX) перед fwrite.
Как изменить только часть файла, не затрагивая остальное?
Режим 'r+' (чтение+запись с начала) или 'c+' (создание при необходимости, не обрезает). Нужно установить указатель в нужную позицию.
$fp = fopen('example.txt', 'r+');
fseek($fp, 5); // перейти на 6-й байт
fwrite($fp, 'вставка'); // перезапишет начиная с этой позиции
Цель: изменение конфигурационных файлов с фиксированным форматом, патч бинарных данных.
Ошибка: неверное смещение – если длина новой строки не совпадает с исходной, данные могут сместиться. Для замены строки на строку другой длины лучше перезаписывать весь файл.
Как безопасно изменить файл с блокировкой от одновременной записи?
Использовать flock вместе с fopen. Режим 'w' или 'a' не блокируют автоматически.
$fp = fopen('counter.txt', 'c+'); // 'c' не обрезает файл
if (flock($fp, LOCK_EX)) {
$content = fread($fp, filesize('counter.txt'));
$content++;
ftruncate($fp, 0);
fwrite($fp, $content);
fflush($fp);
flock($fp, LOCK_UN);
}
fclose($fp);
Цель: работа с файлами-счетчиками, очередями, любыми данными, где важна атомарность.
Типичная ошибка: использование flock на файлах, открытых в режиме 'w', – в Windows блокировка может не работать. Проверять на целевой платформе.
Расширенные примеры изменения файлов
1. Запись большого файла частями (стриминг)
Используем fwrite в цикле для обработки больших данных без загрузки всего в память.
$source = fopen('large_source.txt', 'r');
$dest = fopen('large_dest.txt', 'w');
while (!feof($source)) {
$chunk = fread($source, 8192);
fwrite($dest, $chunk);
}
fclose($source);
fclose($dest);
Файл large_dest.txt будет точной копией исходного.
2. Запись массива данных в CSV-файл
Используем fputcsv, которая автоматически экранирует поля.
$data = [
['Имя', 'Возраст', 'Город'],
['Иван', 30, 'Москва'],
['Петр', 25, 'Санкт-Петербург']
];
$fp = fopen('users.csv', 'w');
foreach ($data as $row) {
fputcsv($fp, $row);
}
fclose($fp);
Содержимое users.csv: Имя,Возраст,Город Иван,30,Москва Петр,25,Санкт-Петербург
3. Атомарная замена файла через временный файл
Для исключения чтения недописанного файла.
$tmp = tempnam(sys_get_temp_dir(), 'prefix');
file_put_contents($tmp, 'Новые данные');
rename($tmp, 'target.txt'); // атомарно на одной файловой системе
Файл target.txt мгновенно получит новое содержимое.
4. Использование SplFileObject для объектно-ориентированной записи
$file = new SplFileObject('example.txt', 'w');
$file->fwrite('Строчка 1' . PHP_EOL);
$file->fwrite('Строчка 2' . PHP_EOL);
// метод fflush отсутствует, закрытие через unset
unset($file);
Файл example.txt содержит две строки.
5. Запись с применением callback через Generator
Полезно для ленивой генерации данных.
function generateLines() {
yield 'Первая линия';
yield 'Вторая линия';
}
$fp = fopen('output.txt', 'w');
foreach (generateLines() as $line) {
fwrite($fp, $line . PHP_EOL);
}
fclose($fp);
Файл output.txt: Первая линия Вторая линия
6. Исправление ошибок кодировки: запись в UTF-8 с BOM
$content = "\xEF\xBB\xBF" . 'Текст в UTF-8';
file_put_contents('utf8_file.txt', $content);
Программа Excel корректно определит кодировку.
7. Работа с бинарными данными: запись изображения из базы
$imageData = file_get_contents('https://example.com/image.jpg');
file_put_contents('local_image.jpg', $imageData);
Локальная копия изображения.
8. Множественная запись с блокировкой и таймаутом
$fp = fopen('shared.log', 'a');
if (flock($fp, LOCK_EX | LOCK_NB)) {
fwrite($fp, date('Y-m-d H:i:s') . " - действие\n");
fflush($fp);
flock($fp, LOCK_UN);
} else {
echo "Не удалось получить блокировку. Пропускаем запись.";
}
fclose($fp);
При занятости файла запись не произойдет, но скрипт продолжит работу.