最近更新: 2021-06-04

PHP框架 - CommonGateway 介紹

CommanGateway Framework 是一個極小化的 PHP Web 框架。它的主程式就只有一個檔案 - index.php 。 我原先甚至不將它稱為 framework ,而只是一個導入器。但解釋麻煩,還是按一般認知,當它是框架吧。

CommonGateway 主要設計目的是用於設計 RESTful API 或是 Single page web app 。 它按照 MVC 的設計模式,將 Web 應用服務分成三個部份,即資料模型(Model)、流程控制項(Controller)與視圖(View)。 CommonGateway 替設計人員處理控制項與視圖工作。 至於資料模型則不是 CommonGateway 的責任。資料模型由設計人員透過其偏好的資料庫框架處理。

CommonGateway 為設計人員完成下列工作:

  • 根據 URL 路徑(PATH_INFO) 選擇 Web 應用服務的控制項。 正是這「依路徑選擇目標」的行為特徵,而且又是 Common Gateway Interface (CGI) 的實作項目,故我將此項目命名為 CommonGateway 。
  • 它會將客戶端送出的文件資料,預先處理成關聯式陣列結構。 除了傳統的 Query string 與 Form data ,它也能處理 HTTP PUT Method 會送出的資料。它也支援 JSON 型態的資料。
  • 它會根據 RESTful 的原則,調用對應的控制項方法。
  • 它會根據控制項方法的回傳結果與客戶端期望的文件回應型態,處理對應的視圖樣板。
  • 它會儘量透過外部注入的方式,將其他資源放入控制項 (即 IoC 模式),減少對原有程式碼的侵入性。

CommonGateway 純粹是一個 MVC 容器,它的定位更像是一個前台載入器 (front loader),不以整合性為目標,不會整合 Log, Config, Database 等等的功能。 它是面向 PHP 熟練者的工具。如果你原本就有偏好的工具庫,你可以繼續使用,不必為了使用 CommonGateway 而另外學習一套方法。

我原先將源碼託管在 google code 。但 2015 年 3月時,google code 通知用戶將終止託管服務。故我現在轉移到 github 託管。

程式文件目錄結構

模仿 Ruby on Rails 規劃文件的目錄結構。

  • 控制器: controllers. 每一個控制器應設計為一個類別。類別名稱即控制器名稱。
  • 視圖: views. 每一個服務分配一個同名的目錄,再依動作名稱規劃視圖樣板的文件名稱。
  • 視圖輔助器: helps. 每一個服務對應一個視圖輔助器,其中定義的函數或變數,都將成為視圖內的區域性資源。

樹狀結構圖:

  • index.php
    • controllers/
      • {app_name}.php
    • views/
      • {app_name}/{action}.p{view_format}
    • helpers/
      • _global.php
      • {app_name}.php

例如:

- index.php
  - controllers/
    - book.php
  - views/
    - book/index.phtml
    - book/index.pxml
    - book/get.phtml
    - book/get.pxml
  - helpers/
    - _global.php
    - book.php

路徑選擇策略

index.php 就是 CommonGateway 本身。 它的應用服務定址方式採用 RESTful 模式,即以 URL 路徑表示服務內容。 例如有一個叫 book 的書籍服務,它的 URL 應為 http://your.host/index.php/book 。它的路徑規則是:

 index.php/{app_name}/{id or action_name}/{option_segments/...}

index.php 之後的路徑內容,會被拆成小節(segments)。 第一小節的 app_name 視為控制項名稱。 第二小節則為資源識別字或是動作識別字。 接下來的其他小節都視為要傳給動作的參數清單。

至於控制項的動作(action),則是依循下列兩個原則選用方法:

原則1: 根據 REQUEST_METHOD

首先根據 RESTful 模式,以 $_SERVER['REQUEST_METHOD'] 欄位為選用方法的目標,找尋控制項內是否有實作同名的方法。 例如 $_SERVER['REQUEST_METHOD'] 之值為 GET 時,CommonGateway 就會嘗試調用控制項的 get() - 方法名稱為小寫。 $_SERVER['REQUEST_METHOD'] 之值為 DELETE 時,CommonGateway 就會嘗試調用控制項的 delete()。

注意,當 $_SERVER['REQUEST_METHOD'] 為 GET ,但路徑沒有第二小節時,它會調用控制項的 index() 。

原則2: 根據 PATH

如果第一個原則找不到相符的控制項方法,而且路徑還有第二段名稱時, CommonGateway 會將第二段名稱視為動作識別字,嘗試調用控制項中同名的方法。 例如 URL 為 index.php/book/info ,則會調用 book 的 info()。

名稱慣例

CommonGateway 首先根據 URL 的服務名稱搜尋控制器的程式碼文件名稱。 再根據找到的程式碼文件名稱決定控制器的類別名稱以及服務的應用名稱(app_name)。 基本上,URL 的服務名稱全為小寫,若有多字組成時,以底線 _ 分隔。控制器的類別名稱,首字母大寫; 程式碼文件名稱應同 URL 的服務名稱。

CommonGateway 結合了一些常見的名稱慣例,設計者可按自己的使用習慣為文件取名。 它的名稱可能組合與優先順序如下:

第一例: URL 的服務名稱為 book 。控制器類別名稱為 Book,各文件名稱組合的採用順序如下:

    1st: controllers/book.php, views/book/, helpers/book.php.
    2nd: controllers/Book.php, views/Book/, helpers/Book.php.

