Верификация файлов в PHP: от file_exists до finfo

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

Основные методы проверки файлов

Наиболее эффективный способ проверки файла перед его использованием - комбинировать функции file_exists() и is_file(), а также учитывать права доступа с помощью is_readable() и is_writable(). Такой подход позволяет избежать ошибок при открытии, чтении или записи.

$filename = '/path/to/file.txt';
if (file_exists($filename) && is_file($filename)) {
    // файл существует и является обычным файлом
    if (is_readable($filename)) {
        // можно читать
    }
    if (is_writable($filename)) {
        // можно писать
    }
}

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

Использование только file_exists() может вернуть true для директорий. Поэтому обязательно добавляйте is_file(). Типичная ошибка - путаница с is_dir(), которая проверяет только директории. Также стоит помнить, что file_exists() кэширует результаты для одного запроса, для сброса кэша применяется clearstatcache().

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

Для проверки прав доступа используются функции is_readable() и is_writable(). Они возвращают true, если у текущего процесса есть соответствующие разрешения.

$file = 'data.csv';
if (is_readable($file)) {
    echo 'Файл доступен для чтения';
}
if (is_writable($file)) {
    echo 'Файл доступен для записи';
}
На платформе Windows права доступа работают иначе, и функции могут не учитывать некоторые ограничения. Также следует учитывать, что для записи может потребоваться проверка прав на директорию, если файл ещё не существует.

Как определить MIME-тип файла (например, является ли он изображением)?

Для получения MIME-типа используйте расширение fileinfo и функцию finfo_file(). Это более надёжный способ, чем проверка расширения.

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, 'image.jpg');
finfo_close($finfo);
if (str_starts_with($mime, 'image/')) {
    echo 'Это изображение, тип: ' . $mime;
}
Расширение fileinfo может быть отключено в некоторых конфигурациях PHP, хотя обычно включено. Альтернатива - устаревшая функция mime_content_type(), но она менее точная. Также стоит помнить, что MIME-тип можно подделать, добавив магические байты.

Как определить тип файла по его расширению?

Расширение извлекается с помощью pathinfo(). Этот метод полезен для быстрой фильтрации, но не является безопасным.

$path = 'document.pdf';
$ext = pathinfo($path, PATHINFO_EXTENSION);
$allowed = ['pdf', 'doc', 'docx'];
if (in_array($ext, $allowed)) {
    echo 'Расширение разрешено';
}
Расширение легко подделать (например, переименовать вредоносный файл в .pdf). По этой причине проверку расширения всегда следует комбинировать с проверкой MIME-типа.

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

Размер файла в байтах возвращает filesize(). Для проверки доступного места на диске подходит disk_free_space().

$filename = 'upload.zip';
$maxSize = 10 * 1024 * 1024; // 10 МБ
if (file_exists($filename) && filesize($filename) > $maxSize) {
    echo 'Файл слишком большой (макс. 10 МБ)';
}
$free = disk_free_space('/');
if ($free < 100 * 1024 * 1024) {
    echo 'Мало свободного места на диске';
}
filesize() кэширует результат, поэтому после изменения файла нужно вызвать clearstatcache(). Для больших файлов (более 2 ГБ на 32-битных системах) результат может быть некорректным - используйте sprintf('%u', filesize()) или специализированные решения.

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

Время последнего изменения возвращает filemtime(). Для времени последнего доступа - fileatime(), для времени создания - filectime() (на самом деле это время изменения inode).

$filename = 'config.php';
$mtime = filemtime($filename);
if (time() - $mtime > 86400) {
    echo 'Файл не изменялся более суток';
}
$ctime = filectime($filename);
echo 'Дата создания (inode): ' . date('Y-m-d H:i:s', $ctime);
filectime() на Windows возвращает время создания файла, на Unix - время изменения метаданных. fileatime() может обновляться при каждом чтении, что снижает производительность, если файлов много.

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

Проверить, является ли путь символической ссылкой, можно с помощью is_link(). Чтобы получить реальный путь (без ссылок), используйте realpath().

$path = 'link_to_file';
if (is_link($path)) {
    $target = readlink($path);
    echo 'Ссылка ведёт на ' . $target;
}
$real = realpath($path);
if ($real !== false) {
    echo 'Реальный путь: ' . $real;
}
realpath() может вернуть false, если файл не существует или нет прав доступа. Для работы с несуществующими файлами realpath не подходит.

Как проверить, что файл является PHP-скриптом?

Комбинация проверки расширения (.php), MIME-типа (text/x-php или application/x-php) и просмотр первых байтов на наличие открывающего тега <?. Надёжнее всего анализировать содержимое с помощью token_get_all() для парсинга.

$filename = 'script.php';
$ext = pathinfo($filename, PATHINFO_EXTENSION);
$isPhp = false;
if ($ext === 'php') {
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $filename);
    if ($mime === 'text/x-php' || $mime === 'application/x-httpd-php') {
        $content = file_get_contents($filename, false, null, 0, 100);
        if (str_contains($content, '<?')) {
            $isPhp = true;
        }
    }
    finfo_close($finfo);
}
echo $isPhp ? 'Это PHP-скрипт' : 'Не PHP-скрипт';
Файл может иметь расширение .php, но не содержать PHP-кода (например, просто текст). Проверка MIME не всегда точна, если сервер не настроен на распознавание PHP-файлов. Полный синтаксический анализ через token_get_all() даёт более надёжный результат, но требует загрузки всего файла.

Расширенные примеры проверки файлов

Пример 1: Проверка загружаемого файла с комплексной валидацией

Функция проверяет расширение, MIME-тип, размер, а также наличие вредоносного содержимого путём поиска опасных конструкций в первых байтах.

Пример
function validateUploadedFile(string $filepath, array $allowedExtensions, array $allowedMimes, int $maxSize): array {
    $result = ['valid' => false, 'error' => ''];

    if (!file_exists($filepath) || !is_file($filepath)) {
        $result['error'] = 'Файл не существует';
        return $result;
    }

    $ext = pathinfo($filepath, PATHINFO_EXTENSION);
    if (!in_array(strtolower($ext), $allowedExtensions)) {
        $result['error'] = 'Недопустимое расширение';
        return $result;
    }

    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $filepath);
    finfo_close($finfo);
    if (!in_array($mime, $allowedMimes)) {
        $result['error'] = 'Недопустимый MIME-тип: ' . $mime;
        return $result;
    }

    $size = filesize($filepath);
    if ($size > $maxSize) {
        $result['error'] = 'Файл превышает лимит в ' . $maxSize . ' байт';
        return $result;
    }

    // Базовый поиск опасных конструкций (например, PHP-код)
    $dangerousPatterns = ['<?', '

	

Проверка файлов PHP - comments

En
проверка файлов php (php)