| <?php | 
| /** | 
|  * PhalApi_Api 接口服务基类 | 
|  * | 
|  * - 实现身份验证、按参数规则解析生成接口参数等操作 | 
|  * - 提供给开发人员自宝义的接口服务具体类继承 | 
|  * | 
|  * <br>通常地,可以这样继承:<br> | 
|  * | 
| ``` | 
|  *  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 <chanzonghuang@gmail.com> 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、接口级当前操作参数规则 | 
|      * | 
|      * <b>当规则有冲突时,以后面为准。另外,被请求的函数名和配置的下标都转成小写再进行匹配。</b> | 
|      * | 
|      * @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 | 
|      * | 
|      * <br>以下是一个简单的示例:<br> | 
| ``` | 
|      *     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 == '*'; | 
|     } | 
| } |