Работа с файлами: чтение, запись и управление файловыми ресурсами
Работа с файлами является неотъемлемой частью веб-разработки. PHP предоставляет широкий набор функций для чтения, записи, копирования и удаления файлов. В этой статье рассматриваются основные файловые функции, их применение, типичные ошибки и лучшие практики.
Основные файловые функции PHP
Как быстро прочитать или записать весь файл?
Функции file_get_contents и file_put_contents позволяют выполнить чтение и запись за одно действие без необходимости ручного управления дескрипторами. Это наиболее удобный способ для работы с файлами небольшого размера (до 100 МБ).
// Чтение содержимого файла
$content = file_get_contents('example.txt');
if ($content === false) {
// обработка ошибки
} else {
echo $content;
}File php function (файловые функции в php)
// Запись данных в файл
$data = "Привет, мир!\n";
$result = file_put_contents('output.txt', $data);
if ($result === false) {
// ошибка записи
} else {
echo "Записано байт: $result";
}Типичные проблемы:
- Отсутствие обработки возвращаемого значения false при ошибке.
- При чтении больших файлов происходит загрузка всего содержимого в память, что может вызвать исчерпание памяти.
- Неверный путь к файлу или отсутствие прав доступа.
Рекомендации: всегда проверять результат, для больших файлов использовать построчное чтение, указывать абсолютные пути или проверять существование файла.
Как построчно прочитать большой файл?
Для обработки файлов, размер которых превышает доступную память, применяется открытие файла с помощью fopen и последовательное чтение строк через fgets.
$handle = fopen('largefile.log', 'r');
if ($handle) {
while (($line = fgets($handle)) !== false) {
// обработка строки
echo $line;
}
fclose($handle);
} else {
// ошибка открытия
}Частые ошибки:
- Не закрытие дескриптора (утечка ресурсов).
- Неверный режим открытия (например, 'w' вместо 'r').
- Пропуск проверки $handle === false.
Решение: всегда вызывать fclose в блоке finally или после завершения цикла, использовать правильный режим.
Как записать данные в файл с выбором режима?
При необходимости дописать данные в конец файла или перезаписать его, используется fopen с нужным режимом ('w' для записи с очисткой, 'a' для дописывания) и fwrite.
$handle = fopen('log.txt', 'a'); // дописывание
if ($handle) {
fwrite($handle, date('Y-m-d H:i:s') . " - запись\n");
fclose($handle);
}При использовании режима 'w' существующий файл будет очищен. Если требуется сохранить прежнее содержимое, следует использовать 'a'.
Также необходимо проверять, что fwrite вернула число байт, равное длине строки, иначе возможна частичная запись.
Как прочитать файл в массив строк?
Функция file возвращает массив, каждый элемент которого соответствует одной строке файла. Полезно для быстрого анализа строк.
$lines = file('data.csv'); // без флагов включает символ новой строки
foreach ($lines as $line) {
echo $line;
}Можно использовать флаг FILE_IGNORE_NEW_LINES и FILE_SKIP_EMPTY_LINES.
Функция также загружает весь файл в память. Для больших массивов строк (миллионы записей) это может быть неэффективно.
Как скопировать или переименовать файл?
Для копирования используется copy, для переименования или перемещения - rename.
if (copy('source.txt', 'backup/source.txt')) {
echo "Файл скопирован";
} else {
echo "Ошибка копирования";
}
if (rename('tmp.txt', 'final.txt')) {
echo "Файл переименован";
}Функция rename может не сработать, если целевой файл уже существует (в некоторых системах требуется предварительно удалить). При копировании необходимо убедиться, что каталог назначения существует.
Как безопасно удалить файл?
Удаление файла выполняется функцией unlink. Перед удалением рекомендуется проверить существование файла с помощью file_exists.
if (file_exists('obsolete.txt')) {
if (unlink('obsolete.txt')) {
echo "Файл удален";
} else {
echo "Ошибка удаления";
}
}Функция unlink не удаляет директории (используется rmdir). При удалении возможна ошибка, если файл заблокирован или недостаточно прав.
Как проверить существование файла и его размер?
file_exists возвращает true, если файл или директория существует. filesize возвращает размер в байтах.
if (file_exists('report.pdf')) {
$size = filesize('report.pdf');
echo "Размер файла: $size байт";
}Результаты filesize кэшируются. Для получения актуальной информации после изменений следует вызвать clearstatcache. Также для больших файлов (более 2 ГБ) на 32-битных системах возможны проблемы с переполнением.
Расширенные примеры использования файловых функций
В этом разделе представлены более сложные сценарии работы с файлами, включая обработку ошибок, блокировки и взаимодействие с внешними ресурсами.
Чтение удаленного файла через HTTP с контекстом
Для загрузки содержимого с удаленного сервера можно использовать file_get_contents с пользовательским контекстом, задающим таймауты и заголовки.
$options = [
'http' => [
'method' => 'GET',
'header' => "User-Agent: MyAgent/1.0\r\n",
'timeout' => 10
]
];
$context = stream_context_create($options);
$data = @file_get_contents('https://api.example.com/data.json', false, $context);
if ($data === false) {
$error = error_get_last();
echo "Ошибка: " . $error['message'];
} else {
echo "Получено " . strlen($data) . " байт";
}Пример вывода: Получено 2048 байт
Пояснение: контекст создается через stream_context_create. Подавление ошибок с @ необходимо для ручной обработки.
Блокировка файла при параллельной записи
Чтобы избежать повреждения данных при одновременной записи несколькими процессами, применяется flock.
$handle = fopen('shared.log', 'a');
if (flock($handle, LOCK_EX)) { // эксклюзивная блокировка
fwrite($handle, "Запись процесса " . getmypid() . "\n");
flock($handle, LOCK_UN); // снятие блокировки
}
fclose($handle);Результат: строка добавляется в файл без конфликтов.
Пояснение: блокировка удерживается до её явного снятия, либо до закрытия дескриптора.
Рекурсивное копирование директории
Стандартная функция copy работает только с файлами. Для копирования папки со всем содержимым можно использовать рекурсивный обход.
function copyDir($src, $dst) {
$dir = opendir($src);
@mkdir($dst, 0777, true);
while (($file = readdir($dir)) !== false) {
if ($file != '.' && $file != '..') {
$srcFile = $src . DIRECTORY_SEPARATOR . $file;
$dstFile = $dst . DIRECTORY_SEPARATOR . $file;
if (is_dir($srcFile)) {
copyDir($srcFile, $dstFile);
} else {
copy($srcFile, $dstFile);
}
}
}
closedir($dir);
}
copyDir('/path/to/source', '/path/to/destination');Файлы и поддиректории успешно скопированы.
Чтение CSV файла с помощью fgetcsv
Функция fgetcsv разбирает строку CSV и возвращает массив полей. Позволяет обрабатывать файлы с разделителями.
$handle = fopen('users.csv', 'r');
$header = fgetcsv($handle); // первая строка - заголовки
while ($row = fgetcsv($handle)) {
echo "Имя: " . $row[0] . ", Email: " . $row[1] . "\n";
}
fclose($handle);Имя: Иван, Email: ivan@example.com Имя: Мария, Email: maria@example.com
Создание временного файла с tmpfile
Функция tmpfile создаёт временный файл, который автоматически удаляется при закрытии дескриптора. Удобно для промежуточных данных.
$temp = tmpfile();
fwrite($temp, "Временные данные");
fseek($temp, 0);
echo fread($temp, 1024); // "Временные данные"
fclose($temp); // файл удалёнВывод: Временные данные
Обработка ошибок файловых операций через исключения
Для более структурированной обработки ошибок можно обернуть вызовы в try-catch и генерировать исключения.
function safeReadFile($filename) {
if (!file_exists($filename)) {
throw new Exception("Файл $filename не найден");
}
$content = file_get_contents($filename);
if ($content === false) {
throw new Exception("Ошибка чтения файла $filename");
}
return $content;
}
try {
$data = safeReadFile('config.ini');
echo $data;
} catch (Exception $e) {
echo "Ошибка: " . $e->getMessage();
}Пример при отсутствии файла: Ошибка: Файл config.ini не найден
Получение MIME-типа файла
Модуль FileInfo (finfo) позволяет определить MIME-тип файла на основе его содержимого.
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file('image.png');
echo "MIME-тип: $mime";MIME-тип: image/png
Изменение прав доступа к файлу
Функция chmod устанавливает права на файл. Требует соответствующих привилегий.
if (chmod('private.txt', 0600)) {
echo "Права изменены на 600";
} else {
echo "Не удалось изменить права";
}Права изменены на 600