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