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

Раздел: Веб-разработка на PHP -> Работа с файлами в PHP

Основы обработки файлов в PHP

Наиболее контролируемый способ - использование функций fopen(), fwrite() (или fread()) и fclose(). Этот подход позволяет точно управлять режимом открытия, буферизацией и обработкой ошибок.

<?php
$filename = 'data.txt';
$handle = fopen($filename, 'w');  // 'w' - запись с перезаписью
if ($handle === false) {
    exit('Не удалось открыть файл');
}
$written = fwrite($handle, 'Текст для записи');
if ($written === false) {
    // обработка ошибки записи
}
fclose($handle);
?>

Пояснение: fopen возвращает дескриптор или false. После операции файл обязательно закрывается. Режим 'w' создаёт файл при отсутствии или перезаписывает существующий.

Типичные ошибки:

  • Отсутствие прав на запись в директории - проверять через is_writable().
  • При одновременной записи нескольких процессов данные могут перемешиваться - рекомендуется блокировка flock().

Как записать строку в файл без ручного управления дескриптором?

Функция file_put_contents() объединяет открытие, запись и закрытие в одну строку.

$bytes = file_put_contents('example.txt', 'Простое содержимое');

Пояснение: Возвращает количество записанных байт или false. По умолчанию файл перезаписывается.

Проблема: нет возможности проверить существование файла перед записью. Для добавления в конец используйте флаг FILE_APPEND.

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

file_get_contents() - самый простой способ получить содержимое файла.

$content = file_get_contents('readme.txt');
if ($content === false) {
    // ошибка чтения
}

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

Ограничение: при больших объёмах данных (например, >100 МБ) может не хватить памяти. Для таких случаев лучше использовать построчное чтение.

Как читать файл построчно для обработки больших объёмов?

Комбинация fopen() + fgets() в цикле.

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

Пояснение: Каждая итерация считывает одну строку (до символа новой строки). Память расходуется минимально.

Возможная проблема: если строки очень длинные, fgets() может прочитать только часть. В таких случаях нужно настроить размер буфера вторым параметром или использовать stream_get_line().

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

Режим 'a' (append) у fopen() или флаг FILE_APPEND для file_put_contents().

file_put_contents('log.txt', 'Новая запись\n', FILE_APPEND);
// или
$handle = fopen('log.txt', 'a');
fwrite($handle, 'Новая запись\n');
fclose($handle);

Пояснение: Указатель устанавливается в конец файла, содержимое сохраняется.

Ошибка: если файл не существует, режим 'a' создаст его, но это может быть неожиданным. Сначала стоит проверить существование через file_exists().

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

Функции file_exists(), is_readable(), is_writable().

if (file_exists('data.txt') && is_writable('data.txt')) {
    // можно писать
}

Пояснение: file_exists() возвращает true для файлов и директорий. Дополнительные проверки уточняют права.

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

Как удалить файл с контролем ошибок?

unlink() удаляет файл. Перед удалением стоит проверить его существование.

if (file_exists('old.txt')) {
    unlink('old.txt');
} else {
    // файл не найден
}

Пояснение: unlink() возвращает true при успехе или false при ошибке.

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

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

Специализированные функции fgetcsv() и fputcsv() упрощают парсинг.

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

Пояснение: Параметры: максимальная длина строки (0 - без ограничения), разделитель, ограничитель текста.

Проблема: CSV может содержать разные разделители (точка с запятой). Нужно указывать их явно. Также некорректно экранированные кавычки ломают парсинг.

Как загрузить файл на сервер из HTML формы?

Суперглобальный массив $_FILES и функция move_uploaded_file().

$uploadDir = 'uploads/';
$tmpName = $_FILES['userfile']['tmp_name'];
$destName = $uploadDir . basename($_FILES['userfile']['name']);
if (move_uploaded_file($tmpName, $destName)) {
    echo 'Файл загружен';
}

Пояснение: Важно проверять MIME-тип и размер файла перед перемещением. move_uploaded_file() гарантирует, что файл был загружен через HTTP POST.

Ошибки безопасности: не следует доверять имени файла из формы - возможны path traversal атаки. Используйте basename() и проверяйте расширение.

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

Комбинация json_encode(), json_decode() и файловых функций.

$data = ['name' => 'John', 'age' => 30];
file_put_contents('user.json', json_encode($data, JSON_PRETTY_PRINT));
// чтение
$json = file_get_contents('user.json');
$array = json_decode($json, true);

Пояснение: json_decode со вторым параметром true возвращает ассоциативный массив.

Проблема: некорректный JSON вызывает json_last_error(). Всегда проверять результат декодирования.

Как записать бинарные данные (например, изображение)?

Для бинарных файлов используется режим 'wb' (binary write) или 'rb'.

$source = fopen('photo.jpg', 'rb');
$dest = fopen('copy.jpg', 'wb');
stream_copy_to_stream($source, $dest);
fclose($source);
fclose($dest);

Пояснение: Режим 'b' отключает преобразование концов строк, что критично для бинарных данных.

Ошибка: на Windows без 'b' бинарные файлы могут быть повреждены (добавление символов \r\n).

Как обрабатывать ошибки при работе с файлами?

Всегда проверять возвращаемые значения на false и использовать пользовательские обработчики ошибок.

