Работа с файлами средствами PHP: от основ до продвинутых техник

Раздел: Работа с данными -> Работа с файлами

Основные операции с файлами в PHP

Наиболее эффективным и гибким способом работы с файлами в PHP считается использование набора функций fopen, fwrite, fread и fclose. Такой подход даёт полный контроль над режимом открытия, блокировками и позиционированием в файле.

$filename = 'data.txt';
$handle = fopen($filename, 'w'); // открытие для записи
if ($handle) {
    fwrite($handle, 'Строка для записи');
    fclose($handle);
    echo 'Файл успешно записан';
} else {
    echo 'Не удалось открыть файл';
}

После вызова fopen возвращается дескриптор файла. Второй параметр указывает режим: 'w' - запись с очисткой, 'a' - добавление, 'r' - чтение. Всегда следует проверять успешность открытия и закрывать дескриптор после завершения работы.

Типичные проблемы и их решение:

  • Отсутствие проверки результата fopen - может привести к ошибкам записи/чтения. Всегда проверяйте, не равен ли дескриптор false.
  • Забытый вызов fclose - утекают ресурсы. Используйте блок try...finally или автоматическое закрытие через деструктор объекта.
  • Недостаточные права доступа к файлу или каталогу - перед открытием проверяйте is_writable.
  • Проблемы с кодировкой строк - при записи текста убедитесь, что строки имеют нужную кодировку (например, UTF-8).

Как записать данные в файл одной функцией?

Для простой записи подходит file_put_contents. Функция автоматически открывает, записывает и закрывает файл. Вторым параметром можно передать строку или массив.

file_put_contents('test.txt', 'Данные для записи');
// или с флагом FILE_APPEND для добавления:
file_put_contents('test.txt', 'Дополнительная строка', FILE_APPEND);

Основная проблема - невозможность задать блокировку без дополнительных опций. Для конкурентной записи используйте flock вместе с fopen.

Как прочитать весь файл в строку?

Функция file_get_contents считывает файл целиком. Удобно для небольших файлов.

$content = file_get_contents('test.txt');
echo $content;

При больших размерах файла может переполнить память. Для больших файлов применяйте чтение по частям через fread.

Как читать файл построчно?

Для построчной обработки используется fgets в цикле с проверкой feof.

$handle = fopen('data.txt', 'r');
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        echo $line;
    }
    fclose($handle);
}

Не забывайте проверять, что fgets возвращает false только при ошибке. Пустая строка может быть корректным значением.

Как защитить файл от одновременной записи?

Для исключительной блокировки файла применяется flock.

$handle = fopen('shared.txt', 'a');
if ($handle && flock($handle, LOCK_EX)) {
    fwrite($handle, 'Новая запись');
    flock($handle, LOCK_UN); // снятие блокировки
}
fclose($handle);

Блокировка работает только внутри процессов, использующих flock. На некоторых файловых системах (NFS) может быть ненадёжной. Для критически важных данных применяйте базы данных.

Как работать с CSV-файлами?

Функция fgetcsv разбирает строку CSV. Для записи используется fputcsv.

$handle = fopen('data.csv', 'r');
while (($row = fgetcsv($handle, 1000, ',')) !== false) {
    print_r($row);
}
fclose($handle);

$handle = fopen('output.csv', 'w');
fputcsv($handle, ['Имя', 'Возраст', 'Город']);
fputcsv($handle, ['Анна', 25, 'Москва']);
fclose($handle);

Разделитель, обрамление кавычек и кодировка могут отличаться. Указывайте параметры явно, чтобы избежать неверного парсинга.

Как сохранить массив в JSON-файл?

Сначала данные преобразуются в JSON с помощью json_encode, затем записываются file_put_contents.

$data = ['name' => 'Иван', 'age' => 30];
$json = json_encode($data, JSON_UNESCAPED_UNICODE);
file_put_contents('user.json', $json);
// Обратное чтение:
$jsonFromFile = file_get_contents('user.json');
$decoded = json_decode($jsonFromFile, true);
print_r($decoded);

При кодировании с кириллицей обязательно используйте флаг JSON_UNESCAPED_UNICODE, иначе символы будут преобразованы в escape-последовательности. Всегда проверяйте успешность json_decode.

Как проверить существование файла и его доступность?

Для проверки применяются file_exists, is_file, is_readable, is_writable.

$file = 'config.php';
if (file_exists($file) && is_readable($file)) {
    echo 'Файл существует и доступен для чтения';
} else {
    echo 'Файл недоступен';
}

file_exists возвращает true также на каталоги. Используйте is_file для точной проверки на файл. Кеширование результатов stat может давать устаревшие данные - для свежей информации вызывайте clearstatcache.

Как удалить или переименовать файл?

Удаление производится функцией unlink, переименование - rename.

if (unlink('tmp.txt')) {
    echo 'Файл удалён';
} else {
    echo 'Ошибка удаления';
}

