como usar “AND”, “OR” para RewriteCond no Apache?

115

É assim que usar AND, OR RewriteCondno Apache?

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

torna-se if ( (A or B) and (C or D) ) rewrite_it.

Então, parece que "OR" tem precedência mais alta do que "AND"? Existe uma maneira de saber facilmente, como na (A or B) and (C or D)sintaxe?

não polaridade
fonte
1
Sim, correto. [OR] é uma precedência mais alta do que o (implícito) "AND". A condição combinada é de fato ((A ou B) e (C ou D)).
Doin
2
Você pode achar os detalhes úteis em ckollars.org/apache-rewrite-htaccess.html#precedence .
Chuck Kollars

Respostas:

118

Essa é uma pergunta interessante e, como não é explicada de forma muito explícita na documentação, responderei examinando o código - fonte do mod_rewrite ; demonstrando um grande benefício do código aberto .

Na seção superior, você encontrará rapidamente as definições usadas para nomear essas sinalizações :

#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

e pesquisar CONDFLAG_ORNEXT confirma que ele é usado com base na existência do sinalizador [OR] :

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

A próxima ocorrência do sinalizador é a implementação real, onde você encontrará o loop que passa por todas as RewriteConditions que um RewriteRule tem, e o que ele basicamente faz é (removido, comentários adicionados para maior clareza):

# 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;
    }
}

Você deve ser capaz de interpretar isso; significa que OR tem uma precedência mais alta, e seu exemplo realmente leva a if ( (A OR B) AND (C OR D) ). Se você tivesse, por exemplo, estas Condições:

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

seria interpretado como if ( (A OR B OR C) and D ).

Sjon
fonte
1
Parece-me que a execução do código-fonte no exemplo .htaccess dado produz ((A OR B) e C) ou D) [ou seja, nem o que o OP espera nem o que você calculou inicialmente]. Você pode me ajudar a encontrar meu erro de interpretação? [Também para ser mais explícito, que tal o inverso: se alguém deseja implementar ((A OR B) AND (C OR D)), o que exatamente se deve codificar no arquivo .htaccess?]
Chuck Kollars
3
+1 para 'demonstrar um grande benefício do código aberto' :) - @ChuckKollars a lógica não produziria (((A ou B) e C) ou D), mas (A ou B) e (C ou D). em cada passagem pelo loop, ele verifica CONDFLAG_ORNEXT que só seria definido quando se olha para A e C. Quando é definido, ele pula a próxima condição se a condição atual passou ou continua sem se importar se a condição atual falhou porque as condições futuras podem passar e o último do grupo OR não terá o sinalizador definido, portanto, se todos falharem, tudo falhará.
tempcke de
1
não consigo entender como fazer ((A e B) OU (C e D)) - Acabei com (A e (B ou C) e D)
Pete
@WillshawMedia Você pode fazer ((A e B) OU (C e D)) dividindo-o em duas regras separadas: RewriteCond A RewriteCond B RewriteRule AB RewriteCond C RewriteCond D RewriteRule CD
Isaac
3

Depois de muitas lutas e de chegar a uma solução geral, flexível e mais legível, no meu caso acabei salvando os resultados dos ORs em variáveis ENV e fazendo os ANDs dessas variáveis.

# 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...

Requisitos :

DrLightman
fonte