$handle = @fopen('protected.txt', 'r');  // @ подавляет предупреждение
if ($handle === false) {
    $error = error_get_last();
    // логирование или исключение
}

Пояснение: Подавление ошибок не рекомендуется; лучше настроить исключения через set_error_handler() или проверять права заранее.

Типичная ошибка: игнорирование возвращаемого значения false приводит к неожиданным сбоям. Каждая операция с файлом требует проверки.

Чтение большого файла с помощью генератора

Позволяет обрабатывать файлы, не загружая их целиком в память.

Пример
function readLinesGenerator($filename) {
    $handle = fopen($filename, 'r');
    while (($line = fgets($handle)) !== false) {
        yield rtrim($line, "\n\r");
    }
    fclose($handle);
}

$lines = readLinesGenerator('large.log');
foreach ($lines as $line) {
    echo $line . PHP_EOL;
}
Вывод первых строк файла large.log (если файл существует).
Пример вывода:
[2025-03-15 10:00:01] INFO Старт приложения
[2025-03-15 10:00:02] DEBUG Параметры загружены
...

Объектно-ориентированный подход с SplFileObject

Итератор для удобной работы с CSV или текстовыми файлами.

Пример
$file = new SplFileObject('data.csv');
$file->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY);
foreach ($file as $row) {
    if ($row !== []) {
        echo implode(' | ', $row) . "\n";
    }
}
Результат:
Имя | Возраст | Город
Иван | 28 | Москва
Мария | 32 | Санкт-Петербург

Потоки и контексты: чтение через HTTP с авторизацией

Использование stream_context_create() для удалённой загрузки файла.

Пример
$context = stream_context_create([
    'http' => [
        'header' => "Authorization: Basic " . base64_encode('user:pass'),
        'timeout' => 30
    ]
]);
$content = file_get_contents('https://api.example.com/data.json', false, $context);
if ($content !== false) {
    echo 'Загружено ' . strlen($content) . ' байт';
}
Вывод: Загружено 2048 байт (при успешном запросе).

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

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

Пример
function findFiles($dir, $extension) {
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
    );
    foreach ($iterator as $file) {
        if (strtolower($file->getExtension()) === $extension) {
            yield $file->getPathname();
        }
    }
}

foreach (findFiles('/var/www/logs', 'log') as $path) {
    echo $path . "\n";
}
/var/www/logs/app.log
/var/www/logs/error.log
/var/www/logs/subdir/debug.log

Блокировка файла для предотвращения одновременной записи

Использование flock() для исключительной блокировки.

Пример
$handle = fopen('counter.txt', 'c+');
if (flock($handle, LOCK_EX)) {
    $count = (int) fread($handle, 1024);
    $count++;
    rewind($handle);
    fwrite($handle, (string)$count);
    flock($handle, LOCK_UN);
} else {
    echo 'Не удалось заблокировать файл';
}
fclose($handle);
Файл counter.txt будет содержать увеличенное значение (например, 5).

Загрузка файла на сервер с проверками

Полный пример с валидацией MIME-типа, размера и перемещением.

Пример
$allowedTypes = ['image/jpeg', 'image/png'];
$maxSize = 2 * 1024 * 1024; // 2MB

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload'])) {
    $file = $_FILES['upload'];
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die('Ошибка загрузки: ' . $file['error']);
    }
    if (!in_array($file['type'], $allowedTypes)) {
        die('Недопустимый тип файла');
    }
    if ($file['size'] > $maxSize) {
        die('Файл слишком большой');
    }
    $dest = 'uploads/' . uniqid() . '_' . basename($file['name']);
    if (move_uploaded_file($file['tmp_name'], $dest)) {
        echo 'Файл сохранён: ' . $dest;
    } else {
        echo 'Ошибка перемещения';
    }
}
При успешной загрузке: Файл сохранён: uploads/63f1a2b3c4d5_photo.jpg

Чтение и запись бинарного файла (изображения) с проверкой

Копирование изображения с использованием fread и fwrite.

Пример
$src = fopen('source.png', 'rb');
$dst = fopen('dest.png', 'wb');
if ($src && $dst) {
    while (!feof($src)) {
        $chunk = fread($src, 8192);
        if ($chunk !== false) {
            fwrite($dst, $chunk);
        }
    }
    fclose($src);
    fclose($dst);
    echo 'Копирование успешно';
} else {
    echo 'Ошибка открытия файлов';
}
Вывод: Копирование успешно (размер dest.png совпадает с source.png).

Временные файлы через tmpfile()

Создание временного файла, который удаляется автоматически после закрытия.

Пример
$handle = tmpfile();
fwrite($handle, 'Временные данные');
rewind($handle);
echo fread($handle, 1024);
fclose($handle);  // файл удалён
Вывод: Временные данные

Атомарная запись с LOCK_EX через file_put_contents

Флаг LOCK_EX обеспечивает эксклюзивную блокировку.

Пример
$data = 'Важные данные' . PHP_EOL;
$result = file_put_contents('lockfile.txt', $data, LOCK_EX);
if ($result !== false) {
    echo 'Записано ' . $result . ' байт';
}
Вывод: Записано 14 байт
- прочитать php (чтение данных в php)
- обработка файлов php (обработка файлов в php)
- Files show php file (показ файлов в php)
- Php загрузка файла (загрузка файла в php)

Обработка файлов в PHP - comments

En
обработка файлов php (php)