if (rename('old.txt', 'new.txt')) {
    echo 'Файл переименован';
}

Для удаления нужны права на запись в каталог. Переименование может не работать между разными файловыми системами - в таких случаях применяйте копирование и удаление.

Расширенные примеры работы с файлами

Чтение больших файлов по частям (буферизация)

Для обработки файлов, размер которых превышает доступную память, используется fread с указанием размера блока.

Пример
$handle = fopen('large_file.txt', 'r');
$bufferSize = 4096; // 4 КБ
while (!feof($handle)) {
    $chunk = fread($handle, $bufferSize);
    // обработка куска
    echo 'Прочитано ' . strlen($chunk) . ' байт
'; } fclose($handle);
Прочитано 4096 байт
Прочитано 4096 байт
...
Прочитано 1234 байт

Функция feof проверяет конец файла. Важно учитывать, что последний блок может быть меньше указанного размера.

Запись с эксклюзивной блокировкой и освобождением ресурсов

В многопоточных скриптах (например, при использовании очередей) блокировка файла предотвращает повреждение данных.

Пример
$file = 'counter.log';
$handle = fopen($file, 'a');
if ($handle) {
    try {
        if (!flock($handle, LOCK_EX)) {
            throw new Exception('Не удалось получить блокировку');
        }
        fwrite($handle, 'Запись с блокировкой: ' . date('Y-m-d H:i:s') . PHP_EOL);
        flock($handle, LOCK_UN);
    } finally {
        fclose($handle);
    }
} else {
    echo 'Ошибка открытия файла';
}
// Файл counter.log пополняется строками без пересечения

Конструкция try...finally гарантирует закрытие файла даже при возникновении исключения.

Создание временного файла

Для хранения промежуточных данных удобно использовать tmpfile - создаёт временный файл, который автоматически удаляется при закрытии.

Пример
$tempHandle = tmpfile();
fwrite($tempHandle, 'Временные данные');
// Делаем что-то с файлом (например, передаём его в другой обработчик)
rewind($tempHandle);
$content = stream_get_contents($tempHandle);
echo $content;
fclose($tempHandle); // файл удаляется
Временные данные

Временный файл создаётся в системной директории и не занимает место после завершения работы скрипта.

Рекурсивное создание директорий

При сохранении файлов во вложенные каталоги может потребоваться создать все уровни папок. Параметр recursive у mkdir позволяет это сделать.

Пример
$path = 'uploads/2024/12/25';
if (!is_dir($path)) {
    mkdir($path, 0755, true);
    echo 'Директория создана';
}
$file = $path . '/log.txt';
file_put_contents($file, 'Содержимое');
Директория создана

Если каталог уже существует, is_dir вернёт true, и создание не выполнится. Права доступа 0755 - стандартные для веб-сервера.

Использование потоковых обёрток (php://input, php://output)

PHP поддерживает потоки для работы с вводом-выводом напрямую. Например, чтение тела POST-запроса через php://input.

Пример
$input = fopen('php://input', 'r');
$rawData = stream_get_contents($input);
fclose($input);
echo 'Получено ' . strlen($rawData) . ' байт данных';
Получено 1024 байт данных

Аналогично для прямого вывода в ответ сервера можно использовать php://output:

Пример
$out = fopen('php://output', 'w');
fwrite($out, 'Генерируемый контент');
fclose($out);

Такой подход полезен при создании динамических изображений, PDF или прямого вывода больших объёмов данных.

Копирование и перемещение файлов с проверкой

Функция copy и rename с предварительной проверкой успешности.

Пример
$source = 'source.txt';
$dest = 'backup/source_backup.txt';
if (!file_exists(dirname($dest))) {
    mkdir(dirname($dest), 0755, true);
}
if (copy($source, $dest)) {
    echo 'Файл скопирован';
} else {
    echo 'Ошибка копирования';
}
// Перемещение (переименование)
if (rename($dest, 'archive/backup.txt')) {
    echo 'Файл перемещён';
}
Файл скопирован
Файл перемещён

При перемещении между разными разделами диска rename может не сработать - в таких случаях требуется copy + unlink.

Чтение и запись бинарных файлов

Режимы 'wb' и 'rb' гарантируют корректную работу с бинарными данными без преобразования символов новой строки.

Пример
$src = fopen('image.jpg', 'rb');
$dst = fopen('copy.jpg', 'wb');
while (!feof($src)) {
    $chunk = fread($src, 8192);
    if ($chunk !== false) {
        fwrite($dst, $chunk);
    }
}
fclose($src);
fclose($dst);
echo 'Бинарное копирование завершено';
Бинарное копирование завершено

Использование режима 'b' особенно важно на Windows, где в текстовом режиме символы перевода строки могут искажаться.

Работа с файлами в PHP - comments

En
Php файл (php)