用PHP重写URL


139

我有一个网址,看起来像:

url.com/picture.php?id=51

我将如何将该URL转换为:

picture.php/Some-text-goes-here/51

我认为WordPress也是一样。

如何使用PHP创建友好的URL?


1
您需要对Apache文件的配置进行大量调整...给出您实际需要的示例吗?您如何插入此文本?它是预定义的还是动态创建的?提供尽可能多的信息,以得到正确的答案...而且,URL可能更具描述性:)
user123_456 2013年

1
为什么选择PHP?Apache mod_rewrite就是您所需要的,SO上对此有很多疑问。而且,如果您“认为” wordpress的功能相同,为什么不简单地查找它呢?;)
nietonfir 2013年


你为什么做这个?目标是重写URL,如果是的话,请使用.htaccess或类似名称。如果目标只是更改字符串,则$ _GET仅获取querystring?
2013年

文本将是图像的标题,然后是斜杠后的ID :)我将不得不研究apache的mod_rewrite提供的内容。希望它不太困难:D
Jazerix

Answers:


194

您基本上可以通过以下两种方式执行此操作:

.htaccess路由与mod_rewrite

.htaccess在您的根文件夹中添加一个名为的文件,并添加如下内容:

RewriteEngine on
RewriteRule ^/?Some-text-goes-here/([0-9]+)$ /picture.php?id=$1

这将告诉Apache为该文件夹启用mod_rewrite,如果询问它与正则表达式匹配的URL,它将在内部将其重写为所需的内容,而最终用户看不到它。简单但不灵活,因此,如果您需要更多功能:

PHP路线

将以下内容放在您的.htaccess文件中:(请注意斜杠)

FallbackResource /index.php

这将告诉它为您index.php通常在您的站点中找不到的所有文件运行您的文件。然后,您可以在其中例如:

$path = ltrim($_SERVER['REQUEST_URI'], '/');    // Trim leading slash(es)
$elements = explode('/', $path);                // Split path on slashes
if(empty($elements[0])) {                       // No path elements means home
    ShowHomepage();
} else switch(array_shift($elements))             // Pop off first item and switch
{
    case 'Some-text-goes-here':
        ShowPicture($elements); // passes rest of parameters to internal function
        break;
    case 'more':
        ...
    default:
        header('HTTP/1.1 404 Not Found');
        Show404Error();
}

这是大型站点和CMS系统执行此操作的方法,因为它在解析URL,配置和数据库相关的URL等方面提供了更大的灵活性。对于零星使用,硬编码的重写规则.htaccess可以很好地工作。


您可以使用正则表达式完全忽略该字符串,而不是对其进行硬编码。换句话说,唯一重要的是ID部分。去picture.php/invalid-text/51也将重定向到相同的位置。您还可以添加检查以查看字符串是否正确,如果不正确,请再次重定向到正确的位置。这就是我在使用htaccess的网站上做到这一点的方式。
Mike

7
方便的小网站,但如果你要解析真正实用/blog/25,以及/picture/51/download/684。此外,它被认为是非常糟糕的做法(让你谷歌PR处罚!)如果不是全部随机生成的URL正确返回404
尼尔斯Keurentjes

5
至少在我的系统上是FallbackResource /index.php(请注意斜线)
杰克·詹姆斯

4
@olli:不良做法评论专门指“不为不存在的URL返回404”,这可以通过答案本身的解决方案来解决。至于第一个问题- FallbackResource仅启动文件系统上实际不存在的文件,因此回退。因此,如果您有一个文件/static/styles.css并由于http://mydomain.tld/static/styles.css从未执行过代码而将其引用,则可以使其按预期和透明地工作。
Niels Keurentjes 2014年

您应该if($elements[0] === NULL)改用它,因为$elements即使它为空,它仍将返回一个计数1。
fireinspace 2016年

57

如果只想更改路由,picture.php则添加重写规则.htaccess将满足您的需求,但是,如果您希望像Wordpress中那样重写URL,则可以使用PHP。这是一个简单的例子。

资料夹结构

根文件夹(.htaccess和)中需要两个文件,index.php最好将其余.php文件放在单独的文件夹中,例如inc/

root/
  inc/
  .htaccess
  index.php

