как использовать «И», «ИЛИ» для RewriteCond на Apache?

115

Это как использовать И, ИЛИ для RewriteCondApache?

rewritecond A [or]
rewritecond B
rewritecond C [or]
rewritecond D
RewriteRule ... something

становится if ( (A or B) and (C or D) ) rewrite_it.

Итак, похоже, что «ИЛИ» имеет более высокий приоритет, чем «И»? Есть ли способ легко сказать, как в (A or B) and (C or D)синтаксисе?

nonopolarity
источник
1
Да исправить. [ИЛИ] имеет более высокий приоритет, чем (неявное) «И». Комбинированное состояние действительно ((A или B) и (C или D)).
Doin
2
Подробности можно найти в ckollars.org/apache-rewrite-htaccess.html#precedence .
Чак Колларс,

Ответы:

118

Это интересный вопрос, и, поскольку он не объясняется очень подробно в документации, я отвечу на него, просмотрев исходный код mod_rewrite ; демонстрируя большое преимущество открытого исходного кода .

В верхнем разделе вы быстро найдете определения, используемые для именования этих флагов :

#define CONDFLAG_NONE               1<<0
#define CONDFLAG_NOCASE             1<<1
#define CONDFLAG_NOTMATCH           1<<2
#define CONDFLAG_ORNEXT             1<<3
#define CONDFLAG_NOVARY             1<<4

и поиск CONDFLAG_ORNEXT подтверждает, что он используется на основании наличия флага [OR] :

else if (   strcasecmp(key, "ornext") == 0
         || strcasecmp(key, "OR") == 0    ) {
    cfg->flags |= CONDFLAG_ORNEXT;
}

Следующее появление флага - это фактическая реализация, в которой вы найдете цикл, который проходит через все условия RewriteConditions, которые есть в RewriteRule, и что он в основном делает (вырезано, комментарии добавлены для ясности):

# loop through all Conditions that precede this Rule
for (i = 0; i < rewriteconds->nelts; ++i) {
    rewritecond_entry *c = &conds[i];

    # execute the current Condition, see if it matches
    rc = apply_rewrite_cond(c, ctx);

    # does this Condition have an 'OR' flag?
    if (c->flags & CONDFLAG_ORNEXT) {
        if (!rc) {
            /* One condition is false, but another can be still true. */
            continue;
        }
        else {
            /* skip the rest of the chained OR conditions */
            while (   i < rewriteconds->nelts
                   && c->flags & CONDFLAG_ORNEXT) {
                c = &conds[++i];
            }
        }
    }
    else if (!rc) {
        return 0;
    }
}

Вы должны уметь это интерпретировать; это означает, что OR имеет более высокий приоритет, и ваш пример действительно приводит к if ( (A OR B) AND (C OR D) ). Если у вас, например, есть следующие Условия:

RewriteCond A [or]
RewriteCond B [or]
RewriteCond C
RewriteCond D

это будет интерпретироваться как if ( (A OR B OR C) and D ).

Сьон Сигурдссон
источник
1
Мне кажется, что выполнение исходного кода в приведенном примере .htaccess дает ((A OR B) и C) или D) [т.е. не то, что надеется OP, ни то, что вы изначально рассчитали]. Можете ли вы помочь мне найти мою ошибку интерпретации? [Также, чтобы быть более понятным, как насчет обратного: если кто-то хочет реализовать ((A OR B) AND (C OR D)), что именно нужно кодировать в файле .htaccess?]
Чак Колларс
3
+1 за «демонстрацию большого преимущества открытого исходного кода» :) - @ChuckKollars логика выдала бы не (((A или B) и C) или D), а (A или B) и (C или D). на каждом проходе цикла он проверяет CONDFLAG_ORNEXT, который может быть установлен только при просмотре A и C. Когда он установлен, он либо пропускает следующее условие, если текущее условие выполнено, либо продолжает, не заботясь о том, что текущее условие не выполнено, потому что будущие условия могут pass, и последний из группы OR не будет иметь установленный флаг, поэтому, если все терпят неудачу, все это терпит неудачу.
tempcke
1
Я не могу понять, как это сделать ((A и B) ИЛИ (C и D)) - Я закончил с (A и (B или C) и D)
Пит
@WillshawMedia Вы можете выполнить ((A и B) OR (C и D)), разделив его на два отдельных правила: RewriteCond A RewriteCond B RewriteRule AB RewriteCond C RewriteCond D RewriteRule CD
Исаак
3

После многих усилий и для достижения общего, гибкого и более читаемого решения в моем случае я закончил тем, что сохранил результаты OR в переменных ENV и выполнил AND этих переменных.

# RESULT_ONE = A OR B
RewriteRule ^ - [E=RESULT_ONE:False]
RewriteCond ...A... [OR]
RewriteCond ...B...
RewriteRule ^ - [E=RESULT_ONE:True]

# RESULT_TWO = C OR D
RewriteRule ^ - [E=RESULT_TWO:False]
RewriteCond ...C... [OR]
RewriteCond ...D...
RewriteRule ^ - [E=RESULT_TWO:True]

# if ( RESULT_ONE AND RESULT_TWO ) then ( RewriteRule ...something... )
RewriteCond %{ENV:RESULT_ONE} =True
RewriteCond %{ENV:RESULT_TWO} =True
RewriteRule ...something...

Требования :

DrLightman
источник