Глава 7. Файлы и файловая система в PHP
Работа с файлами нужна почти в каждом PHP-проекте: загрузка изображений, кеширование, импорт CSV и XML, генерация логов, хранение временных данных. Ошибки в этой области часто приводят не только к сбоям, но и к проблемам безопасности.
Проверка существования файла
Перед чтением файла нужно убедиться, что он существует и доступен. Для этого используются file_exists(), is_file(), is_readable(). Если ожидается именно файл, лучше использовать is_file(), чтобы случайно не обработать каталог.
$path = __DIR__ . '/data.txt';
if (is_file($path) && is_readable($path)) {
echo 'Файл можно читать';
}
Чтение файла
Для небольших файлов удобно использовать file_get_contents(). Для больших файлов лучше читать данные постепенно, чтобы не расходовать много памяти.
$content = file_get_contents($path);
echo htmlspecialchars($content);
Запись в файл
file_put_contents() подходит для простой записи. Флаг FILE_APPEND добавляет данные в конец файла. Для логов полезно добавлять дату и перевод строки.
$line = date('Y-m-d H:i:s') . ' Ошибка импорта' . PHP_EOL;
file_put_contents(__DIR__ . '/log.txt', $line, FILE_APPEND);
Открытие через fopen
Если нужно читать файл построчно, используется fopen(), fgets() и fclose(). Это особенно полезно для больших CSV-файлов.
$handle = fopen(__DIR__ . '/data.csv', 'r');
while (($line = fgets($handle)) !== false) {
echo htmlspecialchars($line) . '<br>';
}
fclose($handle);
Загрузка файлов
Файл, загруженный через форму, сначала попадает во временную директорию. Его нужно проверить и переместить через move_uploaded_file(). Нельзя доверять имени файла, расширению и MIME-типу без дополнительной проверки.
if (isset($_FILES['image']) && is_uploaded_file($_FILES['image']['tmp_name'])) {
$safeName = uniqid('img_', true) . '.jpg';
move_uploaded_file($_FILES['image']['tmp_name'], __DIR__ . '/uploads/' . $safeName);
}
Безопасность
Самая опасная ошибка — разрешить пользователю загрузить PHP-файл в публичную директорию. Для изображений лучше проверять размер, расширение, MIME-тип и фактическое содержимое через getimagesize(). Имена файлов нужно генерировать самостоятельно, а не использовать исходное имя без очистки.
Пути и __DIR__
Относительные пути часто ломаются при подключении файлов из разных мест. Константа __DIR__ возвращает каталог текущего файла и делает путь предсказуемым.
require_once __DIR__ . '/config.php';
Файловый кеш
Простейший кеш можно хранить в файле. Главное — учитывать время жизни кеша и права на запись.
$cacheFile = __DIR__ . '/cache/menu.html';
if (is_file($cacheFile) && time() - filemtime($cacheFile) < 3600) {
echo file_get_contents($cacheFile);
}
Типичные ошибки
- Использование относительных путей без __DIR__.
- Запись в каталог без проверки прав доступа.
- Загрузка файлов без ограничения расширений и размера.
- Вывод содержимого файла без экранирования.
- Хранение временных файлов без очистки.
Итоги
Файловая система в PHP проста, но требует дисциплины. Проверяйте существование файлов, используйте безопасные пути, не доверяйте загруженным данным и не храните исполняемые файлы в публичных каталогах. Эти правила особенно важны для старых сайтов, где модули часто подключались вручную и работали с файлами напрямую.