ScarShow

< IS >

實作一個小型 Router use PHP (2)

2012-04-23  /  IT  /  PHP

接續前一篇,我們開始實做比對的機制,所以我們稍微修改一下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則是將rule讀出,去和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去要求localhost/server.php/test/index.html時因為規則Match會回傳 Success 而要求其他路徑因為規則不符則不會有任何反應。

那麼接下來讓Route Rule支援Default Route,就像是Linux的Route一樣當不清楚該將資料送給哪個Gateway的時候一律送給Default Gateway,所以這邊也一樣。

Router.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[$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']();
        }
    }
}

Server.php

<?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傳入的參數。

Router.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[$route] = $callback;
    }

    public function run() {
        $is_match = FALSE;

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

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

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

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裡方便使用。

至於比對Method的部份我就不放範例了,這部份就讓大家去實做吧,要作到怎麼樣的程度就看專案的情況,但如果你要又好又強大當然已經有大神刻好放在那邊了