Как получить расширение файла в PHP? Надёжные способы и подводные камни
Использование pathinfo() — стандартный и надёжный способ
В PHP есть встроенная функция pathinfo(), которая специально создана для разбора путей к файлам. Она возвращает ассоциативный массив с ключами dirname, basename, filename и extension. Это гораздо безопаснее и удобнее, чем ручной разбор через explode().
<?php
// Простейший случай
$filename = "document.pdf";
$info = pathinfo($filename);
echo $info['extension']; // pdf
// Файл с несколькими точками
$filename2 = "my.file.name.txt";
$info2 = pathinfo($filename2);
echo $info2['extension']; // txt (последняя часть после последней точки)
// Полный путь
$path = "/var/www/uploads/image.jpg";
$info3 = pathinfo($path);
echo $info3['extension']; // jpg
echo $info3['filename']; // image
echo $info3['basename']; // image.jpg
?>
Особые случаи
Функция pathinfo() корректно обрабатывает почти все граничные случаи:
- Файлы без расширения (например,
MakefileилиREADME):pathinfo()вернёт пустую строку дляextension. - Скрытые файлы (например,
.htaccess):pathinfo()вернётhtaccessкак расширение. Это спорный момент, но для большинства задач такое поведение приемлемо. - Файлы с точкой в конце (например,
file.): расширение будет пустой строкой.
<?php
// Без расширения
echo pathinfo('Makefile', PATHINFO_EXTENSION); // (пусто)
// Скрытый файл
echo pathinfo('.htaccess', PATHINFO_EXTENSION); // htaccess
// Точка в конце
echo pathinfo('file.', PATHINFO_EXTENSION); // (пусто)
?>
Безопасность при работе с загруженными файлами
Если вы получаете расширение из имени загруженного файла (например, из $_FILES['file']['name']), никогда не доверяйте этому значению для определения типа файла. Злоумышленник может переименовать файл virus.exe в photo.jpg.exe или использовать null-byte инъекции (shell.php%00.jpg).
Правильный подход — определять MIME-тип файла через его содержимое, а не через расширение:
<?php
// Никогда так не делайте для проверки безопасности!
$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
if ($ext !== 'jpg') {
die('Только JPG разрешены!'); // Это можно обойти
}
// Правильно: используйте finfo для определения реального типа
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if ($mime !== 'image/jpeg') {
die('Это не JPG!');
}
?>
Дополнительные советы
- Используйте константу
PATHINFO_EXTENSIONдля получения только расширения, без создания всего массива:pathinfo($file, PATHINFO_EXTENSION). - Для получения расширения в нижнем регистре всегда используйте
strtolower():strtolower(pathinfo($file, PATHINFO_EXTENSION)). - Если вам нужно проверить, является ли файл изображением, лучше использовать
getimagesize()илиexif_imagetype().
В итоге: pathinfo() — это правильный и безопасный способ для синтаксического разбора имени файла. Но помните, что расширение — это всего лишь строка в имени, а не гарантия содержимого файла.
Альтернативный подход: регулярные выражения
Хотя pathinfo() — предпочтительный метод, в некоторых специфических ситуациях может потребоваться более гибкий подход. Например, если вам нужно извлечь не последнее расширение, а все расширения (для .tar.gz), или если вы работаете с нестандартными именами файлов.
<?php
// Извлечение всех расширений (для .tar.gz)
function getAllExtensions($filename) {
preg_match('/\.([^.]+)$/', $filename, $match);
return $match[1] ?? '';
}
function getAllExtensionsMulti($filename) {
preg_match_all('/\.([^.]+)/', $filename, $matches);
return $matches[1] ?? [];
}
echo getAllExtensions('archive.tar.gz'); // gz
print_r(getAllExtensionsMulti('archive.tar.gz'));
// Array ( [0] => tar [1] => gz )
?>
Регулярное выражение /\.([^.]+)$/ ищет точку, за которой следует один или более символов, не являющихся точкой, до конца строки. Это эквивалентно тому, что делает pathinfo(), но даёт больше контроля.
Когда это может пригодиться:
- Если вам нужно работать с архивами, имеющими двойное расширение (например,
.tar.gz,.tar.bz2). - Если вы пишете свою собственную реализацию для учебных целей.
- Если по какой-то причине
pathinfo()недоступна (хотя это крайне маловероятно).
Однако помните: для 99% реальных задач pathinfo() более чем достаточно. Регулярные выражения добавляют сложность и потенциальные ошибки без веской причины.