123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- <?php
- // +----------------------------------------------------------------------
- // | ThinkPHP [ WE CAN DO IT JUST THINK ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
- // +----------------------------------------------------------------------
- // | Author: liu21st <liu21st@gmail.com>
- // +----------------------------------------------------------------------
-
- namespace think\route;
-
- use Psr\Http\Message\ResponseInterface;
- use think\App;
- use think\Container;
- use think\exception\ValidateException;
- use think\Request;
- use think\Response;
-
- abstract class Dispatch
- {
- /**
- * 应用对象
- * @var App
- */
- protected $app;
-
- /**
- * 请求对象
- * @var Request
- */
- protected $request;
-
- /**
- * 路由规则
- * @var Rule
- */
- protected $rule;
-
- /**
- * 调度信息
- * @var mixed
- */
- protected $dispatch;
-
- /**
- * 调度参数
- * @var array
- */
- protected $param;
-
- /**
- * 状态码
- * @var string
- */
- protected $code;
-
- /**
- * 是否进行大小写转换
- * @var bool
- */
- protected $convert;
-
- public function __construct(Request $request, Rule $rule, $dispatch, $param = [], $code = null)
- {
- $this->request = $request;
- $this->rule = $rule;
- $this->app = Container::pull('app');
- $this->dispatch = $dispatch;
- $this->param = $param;
- $this->code = $code;
-
- if (isset($param['convert'])) {
- $this->convert = $param['convert'];
- }
- }
-
- public function init()
- {
- // 执行路由后置操作
- if ($this->rule->doAfter()) {
- // 设置请求的路由信息
-
- // 设置当前请求的参数
- $this->request->setRouteVars($this->rule->getVars());
- $this->request->routeInfo([
- 'rule' => $this->rule->getRule(),
- 'route' => $this->rule->getRoute(),
- 'option' => $this->rule->getOption(),
- 'var' => $this->rule->getVars(),
- ]);
-
- $this->doRouteAfter();
- }
-
- return $this;
- }
-
- /**
- * 检查路由后置操作
- * @access protected
- * @return void
- */
- protected function doRouteAfter()
- {
- // 记录匹配的路由信息
- $option = $this->rule->getOption();
- $matches = $this->rule->getVars();
-
- // 添加中间件
- if (!empty($option['middleware'])) {
- $this->app['middleware']->import($option['middleware']);
- }
-
- // 绑定模型数据
- if (!empty($option['model'])) {
- $this->createBindModel($option['model'], $matches);
- }
-
- // 指定Header数据
- if (!empty($option['header'])) {
- $header = $option['header'];
- $this->app['hook']->add('response_send', function ($response) use ($header) {
- $response->header($header);
- });
- }
-
- // 指定Response响应数据
- if (!empty($option['response'])) {
- foreach ($option['response'] as $response) {
- $this->app['hook']->add('response_send', $response);
- }
- }
-
- // 开启请求缓存
- if (isset($option['cache']) && $this->request->isGet()) {
- $this->parseRequestCache($option['cache']);
- }
-
- if (!empty($option['append'])) {
- $this->request->setRouteVars($option['append']);
- }
- }
-
- /**
- * 执行路由调度
- * @access public
- * @return mixed
- */
- public function run()
- {
- $option = $this->rule->getOption();
-
- // 检测路由after行为
- if (!empty($option['after'])) {
- $dispatch = $this->checkAfter($option['after']);
-
- if ($dispatch instanceof Response) {
- return $dispatch;
- }
- }
-
- // 数据自动验证
- if (isset($option['validate'])) {
- $this->autoValidate($option['validate']);
- }
-
- $data = $this->exec();
-
- return $this->autoResponse($data);
- }
-
- protected function autoResponse($data)
- {
- if ($data instanceof Response) {
- $response = $data;
- } elseif ($data instanceof ResponseInterface) {
- $response = Response::create((string) $data->getBody(), 'html', $data->getStatusCode());
-
- foreach ($data->getHeaders() as $header => $values) {
- $response->header([$header => implode(", ", $values)]);
- }
- } elseif (!is_null($data)) {
- // 默认自动识别响应输出类型
- $isAjax = $this->request->isAjax();
- $type = $isAjax ? $this->rule->getConfig('default_ajax_return') : $this->rule->getConfig('default_return_type');
-
- $response = Response::create($data, $type);
- } else {
- $data = ob_get_clean();
- $content = false === $data ? '' : $data;
- $status = '' === $content && $this->request->isJson() ? 204 : 200;
-
- $response = Response::create($content, '', $status);
- }
-
- return $response;
- }
-
- /**
- * 检查路由后置行为
- * @access protected
- * @param mixed $after 后置行为
- * @return mixed
- */
- protected function checkAfter($after)
- {
- $this->app['log']->notice('路由后置行为建议使用中间件替代!');
-
- $hook = $this->app['hook'];
-
- $result = null;
-
- foreach ((array) $after as $behavior) {
- $result = $hook->exec($behavior);
-
- if (!is_null($result)) {
- break;
- }
- }
-
- // 路由规则重定向
- if ($result instanceof Response) {
- return $result;
- }
-
- return false;
- }
-
- /**
- * 验证数据
- * @access protected
- * @param array $option
- * @return void
- * @throws ValidateException
- */
- protected function autoValidate($option)
- {
- list($validate, $scene, $message, $batch) = $option;
-
- if (is_array($validate)) {
- // 指定验证规则
- $v = $this->app->validate();
- $v->rule($validate);
- } else {
- // 调用验证器
- $v = $this->app->validate($validate);
- if (!empty($scene)) {
- $v->scene($scene);
- }
- }
-
- if (!empty($message)) {
- $v->message($message);
- }
-
- // 批量验证
- if ($batch) {
- $v->batch(true);
- }
-
- if (!$v->check($this->request->param())) {
- throw new ValidateException($v->getError());
- }
- }
-
- /**
- * 处理路由请求缓存
- * @access protected
- * @param Request $request 请求对象
- * @param string|array $cache 路由缓存
- * @return void
- */
- protected function parseRequestCache($cache)
- {
- if (is_array($cache)) {
- list($key, $expire, $tag) = array_pad($cache, 3, null);
- } else {
- $key = str_replace('|', '/', $this->request->url());
- $expire = $cache;
- $tag = null;
- }
-
- $cache = $this->request->cache($key, $expire, $tag);
- $this->app->setResponseCache($cache);
- }
-
- /**
- * 路由绑定模型实例
- * @access protected
- * @param array|\Clousre $bindModel 绑定模型
- * @param array $matches 路由变量
- * @return void
- */
- protected function createBindModel($bindModel, $matches)
- {
- foreach ($bindModel as $key => $val) {
- if ($val instanceof \Closure) {
- $result = $this->app->invokeFunction($val, $matches);
- } else {
- $fields = explode('&', $key);
-
- if (is_array($val)) {
- list($model, $exception) = $val;
- } else {
- $model = $val;
- $exception = true;
- }
-
- $where = [];
- $match = true;
-
- foreach ($fields as $field) {
- if (!isset($matches[$field])) {
- $match = false;
- break;
- } else {
- $where[] = [$field, '=', $matches[$field]];
- }
- }
-
- if ($match) {
- $query = strpos($model, '\\') ? $model::where($where) : $this->app->model($model)->where($where);
- $result = $query->failException($exception)->find();
- }
- }
-
- if (!empty($result)) {
- // 注入容器
- $this->app->instance(get_class($result), $result);
- }
- }
- }
-
- public function convert($convert)
- {
- $this->convert = $convert;
-
- return $this;
- }
-
- public function getDispatch()
- {
- return $this->dispatch;
- }
-
- public function getParam()
- {
- return $this->param;
- }
-
- abstract public function exec();
-
- public function __sleep()
- {
- return ['rule', 'dispatch', 'convert', 'param', 'code', 'controller', 'actionName'];
- }
-
- public function __wakeup()
- {
- $this->app = Container::pull('app');
- $this->request = $this->app['request'];
- }
-
- public function __debugInfo()
- {
- $data = get_object_vars($this);
- unset($data['app'], $data['request'], $data['rule']);
-
- return $data;
- }
- }
|