由於工做需要,查了一下Apache的文檔,對當中反向引用和條件的運行作了理解和實驗,如下是對Apache 2.2文檔的摘錄,並在上面作了實驗的樣例說明,但願能給一些需要深刻理解的一些幫助。php
其它部分就不作不少其它的說明,看文檔就能夠。express
Apache的mod_rewrite是提供了強大URL操做的殺手級模塊,可以實現差點兒所有你夢想的URL操做,其代價是你必須接受其複雜性,因爲mod_rewrite對於剛開始學習的人的主要障礙就是不easy理解和運用,即便是Apache專家有時也會發掘出mod_rewrite的新用途。換句話說:你或者是打退堂鼓永再也不用,或者是喜歡它並一輩子受用。眼下存在這樣一種傾向:不少剛開始學習的人僅僅是把URL重寫規則當着是會變戲法的魔咒,而並未在使用中真正理解這些規則的含義。瀏覽器
本篇文檔試圖給出充分的背景知識,以便於剛開始學習的人隨後的理解,而不是盲目的複製和粘貼。dom
mod_rewrite使用的是Perl兼容的正則表達式語法。本文不打算具體解說正則表達式語法,你可以到PCRE man page, Perl regular expression man page, Mastering Regular Expressions, by Jeffrey Friedl得到這些內容。學習
RewriteRule指令的說明部分有一個簡單的正則表達式語法簡單介紹,可以去參考一下。spa
另外需要說明的是可以在表達式的最前面加上一個感嘆號('!')表示不匹配,只是這樣的使用方法並不符合正則表達式語法。設計
正則表達式的反向引用能力日誌
這是很是重要的一點:一旦在Pattern或者CondPattern中使用了圓括號,就會創建內部的反向引用,可以使用$N和%N來調用(見下述),並且在Substitution和TestString中都有效。圖-2說明了反向引用被轉換和展開的位置。orm
圖-2: The back-reference flow through a rule.server
內部處理
此模塊的內部處理極爲複雜,但是爲了使通常用戶避免犯低級錯誤,也讓管理員能充分利用其功能,在此仍然作一下說明。
API階段
首先,你必須瞭解Apache是分若干階段來處理HTTP請求的。Apache API對每個階段都提供了一個hook程序。mod_rewrite使用兩個hook程序:其一,從URL到文件名稱的轉換hook(用在讀取HTTP請求以後、受權開始以前); 其二,修正hook(用在受權階段和讀取文件夾級配置(.htaccess)以後、內容處理器激活以前)。
因此,Apache收到一個請求並且肯定了響應主機(或虛擬主機)以後,重寫引擎即開始處理server級配置中的所有mod_rewrite指令(此時處於從URL到文件名稱轉換的階段),此階段完畢後,終於的數據文件夾便肯定了。接下來進入修正程序段並觸發文件夾級配置中的mod_rewrite指令。這兩個階段並不是涇渭分明的,但都實施了把URL重寫成新的URL或者文件名稱。儘管API最初不是爲此目的而設計的,但是現在它已經成爲了API的一種用途。記住下面兩點,會有助於更好地理解:
儘管mod_rewrite可以將URL重寫爲新的URL或文件名稱,甚至將文件名稱重寫爲新的文件名稱,但是以前的API僅僅提供從URL到文件名稱的hook。在Apache 2.0中,添加了兩個丟失的hook以使得處理過程更加清晰。只是這樣作並無給用戶帶來麻煩,用戶僅僅需記住這樣一個事實:藉助從URL到文件名稱的hook比最初API設計的目標功能更強大。
使人難以置信的是,mod_rewrite還提供了文件夾級的URL操做(.htaccess文件),而這些文件必須在將URL轉換成文件名稱以後纔會被處理(這是必須的,因爲.htaccess存在於文件系統中)。換句話說,依據API階段,這時再處理不論什麼URL操做已經太晚了。爲了解決這個"雞和蛋"的問題,mod_rewrite使用了一個小技巧:在進行一個文件夾級的URL/文件名稱操做時,先把文件名稱重寫回對應的URL(一般這個操做是不可行的,但是參考如下的RewriteBase指令就能明確它是怎麼實現的了),而後,對這個新的URL創建一個新的內部的子請求,再又一次開始API階段的運行。
另外,mod_rewrite盡力使這些複雜的操做對用戶透明。但仍須記住:server級的URL操做速度快而且效率高,而文件夾級的操做由於這個"雞和蛋"的問題速度較慢而且效率也低。但從還有一個側面看,這倒是mod_rewrite得覺得通常用戶提供(局部限制的)URL操做的惟一方法。
牢記這兩點!
規則集的處理
當mod_rewrite在這兩個API階段中開始運行時,它會讀取配置結構中配置好的 (或者是在服務啓動時創建的server級的,或者是在遍歷文件夾採集到的文件夾級的)規則集,而後,啓動URL重寫引擎來處理(帶有一個或多個條件的)規則集。不論是server級的仍是文件夾級的規則集,都是由同一個URL重寫引擎處理,僅僅是終於結果處理不一樣而已。
規則集中規則的順序是很是重要的,因爲重寫引擎是按一種特殊的順序處理的:逐個遍歷每個規則(RewriteRule指令),假設出現一個匹配條件的規則,則可能回頭遍歷已有的規則條件(RewriteCond指令)。因爲歷史的緣由,條件規則是前置的,因此控制流程略顯冗長,細節見圖-1。
圖-1:重寫規則集中的控制流
可見,URL首先與每個規則的Pattern匹配,假設匹配失敗,mod_rewrite將立刻終止此規則的處理,繼而處理下一個規則。假設匹配成功,mod_rewrite將尋找相應的規則條件,假設一個條件都沒有,則簡單地用Substitution構造的新值來替換URL,而後繼續處理其它規則;但是假設條件存在,則開始一個內部循環按其列出的順序逐個處理。對規則條件的處理有所不一樣:URL並不與模式進行匹配,而是首先經過擴展變量、反向引用、查找映射表等步驟創建一個TestString字符串,而後用它來與CondPattern匹配。假設匹配失敗,則整個條件集和相應的規則失敗;假設匹配成功,則運行下一個規則直到所有條件運行完成。假設所有條件得以匹配,則以Substitution替換URL,並且繼續處理。
好比如下的配置
RewriteEngine on
#設置Rewrite日誌
RewriteLogLevel 9
RewriteLog "C:/Apache/logs/rewrite.log"
RewriteCond $1 ^(.*)/.(.*) [NC] (1)
RewriteCond %{HTTP_HOST} !^(.*)/.(.*)/.(.*) (2)
RewriteRule ^/(.*) /$1?ID=%1 (3)
#RewriteCond ............... (4)
#RewriteRule ^/(.*) ............ (5)
若是在瀏覽器中輸入 http://www.domain.com/index.php,server端將會進行例如如下操做
規則集合的運行:逐個遍歷每個規則(RewriteRule指令),發現第(3)行,則用輸入 /index.php做爲RewriteRule的Pattern進行匹配,假設匹配不成功,則會繼續往下處理其它的RewriteRule。假設成功,則會去尋找本條RewriteRule前面的所有RewriteCond,而後從第找到的第一個RewriteCond開始,創建TestString字符串,而後用它來與CondPattern匹配。假設匹配失敗,則整個條件集和相應的規則失敗;假設匹配成功,則運行下一個規則直到所有條件運行完成。假設所有條件得以匹配,則以Substitution替換URL,並且繼續處理。
上面的樣例,假設運行到(5),則僅僅會有(4)這個RewriteCond被處理,(3)前面的(1)(2)不會再次被處理,假設開始運行的時候(3)RewriteRule沒有匹配,則(1)(2)就不會有被運行的機會。
反向引用:
從前面的樣例和上面的圖可以看出,
Substitution可以反向引用當前Pattern中的匹配的分組成分(圓括號!)。
引用方法是: $N (0 <= N <= 9)
也可以反向應用RewriteCond條件中最後符合的條件中的分組成分(圓括號!)。記住是最後一個匹配成功的。
引用方法是: %N (1 <= N <= 9)
相同,TestString中可以包括反向引用當前匹配的RewriteRule的Pattern部分的匹配的分組成分(圓括號!)。
引用方法是: $N (0 <= N <= 9)
從處理圖看,僅僅能對該RewriteCond前面的被成功處理過的CondPattern分組成分(圓括號!)進行引用,即條件(2)可以對(1)進行反向引用。