.htaccess

RewriteEngine On
RewriteRule ^inc/.*$ index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]

该文件有四个指令:

  1. RewriteEngine -启用重写引擎
  2. RewriteRule-拒绝访问文件inc/夹中的所有文件,将对该文件夹的任何调用重定向到index.php
  3. RewriteCond -允许直接访问所有其他文件(例如图像,CSS或脚本)
  4. RewriteRule -将其他任何内容重定向到 index.php

index.php

因为现在所有内容都重定向到index.php,所以将确定url是否正确,是否存在所有参数以及参数的类型是否正确。

要测试url,我们需要有一组规则,而最好的工具是正则表达式。通过使用正则表达式,我们将一击杀死两只苍蝇。网址必须通过对允许的字符进行测试的所有必需参数,才能通过此测试。以下是一些规则示例。

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

接下来是准备请求uri。

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

现在我们有了请求uri,最后一步是在正则表达式规则上测试uri。

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
    }
}

由于我们在正则表达式中使用命名子模式,因此成功匹配将填充$params数组,几乎与PHP填充$_GET数组相同。但是,使用动态URL时,$_GET将在不检查参数的情况下填充数组。

    / picture / some + text / 51

    数组
    (
        [0] => / picture / some text / 51
        [文字] =>一些文字
        [1] =>一些文字
        [id] => 51
        [2] => 51
    )

    picture.php?text = some + text&id = 51

    数组
    (
        [文字] =>一些文字
        [id] => 51
    )

这几行代码和对正则表达式的基本了解足以开始构建可靠的路由系统。

完整资料

define( 'INCLUDE_DIR', dirname( __FILE__ ) . '/inc/' );

$rules = array( 
    'picture'   => "/picture/(?'text'[^/]+)/(?'id'\d+)",    // '/picture/some-text/51'
    'album'     => "/album/(?'album'[\w\-]+)",              // '/album/album-slug'
    'category'  => "/category/(?'category'[\w\-]+)",        // '/category/category-slug'
    'page'      => "/page/(?'page'about|contact)",          // '/page/about', '/page/contact'
    'post'      => "/(?'post'[\w\-]+)",                     // '/post-slug'
    'home'      => "/"                                      // '/'
);

$uri = rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' );
$uri = '/' . trim( str_replace( $uri, '', $_SERVER['REQUEST_URI'] ), '/' );
$uri = urldecode( $uri );

foreach ( $rules as $action => $rule ) {
    if ( preg_match( '~^'.$rule.'$~i', $uri, $params ) ) {
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         */
        include( INCLUDE_DIR . $action . '.php' );

        // exit to avoid the 404 message 
        exit();
    }
}

// nothing is found so handle the 404 error
include( INCLUDE_DIR . '404.php' );

2
您如何读取参数?它不适用于$ post_id = htmlentities($ _ GET ['post']);
andrebruton

@Danijel我可以拥有完整的源代码吗?我尝试了上面的代码,但是只输出了文本,CSS没有效果。谢谢。
4

6

这是一个.htaccess文件,几乎所有内容都转发到index.php

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_URI} !-l
RewriteCond %{REQUEST_FILENAME} !\.(ico|css|png|jpg|gif|js)$ [NC]
# otherwise forward it to index.php
RewriteRule . index.php

然后由您来解析$ _SERVER [“ REQUEST_URI”]并路由到picture.php或任何其他内容


8
Apache FallbackResource早在几个主要版本中就引入了该指令,由于它不需要启动整个重写引擎,因此现在是以较低的性能成本实现此行为的首选方法。文档在这里。您的规则也有缺陷,因为您没有引用目录(!-d)并且所有扩展名过滤器都已过时- -f应该已经捕获了它们。
Niels Keurentjes


2

尽管已经回答了,并且作者的意图是创建一个前端控制器类型的应用程序,但是我正在针对所问的问题发布文字规则。如果有人有同样的问题。

RewriteEngine On
RewriteRule ^([^/]+)/([^/]+)/([\d]+)$ $1?id=$3 [L]

以上应该适用于url picture.php/Some-text-goes-here/51。而不使用index.php作为重定向应用。


我需要知道我们必须用href写什么URL?因为我收到404
试题库
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.