為了闡述這個問題,我們從兩個角度入手:
第一,偽靜態是如何作用的?
我們都知道,當我們通過地址訪問一個頁面的時候,訪問的是服務器上的一個實體文件。例如,訪問 http://www.xxx.com/index.html,訪問的是網站根目錄下的 index.html 文件。然而,對於論壇,如果每一個頁面 (主題列表,內容頁) 都是一個靜態的實體文件,那文件的數量將何其之多,並且不能動態的即時的展現論壇的內容。如果僅僅使用動態訪問,那麼不利於增強搜索引擎的友好面,因此,我們將使用偽靜態,展現形式是.html 這樣的靜態頁面,而實際上依然是通過動態腳本來處理的。
Discuz! X2 的偽靜態處理利用了服務器的 rewrite 模塊,通過 rewrite 模塊的配置的規則,對請求的 url 進行轉換。
下面我們以 Apache 的 rewrite 模塊為例,詳細説明一下。
- RewriteRule ^(.*)/forum-(w+)-([0-9]+).html$ $1/forum.php?mod=forumdisplay&fid=$2&page=$3&%1
這是一條 Apache 的偽靜態規則
rewrite 規則匹配的是/forum-XX-XX.html 的 url 請求,經過規則轉換後,實際請求的鏈接是/forum.php?mod=forumdisplay&fid=XX&page=XX,是不是很熟悉,這就是請求論壇主題列表的動態鏈接啦。
所以問題來了,如果偽靜態請求失效/forum-XX-XX.html,提示頁面無法顯示,有幾種錯誤的可能性呢?
1. 規則生效了有沒有?
如果服務器不支持 rewrite,如果忘了重啓服務,如果.htaccess 文件放置的位置不對,等等,都可能導致 rewrite 功能沒有運行,這樣肯定是不行的哦。
所以,先檢查下 rewrite 功能是否正常吧,如果使用空間的話,可以諮詢下空間商的説。
2. 規則錯誤了有沒有?
如果規則是這樣的
- RewriteRule ^(.*)/forum-(w+)-([0-9]+).html$ $1/forum.php?mod=forumdisplay
少了東西是不是,所以/forum-XX-XX.html 只會訪問/forum.php?mod=forumdisplay,這樣列表頁肯定不顯示了
如果規則更誇張一些
- RewriteRule ^(.*)/forum-(w+)-([0-9]+).html$ $1/forumdisplay.php&fid=$2&page=$3&%1
forumdisplay.php 這個文件都沒有,頁面當然無法顯示,這也就解釋了,過去版本 (如 X1.5 7.0) 的有些規則無法在 X2 中直接使用的原因了。
所以規則正確才是頁面能夠正常訪問的前提啊~如果使用新的規則,發現無法訪問了,首先檢查規則有沒有寫錯了。 (其實這些在論壇後台偽靜態設置的地方都可以查的到,根本不用各位站長費心的嘛)
3. 網絡正常有沒有?
如果拼命在偽靜態中找尋原因,沒留意站點已經無法正常訪問,是不是有種緣木求魚的感覺,站點無法訪問的原因請參看官方論壇相關教程。
分析到這一步,相信大家對於偽靜態如何起作用已經有一定的瞭解了,那麼我們轉向下一個問題。
第二,偽靜態如何在論壇顯示?
為什麼我的站偽靜態開啓了,但在首頁還是顯示動態鏈接呀?
那麼下面講解下,偽靜態是如何顯示出來的。
瞭解 Discuz! 的同學應該知道,在論壇模板文件中,所以的 url 鏈接都是以動態的形式返回輸出的。 (在模板中看不到靜態鏈接?是的) 按照常規,應該都顯示動態鏈接,那麼靜態鏈接又是如何實現。
在模板文件的 footer.htm 文件中,有這樣一行代碼。
- <!--{eval output();}-->
這段代碼解析後,直接調用了 function_core.php 中的 output() 函數。
我們來看看 output 函數執行了哪些操作。
- if($_G['setting']['rewritestatus'] || !empty($havedomain))
- {
- $content = ob_get_contents();
- $content = output_replace($content);
- ob_end_clean();
- $_G['gzipcompress'] ? ob_start('ob_gzhandler') : ob_start();
- echo $content;
- }
程
序執行到這裏的時候,執行了一個 $content = ob_get_contents(); 的操作,
ob_get_contents() 獲取當前輸出緩存中的所有數據,也就是説,模板返回的頁面並沒有直接顯示給用户,而是被 $content 變量獲取到
了。然後進入 output_replace 函數執行內容替換 (各種替換,不僅僅是偽靜態鏈接替換),最後又 echo 輸出。
於是,替換工作就放在 output_replace 函數中,
- if(!empty($_G['setting']['output']['str']['search']))
- {
- if(empty($_G['setting']['domain']['app']['default'])) {
- $_G['setting']['output']['str']['replace'] = str_replace('{CURHOST}', $_G['siteurl'], $_G['setting']['output']['str']['replace']);
- }
- $content =
str_replace($_G['setting']['output']['str']['search'],
$_G['setting']['output']['str']['replace'], $content); - }
這一部分,替換的是當前站點的域名,將寫在模板中的'{CURHOST}'佔位符替換為 $_G['siteurl'] 的值。
- if(!empty($_G['setting']['output']['preg']['search']))
- {
- if(empty($_G['setting']['domain']['app']['default'])) {
- $_G['setting']['output']['preg']['search'] =
str_replace('{CURHOST}', preg_quote($_G['siteurl']),
$_G['setting']['output']['preg']['search']); - $_G['setting']['output']['preg']['replace']
= str_replace('{CURHOST}', $_G['siteurl'],
$_G['setting']['output']['preg']['replace']); - }
- $content =
preg_replace($_G['setting']['output']['preg']['search'],
$_G['setting']['output']['preg']['replace'], $content); - }
這一個部分則是替換偽靜態鏈接,具體的實現過程,我簡單介紹下,沒有興趣的同學可以跳過。
$_G['setting']['output']['preg']['search'] 數組中保存的數據,類似於
/<a href="()forum.php?mod=forumdisplay&(amp;)?fid=(w+)(&page=(d+))?"([^>]*)>/e
對應的 $_G['setting']['output']['preg']['replace'] 中的一條數據為
rewriteoutput('forum_forumdisplay', 0, '1', '3', '5', '6')
這裏利用了 preg_replace 函數的一個特性,當匹配模式中帶有 e 修飾符,用 eval 進行後向引用替換,即替換變量和執行對應函數。也就是
説,preg_replace 將匹配到的子串,以參數的形式傳入 rewriteoutput 函數,rewriteoutput 函數返回的值作為替換的內
容。
(頁面上有幾個鏈接,將會執行幾次 rewriteoutput 函數,這就是開啓偽靜態略微影響性能的原因)
問題又來了,偽靜態不被替換這是為什麼呢?
原因很簡單,因為是在頁面輸出之前才做的替換,所以一切程序上做可能做的手腳都是浮雲了。真正起作用的僅僅是輸出之前的鏈接,是不是符合偽靜態的替換規則呢。類似於/forum.php?mod=viewthread&tid=XX#lastpost 這樣的鏈接自然是無法被成功替換的咯
至此,偽靜態是如何替換並顯示的機理就算介紹完畢了。
偽靜態功能的介紹就先到此,希望能對站長解決偽靜態問題提供一些幫助~