一个简单的PHP Router

首先建立一个PHP文件 server.php

<?php
// filename: server.php
print_r($_SERVER);

用curl看看抓到了什么

<?php
$ch2 = curl_init();
curl_setopt($ch2, CURLOPT_URL,'http://cc.com/server.php/segement/input');
curl_setopt($ch2, CURLOPT_HEADER, true);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1);
$orders = curl_exec($ch2);
curl_close($ch2); 
echo $orders;
Array
(
    [HTTP_HOST] => cc.com
    [HTTP_ACCEPT] => */*
    [PATH] => C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;E:\TortoiseSVN\bin;D:\MacType;D:\EmEditor;
    [SystemRoot] => C:\Windows
    [COMSPEC] => C:\Windows\system32\cmd.exe
    [PATHEXT] => .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    [WINDIR] => C:\Windows
    [SERVER_SIGNATURE] => 
    [SERVER_SOFTWARE] => Apache/2.2.6 (Win32) PHP/5.2.5
    [SERVER_NAME] => cc.com
    [SERVER_ADDR] => 127.0.0.1
    [SERVER_PORT] => 80
    [REMOTE_ADDR] => 127.0.0.1
    [DOCUMENT_ROOT] => E:/Web
    [SERVER_ADMIN] => admin@localhost.locahost
    [SCRIPT_FILENAME] => E:/Web/server.php
    [REMOTE_PORT] => 56128
    [GATEWAY_INTERFACE] => CGI/1.1
    [SERVER_PROTOCOL] => HTTP/1.1
    [REQUEST_METHOD] => GET
    [QUERY_STRING] => 
    [REQUEST_URI] => /server.php/segement/input
    [SCRIPT_NAME] => /server.php
    [PATH_INFO] => /segement/input
    [PATH_TRANSLATED] => E:\Web\segement\input
    [PHP_SELF] => /server.php/segement/input
    [REQUEST_TIME] => 1350121675
    [argv] => Array
        (
        )

    [argc] => 0
)

根据 $_SERVER 的内容中我们挑出能用的部份,REQUEST_METHOD 就是HTTP请求时的方法而 PATH_INFO 、PHP_SELF 及 REQUEST_URI 则可以用来当作比对用的URL。PATH_INFO的内容 /segement/input 完全符合需求,另外的 PHP_SELF 及 REQUEST_URI 要做额外的处理才能使用。 接下来就开始了,做一个Router我们需要上面提到的HTTP Method、URL以及Route Rule及Callback,原理很简单当Method及URL与Route rule互相匹配的时候就会去让回调执行相对应的动作。首先新增一个名为router的class把一些基本的架构做出来

<?php
// filename: router.php
class router {
    public function __construct() {
        $this->method = $_SERVER['REQUEST_METHOD'];
        $this->url = $_SERVER['PATH_INFO'];
    }
    public function add($route, $callback) {
        // do something
    }
    public function run() {
        // compare
    }
}

以上的class提供一些基本的功能,当类被初始化时自动取得Method及URL,再用add加入Route Rule及Callback,接来下就可以用run让Router 启动了 那么比对怎样做,直接用 == 字串比对就好  比较好的做法是使用正规表达式去比对字串。 稍微修改一下route.php

<?php
// filename: router.php
class router {
    public function __construct() {
        $this->method = $_SERVER['REQUEST_METHOD'];
        $this->url = $_SERVER['PATH_INFO'];
    }
    public function add($route, $callback) {
        $this->rules[] = array(
            'route' => $route,
            'callback' => $callback
        );
    }
    public function run() {
        foreach($this->rules as $rule) {
            if($rule['route'] == $this->url) {
                $rule['callback']();
                break;
            }
        }
    }
}

为了让例子简单一点这边还是直接使用字串比对,首先修改了add function让它可以接收外在传入,Route Rule及Callback而run function则是将路由规则读出,去和URL做比对。 以上都设定好后,我们修改原先用来测试的server.php,将router生成出来、并加入一条规则接

执行它

<?php
// filename: server.php
require_once 'router.php';
$Router = new router();
$Router->add('/test/index.html', function() {
    echo 'Success';
});
$Router->run();

当我们用curl去请求 cc.com/server.php/test/index.html 时因为规则Match会回传 Success 而要求其他路径因为规则不符则不会有任何反应。 那么接下来让Route Rule支持Default Route,就像是Linux的Route一样当不清楚该将资料送给哪个Gateway的时候一律送给Default Gateway,所以这边也一样。

<?php
// filename: router.php
class router {
    public function __construct() {
        $this->method = $_SERVER['REQUEST_METHOD'];
        $this->url = $_SERVER['PATH_INFO'];
    }

    public function add($route, $callback) {
        $this->rules[$route] = $callback;
    }

    public function run() {
        $is_match = FALSE;

        foreach($this->rules as $route => $callback) {
            if('default' === $route)
                continue;

            if($route == $this->url) {
                $is_match = TRUE;
                $callback();
                break;
            }

            if(!$is_match)
                $this->rules['default']();
        }
    }
}

 

<?php
// filename: server.php
require_once 'router.php';

$Router = new router();

$Router->add('/test/index.html', function() {
    echo 'Success';
});
$Router->add('default', function() {
    echo 'Default';
});

$Router->run();

基本上到这边一个简单的Router Prototype就完成了。那么就在加强一下,使用正规表达式去比对字串。我们稍微对run function做调整并修改server.php传入的参数。

<?php
// filename: server.php
require_once 'router.php';

$Router = new router();

$Router->add('/\/(\w+)\/(\w+)/', function($result) {
    print_r($result);
});
$Router->add('default', function() {
    echo 'Default';
});

$Router->run();

  上面的范例可以Match所有 /*/*/ 的字串,由于已经使用正规表达式去比对字串,用来切字串也是理所当然,所以就将切出来的字串顺便塞到Callback里方便使用。