Пути к файлам в PHP: правильное указание адресов
Работа с адресами файлов в PHP
Наиболее эффективное решение для получения корректного адреса файла
Комбинация магической константы __DIR__ и функции realpath() позволяет получить абсолютный путь без символических ссылок и лишних компонентов.
<?php
$baseDir = __DIR__; // путь к каталогу текущего скрипта
$targetPath = realpath($baseDir . '/../includes/config.php');
echo $targetPath;
?>
Результат:
/var/www/html/includes/config.php
Такой подход гарантирует, что путь будет нормализован, а слеши приведены к корректному виду для операционной системы.
Возможные проблемы:
- Если файл не существует,
realpath()вернётfalse. Нужна проверка. - При использовании
__DIR__внутри подключаемого файла путь будет соответствовать местоположению этого файла, а не родительского скрипта.
Как получить полный путь к текущему исполняемому скрипту?
Используется магическая константа __FILE__ или серверная переменная $_SERVER['SCRIPT_FILENAME'].
<?php
echo __FILE__ . PHP_EOL;
echo $_SERVER['SCRIPT_FILENAME'];
?>
Результаты могут отличаться при вызове из командной строки или через веб-сервер. __FILE__ всегда возвращает путь к файлу, где она написана.
Типичная ошибка:
Путаница между путями при использовании include и require. Если внутри подключаемого файла написан __FILE__, он покажет путь к подключаемому файлу, а не к главному.
Как получить путь к корню веб-сайта, не зависящий от расположения скрипта?
Наиболее надёжный способ - использовать $_SERVER['DOCUMENT_ROOT'] и добавить свою относительную часть.
<?php
$root = $_SERVER['DOCUMENT_ROOT'];
$path = $root . '/uploads/photo.jpg';
echo $path;
?>
Результат:
/var/www/html/uploads/photo.jpg
Ограничения:
DOCUMENT_ROOT отсутствует в CLI-режиме. В некоторых конфигурациях он может не соответствовать реальному корню.
Как преобразовать относительный путь в абсолютный?
Функция realpath() делает это, но требует существования файла. Альтернатива - ручная нормализация с учётом __DIR__ и очисткой компонентов.
<?php
function resolveRelativePath($relative) {
$parts = explode('/', $relative);
$result = explode('/', __DIR__);
foreach ($parts as $part) {
if ($part === '..') {
array_pop($result);
} elseif ($part !== '.') {
$result[] = $part;
}
}
return implode('/', $result);
}
echo resolveRelativePath('../templates/header.php');
?>
Результат (пример):
/var/www/html/templates/header.php
Недостаток:
Такая функция не работает с символическими ссылками и не проверяет существование файла. Желательно использовать realpath() после проверки.
Как правильно работать с путями в разных операционных системах (Windows / Linux)?
Константа DIRECTORY_SEPARATOR и PATH_SEPARATOR помогают создавать кроссплатформенные пути.
<?php
$path = 'config' . DIRECTORY_SEPARATOR . 'app.php';
echo $path;
?>
На Windows результат:
config\app.php, на Linux:
config/app.php
Типичная ошибка:
Жёстко прописанные обратные слеши в коде, которые не работают на Linux. Рекомендуется всегда использовать DIRECTORY_SEPARATOR или нормализовать пути через str_replace.
Как разобрать путь на компоненты: каталог, имя файла, расширение?
Функция pathinfo() возвращает массив с ключами dirname, basename, extension, filename.
<?php
$info = pathinfo('/var/www/html/index.php');
print_r($info);
?>
Результат:
Array
(
[dirname] => /var/www/html
[basename] => index.php
[extension] => php
[filename] => index
)
Нюанс:
Если файл не имеет расширения, ключ extension будет пустой строкой. Ключ filename доступен с PHP 5.2.
Как проверить, существует ли файл по заданному адресу?
Функции file_exists(), is_file(), is_dir().
<?php
$file = '/etc/passwd';
if (file_exists($file)) {
echo 'Файл существует';
}
?>
Проблема кэширования:
Результаты этих функций кэшируются для повышения производительности. При частых проверках в течение одного запроса можно получить устаревшие данные. Для сброса кэша используется clearstatcache().
Расширенные примеры работы с адресами файлов
Пример 1: Рекурсивный обход каталога с получением абсолютных путей
<?php
$directory = __DIR__ . '/project';
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $fileinfo) {
echo $fileinfo->getRealPath() . PHP_EOL;
}
?>
Результат (пример):
/var/www/html/project/index.php /var/www/html/project/css/style.css /var/www/html/project/js/script.js
Пояснение: использование RecursiveDirectoryIterator с флагом SKIP_DOTS и RecursiveIteratorIterator для обхода всех подкаталогов. Метод getRealPath() возвращает абсолютный путь, разрешая симлинки.
Пример 2: Создание универсальной функции для построения пути из частей
<?php
function buildPath(...$parts) {
$normalized = [];
foreach ($parts as $part) {
$trimmed = trim($part, DIRECTORY_SEPARATOR);
if ($trimmed !== '') {
$normalized[] = $trimmed;
}
}
return implode(DIRECTORY_SEPARATOR, $normalized);
}
echo buildPath('/var/www/', 'html', 'uploads', 'image.jpg');
?>
Результат:
/var/www/html/uploads/image.jpg
Пояснение: функция собирает части, удаляет лишние разделители и использует константу DIRECTORY_SEPARATOR для кроссплатформенности.
Пример 3: Безопасное сохранение загруженного файла
<?php
$uploadDir = __DIR__ . DIRECTORY_SEPARATOR . 'uploads';
$originalName = basename($_FILES['userfile']['name']); // защита от path traversal
$targetPath = $uploadDir . DIRECTORY_SEPARATOR . uniqid() . '_' . $originalName;
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $targetPath)) {
echo 'Файл сохранён: ' . $targetPath;
}
?>
Результат:
Файл сохранён: /var/www/html/uploads/5f3a2b1c_image.jpg
Пояснение: basename() удаляет любые попытки выхода за пределы каталога, uniqid() добавляет уникальный префикс, DIRECTORY_SEPARATOR обеспечивает корректный разделитель.
Пример 4: Разрешение символических ссылок и проверка типа файла
<?php
$link = '/var/www/current'; // символическая ссылка на /var/www/html
$real = realpath($link);
echo $real . PHP_EOL;
echo is_link($link) ? 'Это ссылка' : 'Не ссылка';
?>
Результат:
/var/www/html Это ссылка
Пояснение: realpath() возвращает реальный путь, пройдя по цепочке симлинков. is_link() проверяет, является ли переданный путь символической ссылкой.
Пример 5: Извлечение последнего расширения файла
<?php
$file = '/path/to/file.archive.tar.gz';
$ext = pathinfo($file, PATHINFO_EXTENSION);
echo $ext; // выведет 'gz', а не 'tar.gz'
?>
Результат:
gz
Пояснение: pathinfo() с константой PATHINFO_EXTENSION возвращает только часть после последней точки. Для сложных составных расширений требуется дополнительная обработка.