При настройке переменных среды в директивах 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'] и т.д.

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

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

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

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

Ответ 1

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

.htaccess per-dir context

Вот что происходит в контексте .htaccess per-directory (per-dir):

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

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

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

  • Переменные среды задаются в директивах RewriteRule

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

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

    таким образом, набор правил может снова запускаться с самого начала. Чаще всего это произойдет, если одно из правил вызывает перенаправление - внутреннее или внешнее - заставляя процесс запроса начать работу.
  • Из того, что я могу наблюдать, я считаю, что когда # 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

И a docroot/.htaccess следующим образом:

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

RewriteRule .* - [E=MAJOR:flaw]

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

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

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

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

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

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

  • иметь их доступными с назначенными именами

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

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

@технология

Насколько я понимаю, поддержка использования такой конструкции, как %{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 или переменные окружения.

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

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

Ответ 2

Я вообще не тестировал это, и я знаю, что он не касается точек 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;
}