经常看到论坛上有人询问,为什么伪静态失效了,为什么列表页无法实现伪静态。那么 discuz! X2 是如何实现伪静态功能的呢?
为了阐述这个问题,我们从两个角度入手:

第一,伪静态是如何作用的?

我们都知道,当我们通过地址访问一个页面的时候,访问的是服务器上的一个实体文件。例如,访问 http://www.xxx.com/index.html,访问的是网站根目录下的 index.html 文件。然而,对于论坛,如果每一个页面 (主题列表,内容页) 都是一个静态的实体文件,那文件的数量将何其之多,并且不能动态的实时的展现论坛的内容。如果仅仅使用动态访问,那么不利于增强搜索引擎的友好面,因此,我们将使用伪静态,展现形式是.html 这样的静态页面,而实际上依然是通过动态脚本来处理的。

Discuz! X2 的伪静态处理利用了服务器的 rewrite 模块,通过 rewrite 模块的配置的规则,对请求的 url 进行转换。

下面我们以 Apache 的 rewrite 模块为例,详细说明一下。

  1. 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. 规则错误了有没有?
如果规则是这样的

  1. RewriteRule ^(.*)/forum-(w+)-([0-9]+).html$ $1/forum.php?mod=forumdisplay

少了东西是不是,所以/forum-XX-XX.html 只会访问/forum.php?mod=forumdisplay,这样列表页肯定不显示了
如果规则更夸张一些

  1. 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 文件中,有这样一行代码。

  1. <!--{eval output();}-->

这段代码解析后,直接调用了 function_core.php 中的 output() 函数。
我们来看看 output 函数执行了哪些操作。

  1. if($_G['setting']['rewritestatus'] || !empty($havedomain))
  2. {
  3.         $content = ob_get_contents();
  4.         $content = output_replace($content);
  5.         ob_end_clean();
  6.         $_G['gzipcompress'] ? ob_start('ob_gzhandler') : ob_start();
  7.         echo $content;
  8. }


序执行到这里的时候,执行了一个 $content = ob_get_contents(); 的操作,
ob_get_contents() 获取当前输出缓存中的所有数据,也就是说,模板返回的页面并没有直接显示给用户,而是被 $content 变量获取到
了。然后进入 output_replace 函数执行内容替换 (各种替换,不仅仅是伪静态链接替换),最后又 echo 输出。

于是,替换工作就放在 output_replace 函数中,

  1. if(!empty($_G['setting']['output']['str']['search']))
  2. {
  3.             if(empty($_G['setting']['domain']['app']['default'])) {
  4.                 $_G['setting']['output']['str']['replace'] = str_replace('{CURHOST}', $_G['siteurl'], $_G['setting']['output']['str']['replace']);
  5.             }
  6.             $content =
    str_replace($_G['setting']['output']['str']['search'],
    $_G['setting']['output']['str']['replace'], $content);
  7. }

这一部分,替换的是当前站点的域名,将写在模板中的'{CURHOST}'占位符替换为 $_G['siteurl'] 的值。

  1. if(!empty($_G['setting']['output']['preg']['search']))
  2. {
  3.             if(empty($_G['setting']['domain']['app']['default'])) {
  4.                     $_G['setting']['output']['preg']['search'] =
    str_replace('{CURHOST}', preg_quote($_G['siteurl']),
    $_G['setting']['output']['preg']['search']);
  5.                     $_G['setting']['output']['preg']['replace']
    = str_replace('{CURHOST}', $_G['siteurl'],
    $_G['setting']['output']['preg']['replace']);
  6.             }
  7.            $content =
    preg_replace($_G['setting']['output']['preg']['search'],
    $_G['setting']['output']['preg']['replace'], $content);
  8. }     

这一个部分则是替换伪静态链接,具体的实现过程,我简单介绍下,没有兴趣的同学可以跳过。

$_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 这样的链接自然是无法被成功替换的咯

至此,伪静态是如何替换并显示的机理就算介绍完毕了。

伪静态功能的介绍就先到此,希望能对站长解决伪静态问题提供一些帮助~