| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 | <?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 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::get('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 (!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::get('app');        $this->request = $this->app['request'];    }    public function __debugInfo()    {        $data = get_object_vars($this);        unset($data['app'], $data['request'], $data['rule']);        return $data;    }}
 |