第二例: URL 的服務名稱為 Book 。控制器類別名稱為 Book,各文件名稱組合的採用順序如下:

    1st: controllers/Book.php, views/Book/, helpers/Book.php.
    2nd: controllers/book.php, views/book/, helpers/book.php.

第三例: URL 的服務名稱為 book_profile 。控制器類別名稱為 Book_Profile,各文件名稱組合的採用順序如下:

    1st: controllers/book_profile.php, views/book_profile/, helpers/book_profile.php.
    2nd: controllers/Book_Profile.php, views/Book_Profile/, helpers/Book_Profile.php.
    3rd: controllers/BookProfile.php, views/BookProfile/, helpers/BookProfile.php.

第四例: URL 的服務名稱為 Book_Profile 。控制器類別名稱為 Book_Profile,各文件名稱組合的採用順序如下:

    1st: controllers/Book_Profile.php, views/Book_Profile/, helpers/Book_Profile.php.
    2nd: controllers/BookProfile.php, views/BookProfile/, helpers/BookProfile.php.

第五例: URL 的服務名稱為 bookProfile 。控制器類別名稱為 BookProfile,各文件名稱組合的採用順序如下:

    1st: controllers/bookProfile.php, views/bookProfile/, helpers/bookProfile.php.
    2nd: controllers/BookProfile.php, views/BookProfile/, helpers/BookProfile.php.
PHP 不會區分類別名稱的大小寫,所以 new Book(); 與 new book(); 的結果一樣。 故服務名稱的大小寫不會影響 CommonGateway 載入控制器類別的結果。

視圖調用策略

控制項動作的回傳值,決定視圖的處理工作。

CommonGateway 會自動根據服務名稱與 $_SERVER['HTTP_ACCEPT'] 內容,載入對應的視圖。 視圖的副檔名按 Ruby on Rails 型式,開頭為 p ,後接文件型態名稱。 例如 HTML 文件的視圖副檔名為 phtml (這正是 PHP 最早期使用的副檔名); XML 文件的視圖副檔名為 pxml。

比較特別的是 JSON 文件的視圖,其副檔名為 pjs ,不是 pjson 。 或者可以省略視圖,此時將自動調用 json_encode($model), 參考「JSON 處理之自動回傳」之說明。

CommonGateway 會先調用控制項方法,並接收方法回傳值。然後按下列規則處理回傳值,變成視圖內的 $model 變數。 回傳值處理規則請看「控制項方法回傳資料如何成為視圖的資料來源」。 簡列於下:

  • 若為 null (或無回傳值): 大部份控制項的方法不回傳內容,故這是預設行為。 此時會將控制項的公開屬性當作資料來源(model),將控制項的公開屬性內容展開成視圖活動範圍內的區域變數。 例如控制項有公開屬性 title ,CommonGateway 會將此屬性指派為視圖的區域變數 $title 。
  • 若為 true : 同回傳 null 的情形。
  • 若為 false : 視同控制項自行處理回應工作, CommonGateway 不會繼續載入視圖。
  • 若為介於 100 ~ 599 間的整數,視為控制項直接回傳 HTTP 狀態碼。 CommonGateway 會將該狀態碼回傳給瀏覽器,而不載入任何視圖。
  • 若為 array : CommonGateway 會將回傳的陣列視為資料來源, 指派為視圖內的區域變數 $model,並將陣列內容展開成為視圖內的區域變數。 注意,若陣列為數字索引陣列,則展開後的區域變數名稱之字首為 data_ 。 例如 $model = array('a', 'b') ,則視圖內展開的區域變數內容將是 $data_0 == 'a', $data_1 == 'b' ,餘類推。
  • 若為 object : CommonGateway 會將回傳的個體視為資料來源, 指派為視圖內的區域變數 $model。 此時在視圖內將可以調用該個體的方法。這可以取代視圖輔助器(helper)。
  • 若為 array 或 object ,則視圖內將同時分配一個和控制項名稱相同的別名。 例如控制項 MyBook 回傳的資料為 object ,包含一個資料欄位 Title 。 則在視圖內,可用 $model->Title 或 $MyBook->Title 取得 Title 內容。

CommonGateway 除了引入 RoR 的設計概念外,實作時的 PHP 主要參考對象是 CodeIgniter 。 只是 CommonGateway 只著重在「web service」的設計工作,而不是網站設計。 如果你需要一個簡潔有效網站設計框架,我會建議你使用 CodeIgniter 。

相關文章

樂多舊回應
chiu0602@gmail.com(chiu0602) (#comment-22775078)
Sun, 03 Feb 2013 00:51:41 +0800
五百多行code就可以達成MVC, 不禁令我拍案叫絕, 令我不用把代碼轉為RoR, 感謝!
雖不明白為甚麼要把array展開成data_x, 不過還可引用$model (謝謝open source)

高手, 佩服!
未留名 (#comment-22775774)
Mon, 04 Feb 2013 15:00:06 +0800
因為 PHP 不允許像 $0, $1 這類的純數字符號名稱,所以解開時必須賦予一個前置名。
如果陣列是 key/value (Hash) 的話,就會直接變成 $key 這種較有意義的名稱。
sheephead0818@gmail.com(楊宗穎) (#comment-23402922)
Mon, 07 Oct 2013 16:59:59 +0800
深刻見解,好佩服