Практика изменения файлов в PHP

Раздел: Программирование на 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);
При занятости файла запись не произойдет, но скрипт продолжит работу.

Изменение файла в PHP - comments

En
Php изменить файл (php)