При установке переменных среды в директивах Apache RewriteRule, что вызывает имя переменной с префиксом "REDIRECT_"? - PullRequest
68 голосов
/ 16 июня 2010

Я пытаюсь установить переменные окружения 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}]

Ответы [ 3 ]

72 голосов
/ 12 апреля 2012

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

.htaccess для каждого каталога dir

Вот что, похоже, происходит в .htaccess для каждого каталога (для каждого каталога).dir) context:

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

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

  2. Перезапись начинается

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

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

    Если вы не знакомы с этой частью, см. Документацию флага L :

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

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

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

Когда я впервые столкнулся с этой проблемой, я делал что-то вроде следующего в .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 работает аналогично: директивы перезаписи обрабатываются снова, переименованная переменнаяснова назначается исходное имя, и если 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 файлах

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

@ метод trowel

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

VAL может содержать обратные ссылки ($ N или% N), которые будут расширены.

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

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

SetEnvIf

Я не рекомендую полагаться на это, потому что это не соответствует документированное поведение (см. ниже), но это (в docroot/.htaccess, с Apache 2.2.20) работает для меня:

SetEnvIf REDIRECT_VAR (.+) VAR=$1

Только те переменные среды, которые определеныболее ранние директивы SetEnvIf [NoCase] ​​доступны для тестированиятаким образом.

Почему?

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

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

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

8 голосов
/ 16 июня 2010

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

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

РЕДАКТИРОВАТЬ - еще несколько ответов на предложенный вопрос:

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;
}
0 голосов
/ 19 июня 2018

Поскольку я не хочу менять какой-либо код (и при этом я не могу изменить код используемых библиотек) , я выбрал следующий подход: во время начальной загрузки моего приложения - например, в моем 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() - могут нормально работать.


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

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