PhalApi 2.7 開發快速上手

PhalApi是一款國人製作的PHP純後端框架。它的開發相當簡單,同時也具備文檔生成等特色功能。下面,我通過簡單的幾點,讓你可以快速入門使用該框架的開發。
建議使用PHPStorm作為IDE,代碼提示相當完全。由於PHP的熱更新特性,修改過的PHP文件保存後立即生效,無需編譯,無需重啟服務器。

什麼是PhalApi

PhalApi是一個輕量級的PHP接口框架。有別於傳統的框架,它只面向後端接口的開發。
官網:https://www.phalapi.net
官方文檔:http://docs.phalapi.net/#/v2.0/

安裝PhalApi

Composer是PHP的包管理器(類似於Java的Maven、node的npm)。
Composer的安裝請參考https://pkg.phpcomposer.com/#how-to-install-composer,不要在英文官網直接下載安裝包。
Composer安裝後請立即切換到國內源https://developer.aliyun.com/composer。
Phar是PHP界的Jar包,可以像Jar包一樣引入即用。
在項目目錄下執行composer create-project phalapi/phalapi即可創建PhalApi項目,項目路徑為./phalapi
若需要安裝阿里雲OSS的SDK,則在項目路徑下(注意cd到phalapi目錄)按https://help.aliyun.com/document_detail/85580.html說明調用Composer。

composer require aliyuncs/oss-sdk-php  composer install

