使用mod_rewrite进行递归路径组件集合

时间:2012-06-14 21:28:39

标签: regex .htaccess mod-rewrite recursion url-rewriting

我正在尝试做很多次以前做过很多次的事,但我似乎无法让这个工作。我已经尝试了将近2天了,我一直在网上搜索一个有效的例子,发现了许多非常相似的SO问题,但没有一个问题对我有用 - 大多数是按照关键/价值方法进行的,我只想要一系列价值观。


我想要的是什么:

我希望能够使用搜索引擎友好的网址。由于当前网站的工作方式的性质,我想转换此请求URI:

/this/is/a/random/path

...为:

/index.php?p[]=this&p[]=is&p[]=a&p[]=random&p[]=path

因此,当它到达PHP时,它将作为$_GET['p']中的索引数组提供。我也希望它能够容忍一个尾随斜杠,所以我会得到相同的结果:

/this/is/a/random/path/

我是如何尝试的:

我对正则表达式并非坏了,我对mod_rewrite的运作方式有一个合理的理解,但我认为我已经消失了,到目前为止,我已经无法再看到错误的道路了。

以下是我目前的情况:

# Turn mod_rewrite on
RewriteEngine On

# Allow direct loading of files in the /static directory
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^/?static/(.+)$ - [L]

# Recursively capture all path components
RewriteCond %{REQUEST_URI} !^/?(?:index\.php)?$
RewriteRule ^/?([^/]+)(?:/(.+)$|/?$) $2?p[]=$1 [QSA,L]

# Send request to controller
RewriteRule ^.*$ index.php [QSA]

出了什么问题:

第一个RewriteCond / RewriteRule对很好地工作 - 如果我请求/static目录中存在的文件,请求保持原样并且文件被提供。如果该文件不存在,则会进入第二组规则,这样我就可以显示一个基于PHP的性感错误页面。

问题在于第二个RewriteCond / RewriteRule对,可能还有第三个RewriteRule。该条件应该在那里确保最后的迭代不会导致脚本名称被添加到数组 - 这似乎工作。以下是我认为第二个RewriteRule正在做的事情,我怀疑我错过了一些显而易见的事情:

           ^/? # Start of string with optional leading slash
       ([^/]+) # Capture all characters up to next slash
(?:/(.+)$|/?$) # Either grab all characters after the next slash or match the end

     $2?p[]=$1 # Push captured path component onto the array and shift URI down
       [QSA,L] # Merge previous query string, continue to next iteration

这是90%的工作。我遇到的问题:

  • 阵列组件的顺序相反。我明白为什么会这样,我意识到这可能是不可避免的,使用array_reverse()在PHP中很容易解决。我只提到它,以防任何人都能想到我不能的mod_rewrite解决方案。
  • 最后两个位置的重复路径组件导致其失败。例如,如果我请求/home/home/some/path/path,我会得到一个标准的Apache 404,说明找不到最后两个路径组件(上面两个示例中为/home/home/path/path) 。 然而如果我将另一个路径组件添加到最后,例如/home/home/something,那么它会再次起作用。我无法解决造成这种情况的原因。

任何人都可以解释为什么会发生这种情况,或建议更好的方法来做到这一点吗?

1 个答案:

答案 0 :(得分:1)

这不会更容易:

 RewriteCond ${REQUEST_FILENAME} !-f
 RewriteCond ${REQUEST_FILENAME} !-d
 RewriteRule .* rewrite.php [L]

rewrite.php:

 <?php
 $p = array_filter(explode('/',parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)));
 // you _could_ of course do an EVIL $_GET['p'] = $p, but I prefer to leave 
 // the superglobals 'read-only'. Not touching $_GET does however mean
 // that index.php needs to be altered somewhat, allowing for a check on isset($p) 
 // and using that as input
 include 'index.php';
 ?>

在apache中重写一切都很好,但通常只是解析&amp;确定PHP本身的动作要容易得多,以后也更容易维护/修改。

问题/备注:

  

如果我通过路径请求它们,你的htaccess将允许直接访问文件,我不想这样做,除非它们在/ static

中,否则我不想这样做

此时不允许更多或更少的访问权限。只有你的index.php&amp; rewrite.php可以访问其他任何东西都可以在文档根目录之外,其中文件应该驻留在你不希望允许访问的位置。除非您使用此输入盲目地在index.php中包含文件....我不会错过有关现有文件请求的部分,该文件也应该通过管道传送到index.php。在这种情况下,这样的事情会做:

RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^/?static/(.+)$ - [L]

RewriteCond ${REQUEST_URI} !^/?(index\.php)?$
RewriteRule .* rewrite.php [L,QSA]
  

顺便说一下,没有回调的array_filter()是什么?到目前为止,我可以看到所有它将做的是剥离空组件和0组件,我可能想要允许0。

这是为了防止像/foo//bar这样的错误网址造成空洞的“鬼魂”(请注意双//

  

preg_split('#/ +#',$ str,-1,PREG_SPLIT_NO_EMPTY);更好吗?

如果你想允许0 /其他被array_filter过滤的东西,那么是的,那个解决方案会更好。