Извлечение URI из index.php: эффективные способы и нюансы настройки

Раздел: Веб-программирование -> Маршрутизация

При создании современного веб-приложения с единой точкой входа (front controller) файл index.php обрабатывает все входящие запросы. Для правильной маршрутизации необходимо извлечь URI (Uniform Resource Identifier) из HTTP-запроса. Далее рассмотрены основные подходы, их особенности и типичные проблемы.

Основное решение: использование $_SERVER['REQUEST_URI'] и parse_url

Наиболее распространённый способ получить URI - обратиться к суперглобальному массиву $_SERVER['REQUEST_URI']. Эта переменная содержит полный URI, переданный клиентом, включая путь и строку запроса. Для дальнейшей маршрутизации обычно нужен только путь (без query string). Для его извлечения используется функция parse_url().


<?php
// Получение полного URI
$fullUri = $_SERVER['REQUEST_URI']; // /page?id=1

// Извлечение только пути
$path = parse_url($fullUri, PHP_URL_PATH); // /page

// Удаление начального слеша для единообразия
$path = ltrim($path, '/');
?>

Index php uri (получение uri в index.php)

Цель: получить чистый путь для сопоставления с маршрутами приложения.

Возможные проблемы:

  • Двойные слеши в URI (//page//sub) - могут привести к неожиданным результатам. Решение: нормализация через preg_replace('/\/+/', '/', $path).
  • Path traversal (../../etc) - потенциальная уязвимость. Решение: проверка на недопустимые символы и использование basename() или realpath().
  • XSS, если URI выводится без экранирования. Решение: при выводе использовать htmlspecialchars().
  • Многобайтовые символы в URI (кириллица) - parse_url работает корректно, но лучше дополнительно декодировать через rawurldecode().

Как получить URI без строки запроса (query string)?

Использование parse_url($uri, PHP_URL_PATH) уже показано выше. Альтернативно можно использовать strstr($uri, '?', true), если гарантированно есть строка запроса, но при её отсутствии вернётся false. Рекомендуется применять parse_url.

Типичная ошибка: путаница с переменной $_SERVER['QUERY_STRING'] - там только строка запроса, а не полный URI.

Как обработать URI, если сайт расположен не в корневой директории?

Если index.php находится в подпапке (например, /myapp/index.php), то $_SERVER['REQUEST_URI'] будет содержать /myapp/page. Для маршрутизации необходимо удалить префикс с путём к скрипту. Определить префикс можно через $_SERVER['SCRIPT_NAME'] или dirname($_SERVER['SCRIPT_NAME']).


<?php
$basePath = dirname($_SERVER['SCRIPT_NAME']); // /myapp
$uri = $_SERVER['REQUEST_URI']; // /myapp/page?id=1
$path = parse_url($uri, PHP_URL_PATH); // /myapp/page
$path = substr($path, strlen($basePath)); // /page
$path = ltrim($path, '/');
?>

Php route common home (php маршрут common home)

Цель: унификация кода для работы как в корне, так и в поддиректориях.

Проблемы:

  • Если SCRIPT_NAME содержит путь к index.php с именем файла, dirname может дать точку (.) для корня. Нужно предусмотреть этот случай.
  • Различия в поведении SCRIPT_NAME и PHP_SELF - лучше использовать SCRIPT_NAME, так как PHP_SELF может включать PATH_INFO.

Как использовать PATH_INFO для получения части URI после имени скрипта?

При настройке веб-сервера (например, Apache с mod_rewrite) можно направить запросы на index.php таким образом, чтобы часть URI после имени скрипта попала в переменную $_SERVER['PATH_INFO']. Пример правила .htaccess:


RewriteEngine On
RewriteRule ^index\.php/(.*)$ index.php/$1 [L]

Search index php route (маршрут поиска в php)

Теперь запрос /index.php/page/123 будет передан скрипту, а $_SERVER['PATH_INFO'] примет значение /page/123. В index.php:


<?php
$path = $_SERVER['PATH_INFO'] ?? '/';
$path = ltrim($path, '/');
?>

Index php page url ru (url страницы index.php на русском)

Цель: использование встроенных механизмов веб-сервера без перезаписи всего URL.

Ошибки:

  • Переменная PATH_INFO может отсутствовать, если не настроен корректный RewriteRule.
  • В некоторых конфигурациях (например, Nginx) PATH_INFO не поддерживается.

Как передать URI через параметр q в .htaccess?

Популярный метод для «человеко-понятных» URL (ЧПУ): все запросы перенаправляются в index.php с параметром q, содержащим исходный URI. Правило .htaccess:


RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

Php routing (роутинг в php)

В index.php URI получается так:


<?php
$uri = $_GET['q'] ?? '/';
// Далее можно разбирать $uri
?>

Цель: простота и совместимость, не требуется дополнительных преобразований.

Проблемы:

  • Параметр q может конфликтовать с другими GET-параметрами. Решение: использовать уникальное имя (path, route).
  • Пустой q означает корневой URI, нужно задать значение по умолчанию.
  • При включении QSA все остальные параметры сохраняются, но могут быть потеряны, если q переопределяется. В примере QSA добавлен.

Как нормализовать URI (удалить повторяющиеся слеши, декодировать символы)?

Часто приходит URI вида /page//sub/?id=1 – такие URI нужно привести к единому формату. Нормализация включает:

  • Замена нескольких слешей одним: preg_replace('/\/+/', '/', $path).
  • Декодирование percent-encoding: rawurldecode($path).
  • Удаление точек и лишних сегментов (для безопасности).

<?php
function normalizeUri($uri) {
    $path = parse_url($uri, PHP_URL_PATH);
    $path = rawurldecode($path);
    $path = preg_replace('/\/+/', '/', $path);
    $path = rtrim($path, '/');
    return $path ?: '/';
}
?>

Цель: гарантировать, что одинаковые URI будут приведены к одному виду, облегчая сопоставление маршрутов.

Ошибки: излишнее декодирование может превратить %2F обратно в слэш, что меняет структуру пути. Используйте только безопасные функции.

Как защитить приложение от path traversal и вредоносных символов?

При маршрутизации нельзя доверять URI. Для защиты:

  • Отбрасывать сегменты, содержащие '..'.
  • Использовать регулярное выражение, допускающее только буквы, цифры и тире.
  • Проверять длину URI (максимум 2048 символов).

<?php
function sanitizePath($path) {
    // Удаление path traversal
    $parts = explode('/', $path);
    $filtered = [];
    foreach ($parts as $part) {
        if ($part === '..' || $part === '.') continue;
        $filtered[] = preg_replace('/[^a-zA-Z0-9\-_\/]/', '', $part);
    }
    return implode('/', $filtered);
}
?>

Цель: минимизация рисков при использовании URI в файловой системе или в маршрутах.

Распространённая ошибка: чрезмерная фильтрация, которая блокирует допустимые символы (кириллица, пробелы). Необходимо заранее определить допустимые наборы.

Выбор конкретного метода зависит от конфигурации сервера, требований к безопасности и удобства разработки. Комбинирование подходов (например, получение через $_SERVER['REQUEST_URI'] с последующей нормализацией) даёт универсальное решение.

Расширенные примеры получения и обработки URI

Ниже приведены более сложные сценарии, демонстрирующие интеграцию методов в реальных проектах.

Функция getCleanUri() для единой точки входа

Универсальная функция, учитывающая базовый путь и query string:

Пример

<?php
function getCleanUri() {
    // Получение полного URI
    $requestUri = $_SERVER['REQUEST_URI'];
    
    // Разбор на путь и строку запроса
    $parsed = parse_url($requestUri);
    $path = $parsed['path'] ?? '/';
    
    // Удаление базового пути (если скрипт в поддиректории)
    $basePath = dirname($_SERVER['SCRIPT_NAME']);
    if ($basePath !== '/' && strpos($path, $basePath) === 0) {
        $path = substr($path, strlen($basePath));
    }
    
    // Нормализация
    $path = rawurldecode($path);
    $path = preg_replace('/\/+/', '/', $path);
    $path = rtrim($path, '/');
    
    return $path ?: '/';
}

echo getCleanUri(); // Пример вывода: /page/sub
?>
При запросе /myapp/page/sub?x=1 вернёт /page/sub

Класс Router с извлечением пути и параметров

Пример простого маршрутизатора, который обрабатывает динамические сегменты:

Пример

<?php
class Router {
    private $routes = [];
    
    public function add($pattern, $callback) {
        $this->routes[$pattern] = $callback;
    }
    
    public function dispatch($uri) {
        $path = parse_url($uri, PHP_URL_PATH);
        $path = rtrim($path, '/') ?: '/';
        
        foreach ($this->routes as $pattern => $callback) {
            // Преобразование плейсхолдеров {id} в регулярное выражение
            $regex = preg_replace('/\{([a-z]+)\}/', '(?P<$1>[^/]+)', $pattern);
            $regex = '#^' . $regex . '$#';
            
            if (preg_match($regex, $path, $matches)) {
                $params = array_filter($matches, 'is_string', ARRAY_FILTER_USE_KEY);
                return call_user_func($callback, $params);
            }
        }
        http_response_code(404);
        echo '404 Not Found';
    }
}

$router = new Router();
$router->add('/user/{id}', function($params) {
    echo 'User ID: ' . htmlspecialchars($params['id']);
});

$router->dispatch('/user/42');
?>
User ID: 42

Поддержка мультиязычности через префикс в URI

Извлечение двухбуквенного кода языка из начала пути:

Пример

<?php
$uri = $_SERVER['REQUEST_URI'];
$path = parse_url($uri, PHP_URL_PATH);
$path = ltrim($path, '/');

$language = 'en'; // по умолчанию
$segments = explode('/', $path);
if (preg_match('/^[a-z]{2}$/', $segments[0] ?? '')) {
    $language = array_shift($segments);
}
$route = '/' . implode('/', $segments);

echo "Language: $language, Route: $route";
?>
При запросе /ru/about -> Language: ru, Route: /about

Работа с вложенными подпапками на сервере Nginx

Для Nginx типичная конфигурация единой точки входа:

Пример

server {
    listen 80;
    root /var/www/html;
    index index.php;
    
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param QUERY_STRING $args;
    }
}

В index.php при таком подходе uri содержится в $_SERVER['REQUEST_URI'], так как Nginx передаёт его без изменений. Однако у разных версий может отличаться - рекомендуется проверить.

Обработка URI с сохранением строки запроса при перезаписи

Пример .htaccess с передачей всех параметров:

Пример

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?path=$1 [L,QSA]

В index.php:

Пример

<?php
$path = $_GET['path'] ?? '';
$query = $_SERVER['QUERY_STRING']; // содержит все исходные параметры
// Далее можно объединить
parse_str($query, $params);
unset($params['path']); // удаляем использованный параметр
$finalQuery = http_build_query($params);
?>

Результат: при запросе /product/5?sort=asc в $path = 'product/5', $finalQuery = 'sort=asc'.

Получение URI в index.php - comments

En
Index php uri (php)