¿Cómo usar "Y", "O" para RewriteCond en Apache?

115

¿Es así como se usa AND, OR RewriteConden Apache?

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

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

Entonces, ¿parece que "O" tiene una precedencia mayor que "Y"? ¿Hay alguna forma de saberlo fácilmente, como en la (A or B) and (C or D)sintaxis?

no polaridad
fuente
1
Sí correcto. [OR] tiene mayor precedencia que el "Y" (implícito). La condición combinada es de hecho ((A o B) y (C o D)).
Hasta
2
Puede encontrar útiles los detalles en ckollars.org/apache-rewrite-htaccess.html#precedence .
Chuck Kollars

Respuestas:

118

Esta es una pregunta interesante y, dado que no se explica de manera muy explícita en la documentación, la responderé revisando el código fuente de mod_rewrite ; demostrando un gran beneficio del código abierto .

En la sección superior, verá rápidamente las definiciones utilizadas para nombrar estas banderas :

#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

y la búsqueda de CONDFLAG_ORNEXT confirma que se utiliza en función de la existencia de la bandera [OR] :

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

La siguiente aparición de la bandera es la implementación real donde encontrará el bucle que pasa por todas las RewriteConditions que tiene RewriteRule, y lo que básicamente hace es (despojado, comentarios agregados para mayor claridad):

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

Debería poder interpretar esto; significa que OR tiene una precedencia más alta, y su ejemplo de hecho conduce a if ( (A OR B) AND (C OR D) ). Si, por ejemplo, tuviera estas Condiciones:

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

se interpretaría como if ( (A OR B OR C) and D ).

Sjon
fuente
1
Me parece que la ejecución del código fuente en el ejemplo .htaccess dado produce ((A OR B) y C) o D) [es decir, ni lo que el OP espera ni lo que usted calculó inicialmente]. ¿Pueden ayudarme a encontrar mi error de interpretación? [También para ser más explícito, ¿qué pasa con lo contrario: si uno quiere implementar ((A OR B) Y (C OR D)), qué debería codificar exactamente en el archivo .htaccess?]
Chuck Kollars
3
+1 por 'demostrar un gran beneficio del código abierto' :) - @ChuckKollars la lógica no produciría (((A o B) y C) o D) sino (A o B) y (C o D). en cada pasada a través del bucle, comprueba CONDFLAG_ORNEXT, que solo se establecería al mirar A y C.Cuando se establece, omite la siguiente condición si la condición actual pasó o continúa sin importarle que la condición actual haya fallado porque las condiciones futuras pueden pasar y el último del grupo OR no tendrá la bandera establecida, por lo que si todos fallan, todo falla.
tempcke
1
No puedo entender cómo hacer ((A y B) O (C y D)) - Terminé con (A y (B o C) y D)
Pete
@WillshawMedia Puede hacer ((A y B) O (C y D)) dividiéndolo en dos reglas separadas: RewriteCond A RewriteCond B RewriteRule AB RewriteCond C RewriteCond D RewriteRule CD
Isaac
3

Después de muchas luchas y para lograr una solución general, flexible y más legible, en mi caso terminé guardando los resultados de OR en variables ENV y haciendo los AND de esas variables.

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