PhalApi提供了再封裝的PhalApi-OSS阿里雲OSS包(https://github.com/vivlong/phalapi-aliyun-oss),也可以考慮使用

本地調試環境配置

建議使用傻瓜式一體化軟件XAMPP搭建本地的調試環境。
修改XAMPP中Apache的配置文件,找到這兩行:

DocumentRoot "X:/XAMPP/htdocs"  <Directory "X:/XAMPP/htdocs">

將引號內目錄修改到PhalApi項目的public目錄,注意使用左斜杠替換右斜杠、
修改後啟動Apache,則項目在localhost:80上啟動。如需修改端口請參考Apache配置文件的其他配置項。

更優雅的接口地址

默認的接口網址為類似於http://xxx.com/?s=App.User.Login(?s後為接口全限定名),不夠美觀。開啟URI路由匹配後可以實現http://xxx.com/User/Login的接口路徑,比較美觀。(App字段可以省略)
開啟方式:

  1. ./config/sys.php修改enable_uri_match配置為true
  2. 撰寫服務器Rewrite規則。若使用Nginx服務器,請直接參考官方文檔示例(http://docs.phalapi.net/#/v2.0/how-to-request?id=開啟uri路由匹配)。
    若使用Apache服務器:
    • ./public目錄下新建規則文件.htaccess
    • 寫入下列內容(即將所有404請求轉發到index.php,作為一個controller)

      RewriteEngine on  RewriteCond %{REQUEST_FILENAME} !-f  RewriteCond %{REQUEST_FILENAME} !-d  RewriteRule ^(.*)$ index.php
    • 重啟Apache服務器

PhalApi的ADM三層模型

有別於軟件開發領域常用的MVC三層模型,在後端純接口開發中,View層顯然沒有任何意義。所以PhalApi採用了創新的ADM三層模型。

  • A – Api接口層,只負責接受請求、調度Domain層、發送響應
  • D – Domain領域層,最關鍵,負責業務邏輯的處理
  • M – Model數據層,只負責與數據庫的交互。注意,它不僅描述Object的基本信息,還要實現一些與會與數據庫交互的方法。類似於JavaEE中的Bean定義+DAO層

Api接口

三層模型所有的代碼均在./src/app目錄下。Api、Domain、Model目錄下默認生成了一些demo,可以刪除。
在Api目錄下新建php類,假設為MyClass.php。注意下列要求:

  • namespace AppApi; – 命名空間。注意,必須限定到MyClass.php所在目錄。即如果MyClass.php放在./src/app/Api/Mobile下,則namespace AppApiMobile
  • use PhalApiApi; – 要求使用Api類。
  • class MyClass extends Api – 所有類都要繼承自Api類。
    在類中定義的一系列public的函數,就是一個個實際的接口(注意,不是類是接口,而是類的成員函數是接口!所以這個類是起到了接口分類的作用)
    接口的返回格式永遠是JSON,形式為 {ret, data, msg},其中ret是HTTP狀態碼,msg是錯誤提示信息,而data就是我們的業務數據。在函數中return的關聯數組會自動轉化成JSON,注入data。例如

    下面介紹接口的調用網址,假設函數名為hello,類文件為MyClass.php
MyClass路徑 接口URL
./src/app/Api/MyClass.php XXX.com/MyClass/hello
./src/app/Api/Mobile/MyClass.php (正確)XXX.com/Mobile_MyClass/hello
./src/app/Api/Mobile/MyClass.php (錯誤)XXX.com/Mobile/MyClass/hello

即若類文件在Api目錄下發生了層疊,則需要加入下劃線分隔,而不是一直用斜杠。
下面介紹如何在Api層獲取和處理請求參數

  • 重寫public function getRules()方法,

    public function getRules() {    return array(      '[FUNCTION NAME]' => array(      '[PARAM NAME]' => array('name' => '...', 'require' => ..., 'type' => '...', 'source' => '...', 'desc' => '...', 'min' => ..., 'max' => ...)    );  }

    返回一個關聯數組,鍵為該Api類下的函數名稱(即接口),值又是一個關聯數組,鍵為參數名,值又是一個關聯數組,用於進行該參數的配置,具體配置項可參考http://docs.phalapi.net/#/v2.0/api-params。下面介紹幾個常用的:

    • name:string,前端請求時用的參數名
    • require:boolean,是否必須
    • type:參數類型,可以用string、int等
    • source:string, 參數源,可以用post、get(注意小寫)等
    • desc:參數說明,該說明會出現在自動文檔中
    • min和max(均可選):當type是int時,可以起到後端參數校驗的作用
  • 在接口對應函數的代碼中取參數:

$param_var = $this->[PARAM NAME]

取好參數,發給Domain層的對應函數,獲取返回值,發送響應給服務器端。這就是Api層做的事。
一般來說,需要在頭部use一下AppDomain下的對應的Domain,然後在需要時new一個domain對象來使用。

Model模型

在Model目錄下新建php類,假設為User.php。通常的開發習慣是,數據庫一張表對應一個Model類,這樣可以實現對錶的描述(類似於Mybatis與POJO的配合)。以MySQL為例,注意下列要求:

  • namespace AppModel; – 同樣要嚴格限定到User.php目錄
  • use PhalApiModelNotORMModel as NotORM; – 要求使用NotORM類。NotORM是PhalApi內部的封裝的MySQL交互引擎
  • class User extends NotORM – 模型類必須繼承NotORM
  • protected function getTableName($id) {      return 'user_score';  }

    重寫protected function getTableName($id)方法(注意函數簽名帶參$id),返回值是該模型對應的表名。
    現在即可在User類下編寫各個CURD功能的函數,函數參數是進行SQL查詢需要參數。具體使用請參考官方文檔http://docs.phalapi.net/#/v2.0/database-usage。整體風格是鏈式調用,例如:

首先要通過$this->getORM()獲取NotORM實例,然後組織相關SQL關鍵字即可。
值得一提的是,NotORM的功能非常的強大(起到了SQL語句Builder的作用,極大地避免了直接組織SQL語句),它甚至可以接收關聯數組為參數進行各種智能的INSERT、UPDATE等操作。具體使用請參考http://docs.phalapi.net/#/v2.0/database-usage,靠這裡的一兩句話是說不清道不明的。
如果有需要,NotORM也支持執行原生SQL語句。

Domain領域

在Domian領域下新建php類,假設為User.php。對於Domain類,要求比較簡單:

  • namespace AppDomain; – 你懂的
  • 沒了。

Domain類不要求use框架內部的類,也不要求定義的類繼承相關類。
在User類下寫相關業務函數即可。一般來說,此處函數的參數是Api層請求需要的所有參數
在業務函數中,調用Model層中封裝好的有關CURD函數,完成業務邏輯。一般來說,需要在User.php頭部use一下AppModel下的對應的User的model,在進行CURD操作時,new一個model對象來使用。

全局配置文件

./config目錄下的app.php、dbs.php、di.php、sys.php就是四大全局配置文件。
如何在./src中腳本的任意位置取出其中配置的鍵值對?PhalApi用了依賴注入(DI)和反射的功能,實現了看起來非常Java的方式來獲取。
例如在app.php中配置了如下內容:

'token' => array( 'SALT' => 'imsalt', 'expire' => 7200)

要取鹽值,則取法為PhalApiDI()->config->get('app.token.SALT')

即對於這四大配置文件,均用PhalApiDI()->config->get來訪問,參數為配置項的全限定名。

這四大配置文件的作用是:

  • app.php – 用戶自定義的相關配置均追加於此
  • dbs.php – 數據庫相關配置
  • di.php – 依賴注入相關配置,當有新組件插入時需要修改
  • sys.php – 系統級配置,例如是否調試、URI匹配等

全局功能函數

如何定義一個在./src中任何位置都可以調用的功能函數?把這個函數的實現寫在./src/functions.php中即可。在需要調用時,使用App[FUNCTION NAME]來調用。

自動接口文檔

不同於Swagger的高侵入性,PhalApi的自動接口文檔系統非常的簡單優雅。不僅方便前端閱讀,還具備自解釋性,利於後續後端的維護。和JavaDoc類似,它全部依靠多行注釋完成。並且,多行注釋內允許使用基本的HTML標籤,如<b></b>來加粗等等。

注意,合法的多行注釋格式如下,首行有兩個星號。

/**   *   */

這樣使用:

  • 在Api層的每個類文件的use PhalApiApi;下行進行注釋,用於說明該類的整體作用

  • 在getRules函數內進行的相關參數配置會自動反映到接口文檔中

  • 在接口對應函數的正上方進行注釋,用於說明該接口的作用。語法如下:

    • 第一行寫接口名稱
    • @desc後寫接口描述(description)
    • @return後寫接口返回值描述,@return [type] [name] [description]
    • 如果不想要該接口被自動文檔掃描,則添加@ignore

若一切配置無誤,則可以在http://xxx.com/docs.php閱讀到自動生成的接口文檔。

接口文檔網頁還可以進行定製:

  • 修改./public/docs.php中的$projectName,可以修改左上角LOGO處的標題。
  • 如果需要公共說明(如圖中的統一說明),則在./public/docs.php末尾添加php腳本閉標籤(?>)後,可以書寫HTML代碼。由於該頁面布局的原因,不建議將公共說明寫在頭部。

前端請求方式

  • 對於PHP後端,前端在發送POST請求前必須設置如下請求頭,否則後端收不到數據:

header: {'content-type': "application/x-www-form-urlencoded; charset=UTF-8"}

​ GET請求沒有類似限制。

  • 前端用axios來POST時,data為Object,無需stringify

  • 前端收到的響應作res.data後即為如下結構的Object:

{      "ret": 200,      "data": {          ...      },      "msg": ""  }

​ ret為http狀態碼,msg為錯誤提示信息(當狀態碼非200時才有),data為業務數據

  • 在自動接口文檔中,形如App.X_Y.Z的接口請求地址為http://xxx.com/X_Y/Z(如果配置了URI匹配)

開發實例

我們以一個簡單的接口為例(獲取表中分數最高的n個記錄),展示一下PhalApi下一個接口的開發過程

  1. 這是一個公共接口,用於手機端。故在./src/app/Api/Mobile下新建PHP類文件Other.php
  2. 進行namespace、use的基本配置,讓Other類繼承Api。編寫類文件注釋用於生成文檔。注意此處use…as…別名的使用。

  3. 重寫getRules方法,接受一個參數n,函數名是getTopN

  4. getTopN函數以及相關文檔注釋。注意,接口函數都不需要參數,而是在內部獲取。

  5. 開始開發Domain層。在./src/app/Domain/Mobile下新建PHP類文件Other.php

  6. 進行namespace的配置,寫業務邏輯代碼(當n>5時視為n=5處理)。可以看到返回的是一個響應體。所以在Api層是直接return的。注意此處同樣使用了別名use AppModelMobileOther as ModelOther;

  7. 顯然該業務需要與數據庫交互,所以需要開發Model層。在./src/app/Model/Mobile下新建PHP類文件Other.php

  8. 進行namespace和use配置,Other類繼承NotORM類。重寫getTableName($id)方法

  9. 實現getTopN($n)方法。注意,此處SQL查詢的結果會自動轉為關聯數組傳到Domain層,Domain層再向上傳到Api層,Api層響應時關聯數組就自動變成JSON結構注入到響應體的data中了,多麼美妙!

  10. 至此,一個接口開發完成。相應的文檔也已經自動生成了。

系統日誌

PhalApi提供了一個簡單日誌系統。它將日誌等級分類三級:error、info、debug。

保存目錄:./runtime/log

調用方式:PhalApiDI()->logger->error/info/debug([日誌內容], [上下文描述(可選)])

MySQL數據庫

MySQL數據庫需要在./config/dbs.php內配置。

PhalApi支持實現多庫集群、分庫分表。具體請參考:

  • http://docs.phalapi.net/#/v2.0/database-connect
  • http://docs.phalapi.net/#/v2.0/database-multi
  • http://docs.phalapi.net/#/v2.0/database-other

服務端請求CUrl

如果有需要在服務器端利用服務器發送HTTP請求(例如小程序開發中的code2session),PhalApi為我們封裝了CUrl庫來實現,非常簡單。參考http://docs.phalapi.net/#/v2.0/curl。

緩存體系

PhalApi支持許多種緩存。

此處用常用的Redis說明。

  • ./config/di.php中向$di注入Redis依賴:
$redis_config = array('host' => '127.0.0.1', 'port' => 6379);  $di->cache = new PhalApiCacheRedisCache($redis_config);
  • 之後,在需要使用Redis的地方利用PhalApiDI()->cache->set/get/delete等等即可

CORS跨域

利用跨域插件https://github.com/gongshunkai/phalapi-cors,配置相當簡單。

RESTful

由於PHP只是Apache/Nginx上的插件,HTTP服務是由它們提供的,所以若要實現嚴格的RESTful API,必須藉助它們的支持。例如Apache,需要對.htaccess文件進行配置,來轉發/重定向請求,將RESTful的請求轉化為傳統的。

具體可參考這個實踐:https://www.ctolib.com/luyuanxun-phalapi-restful.html

上線部署

PHP項目的部署非常簡單,上傳代碼就完事了!將整個phalapi目錄上傳到服務器上,然後確保你在遠端服務器上也進行了全文開頭的操作:

  • Apache默認文檔路徑指向./public
  • 配置好了Rewrite規則

此刻,你的接口就部署完成了。當然,你應該考慮一下:

  • 真實上線,文檔不能讓別人隨意看到!請備份後刪除./public/docs.php
  • 刪除無用的./sdk和./tests目錄

【全文終,謝謝您的閱讀!】