2014年2月7日 星期五

實現 PHP 下的 RESTful


上一個替客人開發的流動應用程式首次運用了 Ruby on Rails 及 RESTful 概念。由朋友把它有進公司,當時才初歲了解過是甚麼東東。新的項目剛剛展開,我希望後台繼續使用 RESTful 的設計。由於時間緊迫,未能及時學懂 Ruby on Rails,所以嘗試在 PHP 下把它實現。在網上找到 PHP 版的 RESTful 示範,簡單修改一下方便自己使用。

RESTful.inc
//========================================================================================
class RESTLoader  {
    private $control;
    private $segments;

    //----------------------------------------------------------------------------------------
    function __construct($Controller)  {
        $this->control = new $Controller;
    } 

    //----------------------------------------------------------------------------------------
    function run()  {
        if (PHP_SAPI == "cli")  {
            global $argv;
            $_SERVER["PATH_INFO"] = "/".implode("/", array_slice($argv, 1));
        }

        if (!isset($_SERVER["PATH_INFO"]) or $_SERVER["PATH_INFO"] == "/")  {
            $this->segments = false;
        }  else  {
            $this->segments = explode("/", $_SERVER["PATH_INFO"]);
        }

        if (!$this->segments)  {
            //  Without parameter
            return $this->control->index(); 
        }

        if (method_exists($this->control, $this->segments[1]))  {
            //  Request resource by traditional way
            $method = $this->segments[1];

            //  Deny directly invoke method of interface RESTMethod
            if (strpos($method, "rest") === 0)  { 
                header("HTTP/1.0 403 Forbidden");
                echo("The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated.");
                return false;
            }

            $objMethod = array($this->control, $method);
            $arguments = array_slice($this->segments, 2);

            call_user_func_array($objMethod, $arguments);
        }  else  {

            //  Request resource by RESTful way.
            $method = "rest".ucfirst(strtolower($_SERVER['REQUEST_METHOD']));
            $arguments = array_slice($this->segments, 1);
            $this->control->$method($arguments);
        }
    }
}

user.php
require("RESTLoader.inc");
require("user.inc");

//========================================================================================
header("Content-type: application/json");
header("Cache-Control: no-cache, must-revalidate");
header("Expires: Tue, 07 Nov 2000 00:00:00 GMT");

$loader = new RESTLoader("User");
$loader->run();

user.inc
//----------------------------------------------------------------------------------------
interface RESTMethod  {
    public function restGet($segments);
    public function restPost($segments);
    public function restPut($segments);
    public function restDelete($segments);
}

//========================================================================================
class User  {

    //----------------------------------------------------------------------------------------
    public function index()  {
        $result = "create|read|update|delete";
        echo(json_encode($result));
    }

    //----------------------------------------------------------------------------------------
    //  RESTful: controller/id  by POST.
    //  traditional: controller/create
    public function create()  {
        $result = "Create new user without name";
        echo(json_encode($result));
    }

    //----------------------------------------------------------------------------------------
    //  RESTful: controller/id  by GET.
    //  traditional: controller/id or controller/read/id
    public function read($id)  {
        $result = "Read user ".$id;
        echo(json_encode($result));
    }

    //----------------------------------------------------------------------------------------
    //  RESTful: controller/id  by PUT.
    //  traditional: controller/update/id  with POST data.
    public function update($id=false)  {
        if (!$id)  {return $this->create();}
        $result = "Update user ".$id;
        echo(json_encode($result));
    }

    //----------------------------------------------------------------------------------------
    // RESTful: controller/id  by DELETE.
    // traditional: controller/delete/id
    public function delete($id)  {
        $result = "Delete user ".$id;
        echo(json_encode($result));
    }

    //----------------------------------------------------------------------------------------
    //  Calling from RESTful method
    //----------------------------------------------------------------------------------------
    public function restPost($segments)  {
        return $this->create();
    }

    //----------------------------------------------------------------------------------------
    public function restGet($segments)  {
        return $this->read($segments[0]);
    }

    //----------------------------------------------------------------------------------------
    public function restPut($segments)  {
        return $this->update($segments[0]);
    }

    //----------------------------------------------------------------------------------------
    public function restDelete($segments)  {
        return $this->delete($segments[0]);
    }
}

為了讓 .php 在網址內消失,需要利用 .htaccess 達成:
##  Replace /*/* to /*.php/*
RewriteEngine On 
RewriteRule ^([a-z]*)/(.*)$ $1.php/$2

沒有留言: