通常地,可以这样继承:
*
```
* class Api_Demo extends PhalApi_Api {
*
* public function getRules() {
* return array(
* // ...
* );
* }
*
* public function doSth() {
* $rs = array();
*
* // ...
*
* return $rs;
* }
* }
```
*
* @property mixed $whatever 接口参数
* @package PhalApi\Api
* @license http://www.phalapi.net/license GPL 协议 GPL 协议
* @link http://www.phalapi.net/
* @author dogstar 2014-10-02
*/
class PhalApi_Api {
/**
* 设置规则解析后的接口参数
* @param string $name 接口参数名字
* @param mixed $value 接口参数解析后的值
*/
public function __set($name, $value) {
$this->$name = $value;
}
/**
* 获取规则解析后的接口参数
* @param string $name 接口参数名字
* @throws PhalApi_Exception_InternalServerError 获取未设置的接口参数时,返回500
* @return mixed
*/
public function __get($name) {
if(!isset($this->$name) || empty($name)) {
throw new PhalApi_Exception_InternalServerError(
T('PhalApi_Api::${name} undefined', array('name' => $name))
);
}
return $this->$name;
}
/**
* 初始化
*
* 主要完成的初始化工作有:
* - 1、[必须]按参数规则解析生成接口参数
* - 2、[可选]过滤器调用,如:签名验证
* - 3、[可选]用户身份验证
*
* @uses PhalApi_Api::createMemberValue()
* @uses PhalApi_Api::filterCheck()
* @uses PhalApi_Api::userCheck()
* @return null
*/
public function init() {
$this->createMemberValue();
$this->filterCheck();
$this->userCheck();
}
/**
* 按参数规则解析生成接口参数
*
* 根据配置的参数规则,解析过滤,并将接口参数存放于类成员变量
*
* @uses PhalApi_Api::getApiRules()
*/
protected function createMemberValue() {
foreach ($this->getApiRules() as $key => $rule) {
$this->$key = DI()->request->getByRule($rule);
}
}
/**
* 取接口参数规则
*
* 主要包括有:
* - 1、[固定]系统级的service参数
* - 2、应用级统一接口参数规则,在app.apiCommonRules中配置
* - 3、接口级通常参数规则,在子类的*中配置
* - 4、接口级当前操作参数规则
*
* 当规则有冲突时,以后面为准。另外,被请求的函数名和配置的下标都转成小写再进行匹配。
*
* @uses PhalApi_Api::getRules()
* @return array
*/
public function getApiRules() {
$rules = array();
$allRules = $this->getRules();
if (!is_array($allRules)) {
$allRules = array();
}
$allRules = array_change_key_case($allRules, CASE_LOWER);
$action = strtolower(DI()->request->getServiceAction());
if (isset($allRules[$action]) && is_array($allRules[$action])) {
$rules = $allRules[$action];
}
if (isset($allRules['*'])) {
$rules = array_merge($allRules['*'], $rules);
}
$apiCommonRules = DI()->config->get('app.apiCommonRules', array());
if (!empty($apiCommonRules) && is_array($apiCommonRules)) {
$rules = array_merge($apiCommonRules, $rules);
}
return $rules;
}
/**
* 获取参数设置的规则
*
* 可由开发人员根据需要重载
*
* @return array
*/
public function getRules() {
return array();
}
/**
* 过滤器调用
*
* 可由开发人员根据需要重载,以实现项目拦截处理,需要:
* - 1、实现PhalApi_Filter::check()接口
* - 2、注册的过滤器到DI()->filter
*
*
以下是一个简单的示例:
```
* class My_Filter implements PhalApi_Filter {
*
* public function check() {
* //TODO
* }
* }
*
*
* //在初始化文件 init.php 中注册过滤器
* DI()->filter = 'My_Filter';
```
*
* @see PhalApi_Filter::check()
* @throws PhalApi_Exception_BadRequest 当验证失败时,请抛出此异常,以返回400
*/
protected function filterCheck() {
// 过滤服务白名单
if ($this->isServiceWhitelist()) {
return;
}
$filter = DI()->get('filter', 'PhalApi_Filter_None');
if (isset($filter)) {
if (!($filter instanceof PhalApi_Filter)) {
throw new PhalApi_Exception_InternalServerError(
T('DI()->filter should be instanceof PhalApi_Filter'));
}
$filter->check();
}
}
/**
* 用户身份验证
*
* 可由开发人员根据需要重载,此通用操作一般可以使用委托或者放置在应用接口基类
*
* @throws PhalApi_Exception_BadRequest 当验证失败时,请抛出此异常,以返回400
*/
protected function userCheck() {
}
/**
* 是否为白名单的服务
*
* @return boolean
*/
protected function isServiceWhitelist() {
$api = DI()->request->getServiceApi();
$action = DI()->request->getServiceAction();
$serviceWhitelist = DI()->config->get('app.service_whitelist', array());
foreach ($serviceWhitelist as $item) {
$cfgArr = explode('.', $item);
if (count($cfgArr) < 2) {
continue;
}
// 短路返回
if ($this->equalOrIngore($api, $cfgArr[0]) && $this->equalOrIngore($action, $cfgArr[1])) {
return TRUE;
}
}
return FALSE;
}
/**
* 相等或忽略
*
* @param string $str 等判断的字符串
* @param string $cfg 规则配置,*号表示通配
* @return boolean
*/
protected function equalOrIngore($str, $cfg) {
return strcasecmp($str, $cfg) == 0 || $cfg == '*';
}
}