it-roy-ru.com

При установке переменных среды в директивах Apache RewriteRule, что вызывает имя переменной с префиксом "REDIRECT_"?

Я пытаюсь установить переменные окружения Apache (для использования в PHP) с флагом [E=VAR:VAL] в правилах RewriteRule в файле .htaccess.

Я уже обнаружил, что переменные доступны в PHP как переменные сервера $_SERVER, а не $_ENV (что имеет определенный смысл). Однако моя проблема заключается в том, что для некоторых правил флаг [E=VAR:VAL] работает, как и ожидалось, и я получаю переменную $_SERVER['VAR'], но для других правил я заканчиваю переменной $_SERVER['REDIRECT_VAR'] или $_SERVER['REDIRECT_REDIRECT_VAR'] и т.д.

A. Что приводит к тому, что переменная окружения, установленная в Apache с использованием флага [E=VAR:VAL], переименовывается при добавлении «REDIRECT_» к имени переменной?

B. Что я могу сделать, чтобы убедиться, что я получаю переменную среды с неизменным именем, чтобы я мог обращаться к ней в PHP как $_SERVER['VAR'], не прибегая к проверке вариаций имени переменной, имеющего один или несколько экземпляров "REDIRECT_" добавлен к нему?

Частичное решение найдено . Добавление следующего к началу правил перезаписи воссоздает исходный ENV: VAR при каждом перенаправлении (а также оставляет там версии REDIRECT_VAR), если они необходимы:

RewriteCond %{ENV:REDIRECT_VAR} !^$
RewriteRule .* - [E=VAR:%{ENV:REDIRECT_VAR}]
66
trowel

Такое поведение вызывает сожаление и даже не представляется документально подтвержденным.

.htaccess per-dir context

Вот что происходит в контексте .htaccess на каталог (на каталог):

Предположим, что Apache обрабатывает файл .htaccess, который содержит директивы перезаписи.

  1. Apache заполняет свою карту переменных среды всеми стандартными переменными CGI/Apache

  2. Переписывание начинается

  3. Переменные среды устанавливаются в директивах RewriteRule

  4. Когда Apache прекращает обработку директив RewriteRule (из-за флага L или конца набора правил) и URL-адрес был изменен переменной RewriteRule, Apache возобновляет обработку запроса .

    Если вы не знакомы с этой частью, обратитесь к документации L flag :

    Таким образом, набор правил может быть снова запущен с самого начала. Чаще всего это происходит, если одно из правил вызывает перенаправление - внутреннее или внешнее - что приводит к тому, что процесс запроса запускается сначала .
  5. Из того, что я могу наблюдать, я считаю, что когда происходит # 4, # 1 повторяется, тогда переменные среды, которые были установлены в директивах RewriteRule, начинаются с REDIRECT_ и добавляются в карту переменных среды (не обязательно в этом порядке, но конечный результат, состоящий из этой комбинации) .

    На этом этапе имена выбранных переменных стираются, и через минуту я объясню, почему это так важно и неудобно .

Восстановление имен переменных

Когда я впервые столкнулся с этой проблемой, я делал что-то вроде следующего в .htaccess (упрощенно):

RewriteCond %{HTTP_Host} (.+)\.projects\.

RewriteRule (.*) subdomains/%1/docroot/$1

RewriteRule (.+/docroot)/ - [L,E=EFFECTIVE_DOCUMENT_ROOT:$1]

Если бы я должен был установить переменную окружения в первой переменной RewriteRule, Apache перезапустил бы процесс перезаписи и добавил бы переменную с помощью REDIRECT_ (шаги 4 и 5 выше), таким образом, я бы потерял доступ к ней через назначенное мной имя.

