一個(gè)真正的強(qiáng)者,不是擺平了多少人,而是他能幫助到多少人。 --開(kāi)源中國(guó)源創(chuàng)會(huì)分享廣州站 @海洋之心-悟空
首次使用此接口開(kāi)發(fā)框架時(shí),可以先查看此開(kāi)發(fā)示例。
假設(shè),我們需要為開(kāi)源中國(guó)打造一個(gè)平放平臺(tái),其中有一個(gè)接口是可以根據(jù)用戶ID來(lái)獲取用戶的基本信息。
本文,就以模擬獲取開(kāi)源中國(guó)用戶信息接口開(kāi)發(fā)(即:從數(shù)據(jù)庫(kù)獲取用戶的基本信息并以JSON格式返回給客戶端)為例,簡(jiǎn)明的接口開(kāi)發(fā)中的流程,以及用到的基本功能和操作,主要包括有:統(tǒng)一入口文件、參數(shù)規(guī)則配置、接口層/領(lǐng)域?qū)?模型持久層、日志紀(jì)錄、數(shù)據(jù)庫(kù)操作、配置讀取、國(guó)際化翻譯等。
以給大家一個(gè)感觀的認(rèn)識(shí)。
最終接口的調(diào)用與返回結(jié)果如下:
//接口請(qǐng)求格式
http://dev.phalapi.com/demo/?service=User.GetBaseInfo&user_id=帳號(hào)ID
//返回結(jié)果格式
{
"ret": 200,
"data": {
"code": 0, //狀態(tài)碼,0表示正常獲取,1表示用戶不存在
"msg": "",
"info": { //用戶信息
"id": "1", //用戶ID
"name": "dogstar", //帳號(hào)
"note": "oschina" //來(lái)源
}
},
"msg": ""
}
為了更好保護(hù)我們的項(xiàng)目代碼,建議將接口的訪問(wèn)路徑設(shè)置在:./PhalApi/Pubic目錄,并且各套接口(如按版本分:v1/v2/v3等等;按不同終端分:ios/android/pc等)各自獨(dú)立入口。所以本示例中將./PhalApi/Pubic/demo/index.php下。
參數(shù)入口文件的寫(xiě)法,我們可以快速得到基本的入口文件如下:
// $ vim ./Public/demo/index.php
<?php
/**
* Demo 統(tǒng)一入口
*/
require_once dirname(__FILE__) . '/../init.php';
//裝載你的接口
DI()->loader->addDirs('Demo');
/** ---------------- 響應(yīng)接口請(qǐng)求 ---------------- **/
$server = new PhalApi();
$rs = $server->response();
$rs->output();
此外,我們還需要一個(gè)公共的初始化文件:
//$ vim ./Public/init.php
<?php
/**
* 統(tǒng)一初始化
*/
/** ---------------- 根目錄定義,自動(dòng)加載 ---------------- **/
date_default_timezone_set('Asia/Shanghai');
defined('API_ROOT') || define('API_ROOT', dirname(__FILE__) . '/..');
require_once API_ROOT . '/PhalApi/PhalApi.php';
$loader = new PhalApi_Loader(API_ROOT);
/** ---------------- 注冊(cè)&初始化服務(wù)組件 ---------------- **/
//自動(dòng)加載
DI()->loader = $loader;
//配置
DI()->config = new PhalApi_Config_File(API_ROOT . '/Config');
//日志紀(jì)錄
DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime',
PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);
//數(shù)據(jù)操作 - 基于NotORM,$_GET['__sql__']可自行改名
DI()->notorm = function() {
$debug = !empty($_GET['__sql__']) ? true : false;
return new PhalApi_DB_NotORM(DI()->config->get('dbs'), $debug);
};
//調(diào)試模式,$_GET['__debug__']可自行改名
DI()->debug = !empty($_GET['__debug__']) ? true : DI()->config->get('sys.debug');
//翻譯語(yǔ)言包設(shè)定
SL('zh_cn');
遵循最佳實(shí)踐,我們?cè)诰帉?xiě)代碼前先編寫(xiě)單元測(cè)試。但同時(shí)為了減少編寫(xiě)測(cè)試代碼的痛苦,我們可以先定義接口函數(shù)簽名,再通過(guò)腳本自動(dòng)生成測(cè)試代碼骨架來(lái)提高我們的開(kāi)發(fā)效率。
//$vim ./Demo/Api/User.php
<?php
class Api_User extends PhalApi_Api
{
public function getBaseInfo()
{
}
}
通過(guò)腳本生成測(cè)試骨架:
$ mkdir -p ./Demo/Tests/Api/
$ cd ./Demo/Tests/Api
$ php ../../../PhalApi/build_phpunit_test_tpl.php ../../Api/User.php Api_User ./../test_env.php
然后,根據(jù)/Public/demo/index.php入口文件,也搭建一個(gè)測(cè)試環(huán)境的入口文件:
vim ./Demo/Tests/test_env.php
修正一下Api_Examples_User_Test.php里,測(cè)試環(huán)境test_env.php的包含路徑:
//手動(dòng)調(diào)用test_env.php的路徑
require_once dirname(__FILE__) . '/../test_env.php';
并且,修改測(cè)試,以符合我們通過(guò)userId=1獲取基本信息(名字為dogstar,來(lái)源為oschina):
//$vim ./Demo/Tests/Api/Api_User_Test.php
/**
* @group testGetBaseInfo
*/
public function testGetBaseInfo()
{
$str = 'service=User.GetBaseInfo&user_id=1';
parse_str($str, $params);
DI()->request = new PhalApi_Request($params);
$api = new Api_User();
//自己進(jìn)行初始化
$api->init();
$rs = $api->getBaseInfo();
$this->assertNotEmpty($rs);
$this->assertArrayHasKey('code', $rs);
$this->assertArrayHasKey('msg', $rs);
$this->assertArrayHasKey('info', $rs);
$this->assertEquals(0, $rs['code']);
$this->assertEquals('dogstar', $rs['info']['name']);
$this->assertEquals('oschina', $rs['info']['note']);
}
此時(shí),單元測(cè)試是預(yù)期失敗的:
$ phpunit ./Api_User_Test.php
PHPUnit 4.3.4 by Sebastian Bergmann.
F
Time: 3 ms, Memory: 5.25Mb
There was 1 failure:
1) PhpUnderControl_ApiUser_Test::testGetBaseInfo
Failed asserting that a NULL is not empty.
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
此接口層,主要是負(fù)責(zé)響應(yīng)客戶端的請(qǐng)求,調(diào)用需要的領(lǐng)域?qū)舆M(jìn)行必要的服務(wù)功能提供。
為了獲取到用戶ID,我們可以在getRules()函數(shù)里面定義參數(shù)規(guī)則,以便框架自動(dòng)幫我們過(guò)濾獲取需要的參數(shù)。
溫馨提示:
接口層的全部類成員函數(shù)都應(yīng)以小寫(xiě)開(kāi)頭。
但對(duì)外,函數(shù)首字母不區(qū)分大小寫(xiě),因?yàn)榭蚣軙?huì)將請(qǐng)求的函數(shù)強(qiáng)制轉(zhuǎn)換成小寫(xiě)再執(zhí)行。原因在于:
1、我們堅(jiān)持駝峰法的代碼風(fēng)格;
2、對(duì)外界我們可以統(tǒng)一使用大寫(xiě)來(lái)提供服務(wù)名稱,如:User.Login,這樣更顯專業(yè)。
//$vim ./Demo/Api/User.php
<?php
class Api_User extends PhalApi_Api
{
public function getRules()
{
return array(
'getBaseInfo' => array(
'userId' => array('name' => 'userId', 'type' => 'int', 'min' => 1, 'require' => true),
),
);
}
//...
如上,我們就定義了getBaseInfo接口中的userId參數(shù),名字也為userId,整型,最小值為1,必須。
//$vim ./Demo/Api/User.php
public function getBaseInfo()
{
$rs = array('code' => 0, 'msg' => '', 'info' => array());
$domain = new Domain_User();
$info = $domain->getBaseInfo($this->userId);
if (empty($info)) {
DI()->logger->debug('user not found', $this->userId);
$rs['code'] = 1;
$rs['msg'] = T('user not exists');
return $rs;
}
$rs['info'] = $info;
return $rs;
}
領(lǐng)域?qū)又饕顷P(guān)注復(fù)雜業(yè)務(wù)的處理,以及緩存的處理、耗時(shí)操作后臺(tái)異步處理等,并調(diào)用Model持久層獲取需要的數(shù)據(jù)。因此,是Api與Model層之間的橋梁。
在此示例中,我們只需要簡(jiǎn)單地調(diào)用Model層獲取用戶的信息即可,再加強(qiáng)一下用戶ID的合法性判斷。
//$ vim ./Demo/Domain/User.php
<?php
class Domain_User
{
public function getBaseInfo($userId)
{
$rs = array();
$userId = intval($userId);
if ($userId <= 0) {
return $rs;
}
$model = new Model_User();
$rs = $model->getByUserId($userId);
return $rs;
}
}
此一層主要關(guān)注數(shù)據(jù)從持久存儲(chǔ)的獲取,特別是針對(duì)數(shù)據(jù)庫(kù)的操作,但不排除其他媒介,如文件、緩存等。
首先,先準(zhǔn)備一下我們需要的表和數(shù)據(jù):
CREATE TABLE `phalapi_test`.`tbl_user` (
`id` INT NOT NULL,
`name` VARCHAR(45) NULL,
`note` VARCHAR(45) NULL,
PRIMARY KEY (`id`));
INSERT INTO `phalapi_test`.`tbl_user` (`id`, `name`, `note`) VALUES ('1', 'dogstar', 'oschina');
然后,編寫(xiě)需要的Model代碼,即利用NotORm實(shí)現(xiàn)對(duì)數(shù)據(jù)的操作:
//$ vim ./Demo/Model/User.php
<?php
class Model_User
{
public function getByUserId($userId)
{
return DI()->notorm->user->select('*')->where('id = ?', $userId)->fetch();
}
}
在完成上面簡(jiǎn)單的開(kāi)發(fā)后,我們即可以實(shí)現(xiàn)接口的開(kāi)發(fā),運(yùn)行一下剛才的單元測(cè)試,完美通過(guò)!
$ phpunit ./Api_User_Test.php
PHPUnit 4.3.4 by Sebastian Bergmann.
SELECT * FROM tbl_user WHERE (id = ?); -- 1
.SELECT * FROM tbl_user WHERE (id = ?); -- 1<br />
Time: 34 ms, Memory: 6.50Mb
OK (2 tests, 7 assertions)
在單元測(cè)試的保證下,我們便可以放心大膽地將我們的代碼發(fā)布到外網(wǎng),提供給更多的開(kāi)發(fā)者,和終端用戶使用。
因?yàn)槭菃卧獪y(cè)試,所以我們配置搭建了新的一個(gè)測(cè)試環(huán)境,特別對(duì)于數(shù)據(jù)庫(kù)的配置,如下:
//$ vim ./Config/dbs.php
<?php
/**
* examples配置
*/
return array(
/**
* DB數(shù)據(jù)庫(kù)服務(wù)器集群
*/
'servers' => array(
'db_demo' => array(
'host' => '192.168.0.104', //數(shù)據(jù)庫(kù)域名
'name' => 'phalapi_test', //數(shù)據(jù)庫(kù)名字
'user' => 'root', //數(shù)據(jù)庫(kù)用戶名
'password' => '123456', //數(shù)據(jù)庫(kù)密碼
'port' => '3306', //數(shù)據(jù)庫(kù)端口
),
),
/**
* 自定義路由表
*/
'tables' => array(
'__default__' => array(
'prefix' => 'tbl_',
'key' => 'id',
'map' => array(
array('db' => 'db_demo'),
),
),
),
);
溫馨提示:
為了方便在單元測(cè)試時(shí)進(jìn)行調(diào)試,和查看日志,對(duì)于全部查詢、執(zhí)行的SQL語(yǔ)句都會(huì)顯示出來(lái),全部的日志改用控制臺(tái)輸出。
接口調(diào)用的鏈接,這時(shí)已經(jīng)相當(dāng)明了了,即:域名 + 路徑(/demo) + 參數(shù)(可從單元測(cè)試那直接獲?。?/p>
如本示例的是:
http://dev.phalapi.com/demo/?service=User.GetBaseInfo&user_id=1
返回的結(jié)果是:
{
"ret": 200,
"data": {
"code": 0,
"msg": "",
"info": {
"id": "1",
"name": "dogstar",
"note": "oschina"
}
},
"msg": ""
}
截圖效果:
溫馨提示:
如果提示日志寫(xiě)入失敗,請(qǐng)確保./Runtime目錄具有寫(xiě)入權(quán)限,即0777。
當(dāng)我們?cè)L問(wèn)一個(gè)不存在的用戶時(shí),將會(huì)觸發(fā)日志紀(jì)錄:
DI()->logger->debug('user not found', $this->userId);
如訪問(wèn):
http://dev.phalapi.com/demo/?service=User.GetBaseInfo&user_id=2
然后,可以在Runtime下看到按天分目錄的日志:
$ tailf ./Runtime/log/201501/20150128.log
2015-01-28 00:37:34|DEBUG|user not found|2
溫馨提示:
外網(wǎng)環(huán)境上,請(qǐng)把Runtime目錄軟鏈到磁盤(pán)空間很大的路徑。
當(dāng)需要翻譯時(shí),可以使用人性化的函數(shù)T(),如:
$rs['msg'] = T('user not exists');
對(duì)應(yīng)地需要補(bǔ)充翻譯的內(nèi)容:
//$ vim ./Language/zh_cn/common.php
'user not exists' => '用戶不存在',
還是以上面的用戶不存在為例,看下運(yùn)行的截圖效果:
配置的讀取,使用方便,直接通過(guò)以下方式便可以獲取,以點(diǎn)號(hào)分割:
DI()->config->get('dbs')
第一段,必須為文件名,后面的為用點(diǎn)號(hào)相連的數(shù)組下標(biāo),不限級(jí)。
為了方便客戶端實(shí)時(shí)查看最新的接口參數(shù),這里提供了一個(gè)快速的在線工具:
http://demo.phalapi.net/checkApiParams.php?service=User.GetBaseInfo
用瀏覽器打開(kāi)后,即可以看到最新的接口參數(shù)說(shuō)明, 不需要后臺(tái)接口開(kāi)發(fā)人員編寫(xiě)文檔維護(hù),直接從代碼中生成參數(shù)報(bào)表:
從上面可以得到,此示例相關(guān)的代碼如下:
Demo$ tree
.
├── Api
│ ├── Default.php
│ └── User.php
├── Domain
│ └── User.php
├── Model
│ └── User.php
└── Tests
├── Api
│ ├── Api_Default_Test.php
│ └── Api_User_Test.php
└── test_env.php
更多建議: