bagaimana cara menggunakan "AND", "OR" untuk RewriteCond di Apache?

115

Apakah ini cara menggunakan AND, OR untuk RewriteConddi Apache?

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

menjadi if ( (A or B) and (C or D) ) rewrite_it.

Jadi sepertinya "ATAU" lebih diutamakan daripada "DAN"? Apakah ada cara untuk mengetahui dengan mudah, seperti dalam (A or B) and (C or D)sintaks?

nonopolaritas
sumber
1
Ya benar. [OR] lebih diutamakan daripada "DAN" (implisit). Kondisi gabungan memang ((A atau B) dan (C atau D)).
Lakukan
2
Anda mungkin menemukan detail di ckollars.org/apache-rewrite-htaccess.html#precedence berguna.
Chuck Kollars

Jawaban:

118

Ini adalah pertanyaan yang menarik dan karena tidak dijelaskan secara eksplisit dalam dokumentasi, saya akan menjawabnya dengan menelusuri kode sumber mod_rewrite ; mendemonstrasikan keuntungan besar dari open-source .

Di bagian atas, Anda akan segera melihat definisi yang digunakan untuk menamai bendera ini :

#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

dan menelusuri CONDFLAG_ORNEXT mengonfirmasi bahwa itu digunakan berdasarkan keberadaan panji [OR] :

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

Kemunculan flag berikutnya adalah implementasi aktual di mana Anda akan menemukan loop yang melewati semua RewriteConditions yang dimiliki RewriteRule, dan yang pada dasarnya dilakukannya adalah (dilucuti, komentar ditambahkan untuk kejelasan):

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

Anda harus bisa menafsirkan ini; itu berarti OR memiliki prioritas yang lebih tinggi, dan teladan Anda memang mengarah ke if ( (A OR B) AND (C OR D) ). Jika Anda ingin, misalnya, memiliki Ketentuan ini:

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

itu akan diartikan sebagai if ( (A OR B OR C) and D ).

Sjon
sumber
1
Tampak bagi saya bahwa eksekusi kode sumber pada contoh .htaccess yang diberikan menghasilkan ((A OR B) dan C) atau D) [yaitu bukan apa yang diharapkan OP atau apa yang Anda hitung pada awalnya]. Dapatkah Anda membantu saya menemukan kesalahan interpretasi saya? [Juga agar lebih jelas, bagaimana dengan kebalikannya: jika seseorang ingin mengimplementasikan ((A OR B) AND (C OR D)), apa sebenarnya yang harus satu kode dalam file .htaccess?]
Chuck Kollars
3
+1 untuk 'mendemonstrasikan keuntungan besar dari open-source' :) - @ChuckKollars logika tidak akan menghasilkan (((A atau B) dan C) atau D) tetapi (A atau B) dan (C atau D). pada setiap lintasan meskipun loop memeriksa CONDFLAG_ORNEXT yang hanya akan disetel ketika melihat A dan C. Ketika disetel, ia akan melewatkan kondisi berikutnya jika kondisi saat ini berlalu atau terus tidak peduli bahwa kondisi saat ini gagal karena kondisi masa depan mungkin lulus dan yang terakhir dari grup OR tidak akan memiliki flag yang disetel jadi jika semua gagal semuanya gagal.
tempcke
1
tidak bisa memikirkan bagaimana melakukan ((A dan B) ATAU (C dan D)) - Saya telah berakhir dengan (A dan (B atau C) dan D)
Pete
@WillshawMedia Anda dapat melakukan ((A dan B) OR (C dan D)) dengan membaginya menjadi dua aturan terpisah: RewriteCond A RewriteCond B RewriteRule AB RewriteCond C RewriteCond D RewriteRule CD
Isaac
3

Setelah banyak perjuangan dan untuk mencapai solusi yang umum, fleksibel dan lebih mudah dibaca, dalam kasus saya, saya akhirnya menyimpan hasil OR ke dalam variabel ENV dan melakukan DAN dari variabel tersebut.

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

Persyaratan :

DrLightman
sumber