3.路由实现(phalapi框架总结)

mac2025-08-22  3

先来看框架的入口函数:index.php

<?php /** * 统一访问入口 */ require_once dirname(__FILE__) . '/init.php'; define('DS','/'); //$pai = new \PhalApi\PhalApi(); $pai = new \App\Common\MyPhalApi(); $pai->response()->output();

处理请求和响应的是response方法,这个方法的开头是这样的:

public function response() { $di = \PhalApi\DI(); error_reporting(E_ALL & ~E_NOTICE); set_error_handler(array(&$this,"customError")); // 开始响应接口请求 $di->tracer->mark('PHALAPI_RESPONSE'); $rs = $di->response; $isError = 0; $errorData = \PhalApi\DI()->requestInfo->get('errorData'); try { // 接口调度与响应 $api = ApiFactory::generateService(); $action = $di->request->getServiceAction(); $data = call_user_func(array($api, $action)); $rs->setData($data); } catch (Exception $ex) { ....... }

要先从request请求中获取到用户想访问的类以及方法,我们先看类,也就是$api    = ApiFactory::generateService();这个方法:

static function generateService($isInitialize = TRUE) { $di = DI(); $service = $di->request->getService(); $namespace = $di->request->getNamespace(); $api = $di->request->getServiceApi(); $action = $di->request->getServiceAction(); if (empty($api) || empty($action)) { throw new BadRequestException( T('service ({service}) illegal', array('service' => $service)) ); } $apiClass = '\\' . str_replace('_', '\\', $namespace) . '\\Api\\' . str_replace('_', '\\', ucfirst($api)); if (!class_exists($apiClass)) { throw new BadRequestException( T('no such service as {service}', array('service' => $service)), 4 ); } $api = new $apiClass(); if (!is_subclass_of($api, '\\PhalApi\\Api')) { throw new InternalServerErrorException( T('{class} should be subclass of \\PhalApi\\Api', array('class' => $apiClass)) ); } if (!method_exists($api, $action) || !is_callable(array($api, $action))) { throw new BadRequestException( T('no such service as {service}', array('service' => $service)), 4 ); } if ($isInitialize) { $api->init(); } return $api; }

其实就是从request里去取出相关的类名称及方法名,然后进行一系列合法性判断.先看request的getService方法:

/** * 获取接口服务名称 * * - 子类可重载此方法指定参数名称,以及默认接口服务 * - 需要转换为原始的接口服务格式,即:Namespace.Class.Action * - 当命名空间为空时,默认使用App命名空间 * - 为保持兼容性,子类需兼容父类的实现 * - 参数名为:service,支持短参数名:s,并优先完全参数名 * * @return string 接口服务名称,如:Default.Index */ public function getService() { $service = $this->get('service', $this->get('s', 'App.Site.Index')); if (count(explode('.', $service)) == 2) { $service = 'App.' . $service; } return $service; }

就是通过地址栏取出相关参数.

request的构造方法里就先调用了getService方法:

public function __construct($data = NULL) { // 主数据源 $this->data = $this->genData($data); // 备用数据源 $this->get = $_GET; $this->post = $_POST; $this->request = $_REQUEST; $this->cookie = $_COOKIE; @list($this->namespace, $this->apiName, $this->actionName) = explode('.', $this->getService()); }

当然安全起见我们不会把类名这些全部完整的通过url发来,比如我用?r=a表示?s=User.Weibo.Login?简单,直接在本地做个映射。比如我通过$_GET['r']获取到值为a,然后去一个自己配置的路由映射表里,取出a所对应的真实路径User.Weibo.Login.这样就达到效果了。修改后的getService方法如下:

public function getService() { // 优先返回自定义格式的接口服务名称 $service = $this->get('r'); $service = trim($service); if($service) { //下面要换成redis缓存 //$service = \PhalApi\DI()->config->get('rules.'.$service); $rulesData = \app\MDC()->PublicObj->rulesCache(); $service = $rulesData[$service]; } if (!empty($service)) { $namespace = count(explode('.', $service)) == 2 ? 'App.' : ''; return $namespace . $service; } return parent::getService(); } $rulesData里就是去自己配置的映射表里取出对应的真实请求类以及方法.

当然,一般不修改框架源码,这个方法我是自己写了一个扩展子类,然后去di.php把request类注册成自己扩展的类,如下:

$di->request = new App\Common\Request();

 

最新回复(0)