В этом случае первый RewriteRule изменяет URL-адрес, поэтому после обработки обоих RewriteRule Apache перезапускает процедуру и снова обрабатывает .htaccess. Во второй раз первая RewriteRule пропускается из-за директивы RewriteCond, но вторая RewriteRule совпадает, устанавливает переменную окружения (снова) и, что важно, не меняет URL. Таким образом, процесс запроса/перезаписи не начинается заново, и выбранное мной имя переменной остается неизменным. В этом случае у меня фактически есть оба REDIRECT_EFFECTIVE_DOCUMENT_ROOT и EFFECTIVE_DOCUMENT_ROOT. Если бы я использовал флаг L в первом RewriteRule, у меня был бы только EFFECTIVE_DOCUMENT_ROOT.

Частичное решение @ trowel работает аналогично: директивы rewrite снова обрабатываются, переименованная переменная снова присваивается исходному имени, и если URL-адрес не изменяется, процесс заканчивается, и назначенное имя переменной остается неизменным.

Почему эти методы неадекватны

Оба эти метода страдают одним существенным недостатком: когда правила перезаписи в файле .htaccess, в котором вы устанавливаете переменные среды, переписывают URL-адрес в более глубоко вложенный каталог, в котором есть файл .htaccess, который выполняет любую перезапись, назначенное имя переменной снова стирается ,.

Скажем, у вас есть макет каталога, как это:

docroot/
        .htaccess
        A.php
        B.php
        sub/
                .htaccess
                A.php
                B.php

И docroot/.htaccess, как это:

RewriteRule ^A\.php sub/B.php [L]

RewriteRule .* - [E=MAJOR:flaw]

Итак, вы запрашиваете /A.php, и он переписывается в sub/B.php. У вас все еще есть переменная MAJOR.

Однако если у вас есть какие-либо директивы перезаписи в docroot/sub/.htaccess (даже просто RewriteEngine Off или RewriteEngine On), ваша переменная MAJOR исчезнет. Это связано с тем, что после перезаписи URL-адреса на sub/B.php обрабатывается docroot/sub/.htaccess, и если он содержит какие-либо директивы перезаписи, директивы перезаписи в docroot/.htaccess больше не обрабатываются. Если у вас был REDIRECT_MAJOR после обработки docroot/.htaccess (например, если вы опускаете флаг L в первой RewriteRule), он все равно будет у вас, но эти директивы больше не будут запускаться для установки выбранного вами имени переменной.

Наследование

Итак, скажем, вы хотите:

  1. установить переменные окружения в директивах RewriteRule на определенном уровне дерева каталогов (например, docroot/.htaccess)

  2. сделать их доступными в сценариях на более глубоких уровнях

  3. иметь их в наличии с присвоенными именами

  4. иметь возможность переписывать директивы в более глубоко вложенных файлах .htaccess

Возможное решение - использовать директивы RewriteOptions inherit в более глубоко вложенных файлах .htaccess. Это позволяет вам повторно запускать директивы перезаписи в файлах с меньшей глубиной вложенности и использовать методы, описанные выше, для установки переменных с выбранными именами. Тем не менее, обратите внимание, что это увеличивает сложность, потому что вам нужно быть более осторожным при составлении директив перезаписи в менее глубоко вложенных файлах, чтобы они не вызывали проблем при повторном запуске из более глубоко вложенных каталогов. Я полагаю, что Apache удаляет префикс per-dir для более глубоко вложенного каталога и запускает директивы rewrite в менее глубоко вложенных файлах с этим значением.Техника @ Мастерок.

Насколько я вижу, поддержка использования конструкции, подобной %{ENV:REDIRECT_VAR}, в компоненте значения флага RewriteRuleE (например, [E=VAR:%{ENV:REDIRECT_VAR}]), по-видимому, не является задокументировано :

Кажется, это работает, но если вы хотите не полагаться на что-то недокументированное (пожалуйста, исправьте меня, если я ошибаюсь в этом), это можно легко сделать следующим образом:.

RewriteCond %{ENV:REDIRECT_VAR} (.+) RewriteRule .* - [E=VAR:%1]

docroot/.htaccess, с Apache 2.2.20) работает для меня:

SetEnvIf REDIRECT_VAR (.+) VAR=$1

Зачем?.

Я не знаю, каково обоснование для префикса этих имен с помощью REDIRECT_ - неудивительно, так как это не упоминается в разделах документации Apache для директивы mod_rewrite , RewriteRule flags или переменные среды .

На данный момент это кажется большой неприятностью для меня, из-за отсутствия объяснения, почему это лучше, чем оставить назначенные имена в покое. Отсутствие документации только способствует моему скептицизму по этому поводу.

Возможность назначать переменные окружения в правилах перезаписи полезна, или, по крайней мере, так будет. Но полезность значительно снижается этим изменяющим имя поведением. Сложность этого поста иллюстрирует, насколько чокнуты это поведение и обручи, которые нужно преодолеть, чтобы попытаться его преодолеть.

Being able to assign environment variables in rewrite rules is useful, or at least, it would be. But the usefulness is greatly diminished by this name-changing behavior. The complexity of this post illustrates how nuts this behavior and the hoops that have to be jumped through to try to overcome it are.

71
JMM

Я вообще не проверял это и знаю, что он не затрагивает точки A или B, но в комментариях к документации PHP есть описание этой проблемы и некоторые возможные решения для доступа к этим переменным с использованием $_SERVER['VAR']:

http://www.php.net/manual/en/reserved.variables.php#79811

EDIT - еще несколько ответов на предложенный вопрос:

A: Переменные среды переименовываются Apache, если они участвуют в перенаправлении. Например, если у вас есть следующее правило:

RewriteRule ^index.php - [E=VAR1:'hello',E=VAR2:'world']

Затем вы можете получить доступ к VAR1 и VAR2, используя $_SERVER['VAR1'] и $_SERVER['VAR2']. Однако, если вы перенаправите страницу следующим образом:

RewriteRule ^index.php index2.php [E=VAR1:'hello',E=VAR2:'world']

Тогда вы должны использовать $_SERVER['REDIRECT_VAR1'] и т.д.

B: Лучший способ преодолеть эту проблему - обработать переменные, которые вас интересуют, с помощью PHP. Создайте функцию, которая запускается через массив $_SERVER и находит нужные вам элементы. Вы можете даже использовать такую ​​функцию:

function myGetEnv($key) {
    $prefix = "REDIRECT_";
    if(array_key_exists($key, $_SERVER))
        return $_SERVER[$key];
    foreach($_SERVER as $k=>$v) {
        if(substr($k, 0, strlen($prefix)) == $prefix) {
            if(substr($k, -(strlen($key))) == $key)
                return $v;
        }
    }
    return null;
}
8
thetaiko

Поскольку я не хочу менять какой-либо код (и при этом я не могу изменить код используемых библиотек), я выбрал следующий подход: при загрузке моего приложения - например, в моем index.php - я переделываю суперглобальный $_ENV так, чтобы переменные с префиксом REDIRECT_ были переписаны с их обычным предполагаемым именем:

// Fix ENV vars getting prepended with `REDIRECT_` by Apache
foreach ($_ENV as $key => $value) {
    if (substr($key, 0, 9) === 'REDIRECT_') {
        $_ENV[str_replace('REDIRECT_', '', $key)] = $value;
        putenv(str_replace('REDIRECT_', '', $key) . '=' . $value);
    }
}

Мы не только напрямую устанавливаем его в $_ENV, но также храним его с помощью putenv(). Таким образом, существующий код и библиотеки - которые могут использовать getenv() - могут работать нормально.


На заметку: если вы извлекаете заголовки - как HTTP_AUTHORIZATION - в своем коде, вам нужно выполнить те же манипуляции с $_SERVER:

foreach ($_SERVER as $key => $value) {
    if (substr($key, 0, 9) === 'REDIRECT_') {
        $_SERVER[str_replace('REDIRECT_', '', $key)] = $value;
    }
}
0
Bramus