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

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

.htaccess per-dir context

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  4. иметь возможность переписывать директивы в более глубоко вложенных файлах .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 или переменных среды .

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

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

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