這是技術外加嘴砲文。
專案就是這樣,從許多大中小型的 Framework 挑一個適合的來用,萬一沒有中意的,那只好-複製貼上-自己刻一個比較快。當然,不是什麼時候都可以重造輪子的,案子很急很趕-屎都在門邊-的時候,挑幾個小型的 Framework 快速上手應該是比較好的解法。
那我寫這個幹麼(被揍飛!
需求認知
快速開發,又得要考慮多人同時進行,版本控制就很重要,git 當然是不二選擇。除了 git 我想不到其他的辦法,沒辦法,誰叫 git 這麼好用呢。版本控制確定了,那麼就得決定專案需求的方向。各大類型的 Framework 總是免不了一大堆的設定、Controllers、Modules、Views、Plugins 等等,當我不需要得時候,是不是能有一個檔案搞定的解法呢?
又,資料庫怎麼辦?在 gitHub 上面不乏有許多小型的資料庫工具可用,像是以 @pct 的 4money 專案所使用的 Idiorm 就是一個不錯的工具。當然,如果你想自己用 PDO 來刻也是可以的。
那,Views 怎麼辦呢?用 Smarty 嗎?還是?
確認需求
所以,我決定這麼做:
- 自己刻一個簡易的 Bootstrap,然後可以使用 Controllers, Plugins, Modules 等等。
- 資料庫使用 idiorm。
自己刻,比較快?
真的嗎?
首先,我知道 RESTful 的 API 需要回傳 HTTP 狀態碼,然後要將 body 填入回傳的資訊。我們有兩種方法,一種是 JSON,另一種是 XML,所以我們就會有兩種回傳資訊的功能。
HTTP 狀態碼則可以用 header() 來送,簡單明瞭(喂
<?php
header('HTTP/1.0 400 BAD REQUEST');
?>
那麼,Bootstrap 還是沒有解決啊?
<?php
class Controller {
public function __construct() {
}
}
class Container extends Controller {
public function __construct() {
}
public function startup() {
// 開始跑
}
}
$container = new Container();
$container->startup();
?>
那我們的 Controllers 呢?舉一個簡單的例子,我們開一個目錄叫做 controllers,然後裡面放了個 Api.php 這樣。
<?php
class Api extends Controller {
public function __construct() {
}
public function startup() {
// 開始跑
}
}
?>
然後訪問 index.php 的時候,發現 Api.php 並不會動,那一定是有什麼-奇怪的東西混進來了-寫錯了。所以我們再來看一下 index.php 是不是有漏掉的地方。
<?php
class Controller {
public function __construct() {
}
}
class Container extends Controller {
private $controller;
public function __construct() {
}
public function startup() {
// 開始跑
require_once 'controllers/Api.php';
$this->controller = new Api;
$this->controller->startup();
}
}
$container = new Container();
$container->startup();
?>
疑?那最近每一套 Framework 都很夯的 Routing 功能,或是去除掉 index.php 的動作要怎麼做呢?當然,如果你有用過 Rewrite Engine 的話,這個問題應該很快就能夠解決。要怎麼把網址拆開來,依照 Controller/Method/Value1/Value2 來傳遞到你的 Controller 裡面,這就是請自己努力的地方了。而其實,你也可以參考各大 Framework 的作法,也是可以學到一點東西的。
所以,我把網址所傳入的東西依照 Controller/Method/Value1/Value2 拆開來了,那麼,我們的 Container 就可以這樣改寫:
<?php
class Container extends Controller {
private $controller;
private $segments;
public function __construct() {
}
public function startup() {
// 開始跑
// 做了一狗票的事情,把網址轉成下列格式,存入 segments 中。
// Controller/Method/Value1/Value2
// 所以我們就把 Controller 提出來
$controllerName = ucfirst(strtolower(array_shift($this->segments)));
$controllerPath = dirname(__FILE__) . DIRECTORY_SEPArATOR . 'controllers'. DIRECTORY_SEPARATOR . $controllerName . '.php';
if (file_exists($controllerPath)) {
require_once $controllerPath;
$this->controller = new $controllerName;
$this->controller->startup();
} else {
die('Controller does not exists.');
}
}
}
?>
方法呼叫也是一樣,繼續改寫 Container 的部份:
<?php
if (file_exists($controllerPath)) {
require_once $controllerPath;
$this->controller = new $controllerName;
$this->controller->startup();
$methodName = ucfirst(strtolower(array_shift($this->segments)));
if (method_exists($this->controller, $methodName)) {
$this->controller->$methodName();
} else {
die('Controller's method does not exists.');
}
} else {
die('Controller does not exists.');
}
?>
傳值呢?我們有好多好多的值要傳,怎麼傳?在 PHP 中有一個方法,叫做 call_user_func_array
,使用的方式也很簡單,我們看 code 比較快:
<?php
if (method_exists($this->controller, $methodName)) {
// 原本的呼叫方式我們不要用了
// $this->controller->$methodName();
call_user_func_array( array(&$this->controller, $methodName), $this->segments );
} else {
die('Controller's method does not exists.');
}
?>
那,如果我要回傳呢?我們在這裡用 JSON 回傳的方式來舉例說明。我們寫一個靜態方法在 Controller 裡面,用靜態的方式來呼叫就好了。
<?php
class Controller {
public function __construct() {
}
public static function responseJSON($data = null) {
if ($data !== null) {
if (!is_array($data)) {
$data = array((string) $data);
}
if ($data = json_encode($data)) {
header("pragma: no-cache");
header("cache-control: no-store, no-cache, max-age=0, must-revalidate");
header("content-type: text/x-json; charset=utf-8");
header("X-JSON: ". $data);
echo $data;
exit;
} else {
die('JSON object invalid.');
}
} else {
die('JSON object invalid.');
}
}
}
// ... 後面省略一萬行
?>
你可以這麼做:
<?php
class Api extends Controller {
public function __construct() {
}
public function startup() {
// 開始跑
}
public function ThereIsTheTest() {
// 這裡省略一萬行
$data = array('value' => 'There is the test');
Controller::responseJSON($data);
}
}
?>
是的,這樣一個簡易的 Bootstrap 就完成了。
錯誤控制
上面看到不少 die(),寫太多的話你以後要除錯真的會 DIE!不過這個下次再談(疑)。
資料控制
idiorm 我們下次見(喂喂)。
小結
以入門者來說,要寫一個簡易的控制器並不是難事,你看像我這種弱雞都寫得出類似 RESTful API 的東西了,可見這並非太過於困難。-只要有心,人人都可以自己刻-。程式碼的部份無法交代太多,不過,已經有一個專案是以這種類似模式在開發,然後最近要正式上線測試了。
所以說,在程式上還是有很多眉眉角角是必須要去注意的。就像是你永遠沒辦法預防使用者在 ajax 的時候,按下 ESC 或是 F5 鍵一樣(這在瀏覽器動作上,是有可能中斷 ajax 傳輸的操作)。
這是一個很入門的輪子,就像是 @pct 說得,如果沒有辦法改變世界,那最起碼改變自己的人生。