Răsfoiți Sursa

Add middleware

朱金辉 2 ani în urmă
părinte
comite
d6755fa4f5

+ 6 - 0
README.md

@@ -362,4 +362,10 @@ composer.json:
 			$this->cache->remove($cache_id);
 		}
 	}
+	Middleware使用:
+	$app = \Qii::getInstance();
+	全局middleware,所有请求过来以后都会先执行
+	$app->setGlobalMiddle('\middleware\site');
+	route路由middleware,见demo中的使用
+	$app->run();
 * 更多好的方法见方法提供的文件示例或***demo***文件。

+ 9 - 0
demo/private/configure/route/admin.php

@@ -0,0 +1,9 @@
+<?php
+use Qii\Base\Route;
+
+Route::prefix("/admin")->setMiddleware(['\middleware\admin'])->group(function($route){
+    $route->get("/user/login", function(){return "admin";});
+    $route->get('/book/info', function(){return 'book/info';});
+    $route->get('/chapter/edit', ['admin\chapter\edit', 'index']);
+    $route->put('/chapter/add', 'admin\chapter\add');
+});

+ 10 - 1
demo/private/controller/index.php

@@ -26,7 +26,16 @@ class index extends \Qii\Base\Controller
         
         return new \Qii\Base\Response(array('format' => 'json', 'body' => $data));
     }
-    
+
+    /**
+     * 设置controller中间件,此中间件将会在controller初始化之前使用
+     * @return array
+     */
+    public function getMiddleware()
+    {
+        return parent::getMiddleware(); // TODO: Change the autogenerated stub
+    }
+
     public function dispatchAction()
     {
         echo "<p>Dispatch start ". __CLASS__ ."</p>";

+ 22 - 0
demo/private/middleware/admin.php

@@ -0,0 +1,22 @@
+<?php
+namespace middleware;
+
+use Qii\Base\Middleware\Request as Middleware;
+use Qii\Base\Request;
+use Closure;
+
+class admin implements Middleware
+{
+
+    public function __construct()
+    {
+    }
+    public function handle(Request $request, Closure $next) {
+        $isValid = false;
+        if (!$isValid) {
+            echo '管理员登录后重试';
+            return false;
+        }
+        return $next($request);
+    }
+}

+ 19 - 0
demo/private/middleware/auth.php

@@ -0,0 +1,19 @@
+<?php
+namespace middleware;
+
+use Closure;
+use Qii\Base\Middleware\Request as Middleware;
+use \Qii\Base\Request;
+
+class auth implements Middleware
+{
+    public function __construct()
+    {
+    }
+    public function handle(Request $request, Closure $next) {
+        if (!$_SESSION['admin']) {
+            return "This need login <br />";
+        }
+        return $next($request);
+    }
+}

+ 17 - 0
demo/private/middleware/site.php

@@ -0,0 +1,17 @@
+<?php
+namespace middleware;
+
+use Closure;
+use Qii\Base\Middleware\Request as Middleware;
+use \Qii\Base\Request;
+
+class site implements Middleware
+{
+    public function __construct(){
+
+    }
+    public function handle(Request $request, Closure $next) {
+        echo "This is global middleware";
+        return $next($request);
+    }
+}

+ 38 - 11
demo/public/index.php

@@ -1,14 +1,41 @@
 <?php
 require_once('../../src/Qii.php');
-//composer安装的将上边那行换成 require("../../vendor/autoload.php");
-
+//composer安装的将上边那行 require_once('../../src/Qii.php'); 换成 require("../../vendor/autoload.php");
 $app = \Qii::getInstance();
-$app->setWorkspace('../private')
-->setCachePath('tmp')
-->setAppConfigure('configure/app.ini')
-->setUseNamespace('Bootstrap', false)
-->setLogger('plugins\logger')
-->setDB('configure/db.ini')
-->setRouter('configure/router.config.php')
-->setBootstrap()
-->run();
+
+//程序所在目录,不用暴露在外网上
+$app->setWorkspace('../private');
+//临时文件写入目录
+$app->setCachePath('tmp');
+//app相关配置
+$app->setAppConfigure('configure/app.ini');
+
+//设置指定前缀是否使用namespace,仅限于使用框架本省的autoload
+$app->setUseNamespace('Bootstrap', false);
+//设置logger,用于错误日志收集
+$app->setLogger('plugins\logger');
+//设置数据库配置文件
+$app->setDB('configure/db.ini');
+
+//设置路由
+Route::prefix("/admin")->setMiddleware(['\middleware\admin'])->group(function($route){
+    $route->get("/user/login", function(){return "admin";});
+    $route->get('/book/info', function(){return 'book/info';});
+    $route->get('/chapter/edit', ['admin\chapter\edit', 'index']);
+    $route->put('/chapter/add', 'admin\chapter\add');
+});
+
+Route::prefix("/user")->setMiddleware(['\middleware\auth'])->group(function($route) {
+    $route->get('/profile', function(){return 'login first';});
+    $route->get('/index', function(){return 'this is user center';});
+});
+//或者从目录中加载
+$app->loadRouteFromPath('../private/configure/route');
+//设置路由规则
+$app->setRouter('configure/router.config.php');
+//设置启动执行的代码,可以用于初始化需要用到的东西
+$app->setBootstrap();
+//设置全局中间件
+$app->setGlobalMiddleware(['\middleware\site']);
+//运行
+$app->run();

+ 104 - 31
src/Application.php

@@ -1,5 +1,4 @@
 <?php
-
 namespace Qii;
 
 use Qii\Base\Bootstrap;
@@ -14,8 +13,10 @@ use \Qii\Config\Register;
 use \Qii\Config\Consts;
 use \Qii\Config\Setting;
 use Qii\Exceptions\ClassInstanceof;
+use Qii\Exceptions\ClassNotFound;
 use Qii\Exceptions\FileNotFound;
 use Qii\Exceptions\FolderDoesNotExist;
+use Qii\Request\Url;
 
 class Application
 {
@@ -40,8 +41,13 @@ class Application
     /**
      * @var array $paths 网站使用的路径
      */
-    public static $paths = array('configure', 'controller', 'model', 'view', 'plugins', 'tmp');
+    public static $paths = array('configure', 'controller', 'model', 'middleware', 'helper', 'view', 'plugins', 'tmp');
 
+    /**
+     * 系统注册的 router
+     * @var array
+     */
+    private $router = [];
     /**
      * Qii\Request\Url
      */
@@ -54,6 +60,15 @@ class Application
      * @var mixed $helper
      */
     public $helper;
+    /**
+     * 全局middleware
+     * @var array
+     */
+    private $middleware = [];
+    /**
+     * 路由middleware
+     * @var array middleware
+     */
 
     public function __construct()
     {
@@ -189,6 +204,11 @@ class Application
         if (!Register::setAppConfigure($ini, $env)) throw new FileNotFound($ini, 404);
         //载入request方法
         $this->request = Psr4::getInstance()->loadClass('\Qii\Request\Http');
+        $this->helper->load(self::$workspace);
+        $this->dispatcher = $this->setDispatcher();
+        if (!$this->dispatcher instanceof Dispatcher) {
+            throw new \Exception('Dispatcher must instance of Qii\Base\Dispatcher', __LINE__);
+        }
         Setting::getInstance()->setDefaultTimeZone();
         Setting::getInstance()->setDefaultControllerAction();
         Setting::getInstance()->setDefaultNamespace();
@@ -260,7 +280,7 @@ class Application
     public function setBootstrap()
     {
         Psr4::getInstance()->loadFileByClass('Bootstrap');
-        if (!class_exists('Bootstrap', false)) throw new \Qii\Exceptions\ClassNotFound(\Qii::i(1405, 'Bootstrap'), __LINE__);;
+        if (!class_exists('Bootstrap', false)) throw new ClassNotFound(\Qii::i(1405, 'Bootstrap'), __LINE__);;
         $bootstrap = Psr4::getInstance()->instance('Bootstrap');
         if (!$bootstrap instanceof Bootstrap) {
             throw new ClassInstanceof(Qii::i(1107, 'Bootstrap', 'Qii\Bootstrap'), __LINE__);;
@@ -274,7 +294,29 @@ class Application
         }
         return $this;
     }
-
+    /**
+     * 全局 Middleware
+     * @param array $middleware
+     * @return void
+     */
+    public function setGlobalMiddleware($middleware) {
+        if (!is_array($middleware)) {
+            return $this;
+        }
+        $this->middleware = array_merge($this->middleware, $middleware);
+        return $this;
+    }
+    /**
+     * Set Dispatcher
+     * @return void
+     * @throws ClassNotFound
+     */
+    public function setDispatcher() {
+        if ($this->dispatcher !== null) {
+            return $this->dispatcher;
+        }
+        return Psr4::getInstance()->loadClass('\Qii\Base\Dispatcher');
+    }
     /**
      * 设置写loger的类
      *
@@ -391,38 +433,31 @@ class Application
         return $this;
     }
 
+    /**
+     * @param string $path 从指定路径加载路由
+     *
+     * @return $this
+     */
+    public function loadRouteFromPath($path) {
+        Import::requireByDir($path);
+        return $this;
+    }
     /**
      * 设置路由规则
      *
      * @param string $router 路由配置文件位置
-     * @return $this
+     * @return mixed
      */
     public function setRouter($router)
     {
+        $router = Import::includes(Psr4::realpath(Psr4::getInstance()->getFileByPrefix($router)));
+        if(is_array($router)) {
+            $this->router = array_merge($this->router, $router);
+        }
         Register::set(
             Consts::APP_SITE_ROUTER,
-            Import::includes(
-                Psr4::realpath(Psr4::getInstance()->getFileByPrefix($router)
-                )
-            )
-        );
-        //载入rewrite规则后重写request
-        $rewrite = Psr4::loadStatic(
-            '\Qii\Router\Parse',
-            'get',
-            \Qii\Request\Url::getPathInfo(),
-            $this->request->controller,
-            $this->request->action,
-            $this->request->url->get(2)
+            $this->router
         );
-        $rewrite['controller'] = $rewrite['controller'] ? $rewrite['controller'] : $this->request->defaultController();
-        $rewrite['action'] = $rewrite['action'] ? $rewrite['action'] : $this->request->defaultAction();
-        //是否已经rewrite,如果和url中的不一样就是已经rewrite
-        if ($this->request->controller != $rewrite['controller'] || $this->request->action != $rewrite['action']) {
-            $this->request->setRouted(true);
-        }
-        $this->request->setControllerName($rewrite['controller']);
-        $this->request->setActionName($rewrite['action']);
         return $this;
     }
 
@@ -445,10 +480,48 @@ class Application
      */
     public function run()
     {
-        $this->helper->load(self::$workspace);
-        $this->dispatcher = Psr4::getInstance()->loadClass('\Qii\Base\Dispatcher');
-        if (!$this->dispatcher instanceof \Qii\Base\Dispatcher) {
-            throw new \Exception('Dispatcher must instance of Qii\Base\Dispatcher', __LINE__);
+        //全局middleware
+        $reserve = array_unique(array_reverse($this->middleware));
+        $next = array_reduce($reserve,function ($carry, $item){
+            return function () use ($carry, $item){
+                return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
+            };
+        }, function(){
+            return true;
+        })();
+        if ($next === false) {
+            return $this;
+        }
+        //route middleware
+        $next = call_user_func(
+            array_reduce(['Route'], function($carry, $item){
+                return function () use ($carry, $item){
+                    return \Qii::getInstance("Qii\Base\\". $item)->match($this->request, $carry);
+                };
+            }, function(){
+                //载入rewrite规则后重写request
+                $rewrite = Psr4::loadStatic(
+                    '\Qii\Router\Parse',
+                    'get',
+                    Url::getPathInfo(),
+                    $this->request->controller,
+                    $this->request->action,
+                    $this->request->url->get(2)
+                );
+                $rewrite['controller'] = isset($rewrite['controller']) && $rewrite['controller'] ? $rewrite['controller'] : $this->request->defaultController();
+                $rewrite['action'] = isset($rewrite['action']) && $rewrite['action'] ? $rewrite['action'] : $this->request->defaultAction();
+
+                //是否已经rewrite,如果和url中的不一样就是已经rewrite
+                if ($this->request->controller != $rewrite['controller'] || $this->request->action != $rewrite['action']) {
+                    $this->request->setRouted(true);
+                }
+                $this->request->setControllerName($rewrite['controller']);
+                $this->request->setActionName($rewrite['action']);
+                return true;
+            })
+        );
+        if ($next === false) {
+            return $this;
         }
         //如果app.ini中设置了host的话,看host对应的controller路径
         $hosts = $this->appConfigure('hosts');
@@ -457,7 +530,7 @@ class Application
                 if ($host['domain'] == $this->request->host) {
                     Register::set(
                         Consts::APP_DEFAULT_CONTROLLER_PREFIX,
-                        ($host['path'] ? $host['path'] : $host['domain'])
+                        (isset($host['path']) && $host['path'] ? $host['path'] : $host['domain'])
                     );
                     break;
                 }

+ 5 - 3
src/Autoloader/Import.php

@@ -1,6 +1,8 @@
 <?php
 namespace Qii\Autoloader;
 
+use Qii\Exceptions\FileNotFound;
+
 class Import
 {
     const VERSION = '1.3';
@@ -50,7 +52,7 @@ class Import
             return $configure;
         }else{
             //print_r(debug_backtrace());
-            throw new \Qii\Exceptions\FileNotFound($file, 404);
+            throw new FileNotFound($file, 404);
         }
         return false;
     }
@@ -63,7 +65,7 @@ class Import
      */
     public static function requireByClass($className)
     {
-        return \Qii\Autoloader\Psr4::getInstance()->loadFileByClass($className);
+        return Psr4::getInstance()->loadFileByClass($className);
     }
 
     /**
@@ -74,7 +76,7 @@ class Import
      */
     public static function requireByDir($dir)
     {
-        if (!is_dir($dir)) throw new \Qii\Exceptions\FileNotFound($dir, 404);
+        if (!is_dir($dir)) throw new FileNotFound($dir, 404);
         $files = self::findFiles($dir, array('php'));
         if (isset($files['php'])) self::requires($files['php']);
     }

+ 2 - 2
src/Autoloader/Instance.php

@@ -65,8 +65,8 @@ class Instance
     {
         $args = func_get_args();
         $className = array_shift($args);
-        $className = \Qii\Autoloader\Psr4::getInstance()->getClassName($className);
-        \Qii\Autoloader\Psr4::getInstance()->loadFileByClass($className);
+        $className = Psr4::getInstance()->getClassName($className);
+        Psr4::getInstance()->loadFileByClass($className);
         return call_user_func_array(array('\Qii\Autoloader\Instance', 'initialize'), array_merge(array($className), $args));
     }
 }

+ 5 - 3
src/Autoloader/Psr4.php

@@ -79,7 +79,7 @@ class Psr4
      */
     public function register()
     {
-        spl_autoload_register(array($this, 'loadFileByClass'));
+        spl_autoload_register(array($this, 'loadFileByClass'), true, true);
         return $this;
     }
     /**
@@ -321,11 +321,13 @@ class Psr4
     public function loadClass($class)
     {
         $args = func_get_args();
+        if (count($args) == 0) {
+            return;
+        }
         //去掉第一个斜杠
-        $class = array_shift($args);
+        $class = $args[0];
         $class = preg_replace("/^\\\\/", "", $class);
         $class = $this->getClassName($class);
-        array_unshift($args, $class);
 
         if (class_exists($class, false)) {
             return call_user_func_array(array($this, 'instance'), $args);

+ 9 - 3
src/Base/Controller.php

@@ -69,7 +69,7 @@ abstract class Controller
      * @var bool
      */
     public $enableDB = false;
-    
+
     public function __construct()
     {
         $this->load = Psr4::getInstance()->loadClass('\Qii\Autoloader\Loader');
@@ -202,7 +202,13 @@ abstract class Controller
     {
         return true;
     }
-    
+    /**
+     * 获取 controller middleware
+     * @return array
+     */
+    public function getMiddleware() {
+        return array();
+    }
     /**
      * 执行完dispatch后调用
      */
@@ -248,7 +254,7 @@ abstract class Controller
     
     /**
      * 获取 request 方法
-     * @return Qii_Request_Http
+     * @return Qii\Request\Http
      */
     final public function getRequest()
     {

+ 92 - 39
src/Base/Dispatcher.php

@@ -3,6 +3,8 @@ namespace Qii\Base;
 
 use \Qii\Config\Register;
 use \Qii\Config\Consts;
+use Qii\Exceptions\MethodNotFound;
+use Qii\Request\Http;
 
 class Dispatcher
 {
@@ -12,20 +14,62 @@ class Dispatcher
 
     public $actionCls = null;
 
+    private $requestMiddleWare = array();
+
     public function __construct()
     {
 
     }
     /**
      * 设置请求
-     * @param \Qii\Request\Http $request 当前请求
+     * @param Http $request 当前请求
      */
-    public function setRequest(\Qii\Request\Http $request)
+    public function setRequest(Http $request)
     {
         $this->request = $request;
         return $this;
     }
 
+    /**
+     * 设置中间件
+     *
+     * @param array $middleware 中间件
+     * @return $this
+     */
+    public function setMiddleware($middleware) {
+        if (!is_array($middleware)) {
+            return $this;
+        }
+        $this->requestMiddleWare = array_merge($this->requestMiddleWare, $middleware);
+        return $this;
+    }
+
+    /**
+     * 获取 controller 设置的middleware
+     * @param $controller
+     * @return $this
+     */
+    public function getMiddleWare($controller)
+    {
+        if (method_exists($controller, 'getMiddleware')) {
+            $this->requestMiddleWare = array_merge($this->requestMiddleWare, $controller->getMiddleware());
+        }
+        return $this;
+    }
+
+    /**
+     * 整理 middleware
+     * @return $this
+     */
+    public function gatherMiddleware()
+    {
+        if(count($this->requestMiddleWare) == 0) {
+            return $this;
+        }
+        $this->requestMiddleWare = array_unique($this->requestMiddleWare);
+        return $this;
+    }
+
     /**
      * 转发
      * @param string $controller
@@ -43,6 +87,7 @@ class Dispatcher
         if(substr($controller,0, 1) != '\\') {
             $controllerName = Register::get(Consts::APP_DEFAULT_CONTROLLER_PREFIX) . '_' . $controller;
         }
+
         $funcArgs = array();
         if (count($args) > 2) {
             $funcArgs = array_slice($args, 2);
@@ -51,52 +96,60 @@ class Dispatcher
         $psr4 = \Qii\Autoloader\Psr4::getInstance();
         $controllerCls = call_user_func_array(array($psr4, 'loadClass'), $funcArgs);
 
-        $method = new \ReflectionMethod($this, 'dispatch');
-        foreach($method->getParameters() as $property)
-        {
-        	$param = $property->getName();
-        	$this->request->setParam($param, $$param);
-        }
-        $this->controllerCls = $controllerCls;
-        $this->controllerCls->setRequest($this->request);
-        $this->controllerCls->controller = $controllerCls;
-        $this->controllerCls->controllerId = $controller;
-        $this->controllerCls->actionId = $action;
-        $response = null;
-        //查看是否设置了当前action的对应关系,如果设置了就走对应关系里边的,否则走当前类中的
-        if ($this->controllerCls->actions && isset($this->controllerCls->actions[$action]) && $this->controllerCls->actions[$action]) {
-            $actionArgs = array();
-            $actionArgs[] = $this->controllerCls->actions[$action];
-            $actionCls = call_user_func_array(array($psr4, 'loadClass'), $actionArgs);
-            $this->actionCls = $actionCls;
-            $this->actionCls->setRequest($this->request);
-            $this->actionCls->controller = $this->controllerCls;
-            $this->actionCls->actionId = $action;
-            $this->actionCls->controllerId = $this->controllerCls->controllerId;
-            //支持多个action对应到同一个文件,如果对应的文件中存在指定的方法就直接调用
-            if (method_exists($this->actionCls, $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX))) {
-                $this->actionCls->response = $response = call_user_func_array(array($this->actionCls, $action. Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX)), $funcArgs);
-            }
-            if(method_exists($this->actionCls, 'initialization'))
+        /**middleWare**/
+        $this->getMiddleWare($controllerCls)->gatherMiddleware();
+        $pipeline = array_reduce($this->requestMiddleWare,function ($carry, $item){
+            return function () use ($carry, $item){
+                return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
+            };
+        }, function($funcArgs, $controllerCls, $psr4, $controller, $action){$method = new \ReflectionMethod($this, 'dispatch');
+            foreach($method->getParameters() as $property)
             {
-                call_user_func_array(array($this->actionCls, 'initialization'), array($this->actionCls));
+                $param = $property->getName();
+                $this->request->setParam($param, $$param);
             }
-            if (!method_exists($this->actionCls, 'run')) {
-                throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $this->controllerCls->actions[$action] . '->run'), __LINE__);
+            $this->controllerCls = $controllerCls;
+            $this->controllerCls->setRequest($this->request);
+            $this->controllerCls->controller = $controllerCls;
+            $this->controllerCls->controllerId = $controller;
+            $this->controllerCls->actionId = $action;
+
+            //查看是否设置了当前action的对应关系,如果设置了就走对应关系里边的,否则走当前类中的
+            if ($this->controllerCls->actions
+                && isset($this->controllerCls->actions[$action])
+                && $this->controllerCls->actions[$action]) {
+                $actionArgs = array();
+                $actionArgs[] = $this->controllerCls->actions[$action];
+                $actionCls = call_user_func_array(array($psr4, 'loadClass'), $actionArgs);
+                $this->actionCls = $actionCls;
+                $this->actionCls->setRequest($this->request);
+                $this->actionCls->controller = $this->controllerCls;
+                $this->actionCls->actionId = $action;
+                $this->actionCls->controllerId = $this->controllerCls->controllerId;
+                //支持多个action对应到同一个文件,如果对应的文件中存在指定的方法就直接调用
+                if (method_exists($this->actionCls, $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX))) {
+                    $this->actionCls->response = $response = call_user_func_array(array($this->actionCls, $action. Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX)), $funcArgs);
+                }
+                if(method_exists($this->actionCls, 'initialization')) {
+                    call_user_func_array(array($this->actionCls, 'initialization'), array($this->actionCls));
+                }
+                if (!method_exists($this->actionCls, 'run')) {
+                    throw new MethodNotFound(\Qii::i(1101, $this->controllerCls->actions[$action] . '->run'), __LINE__);
+                }
+                return call_user_func_array(array($this->actionCls, 'run'), array_slice($funcArgs, 1));
             }
-            $response = call_user_func_array(array($this->actionCls, 'run'), array_slice($funcArgs, 1));
-        } else {
-            if(method_exists($this->controllerCls, 'initialization'))
-            {
+
+            if(method_exists($this->controllerCls, 'initialization')) {
                 call_user_func_array(array($this->controllerCls, 'initialization'), array($this->controllerCls));
             }
             array_shift($funcArgs);
             $actionName = $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);
             if (!method_exists($this->controllerCls, $actionName) && !method_exists($this->controllerCls, '__call')) {
-                throw new \Qii\Exceptions\MethodNotFound(\Qii::i(1101, $controller . '->' . $actionName), __LINE__);
+                throw new MethodNotFound(\Qii::i(1101, $controller . '->' . $actionName), __LINE__);
             }
             $this->controllerCls->response = $response = call_user_func_array(array($this->controllerCls, $actionName), $funcArgs);
-        }
-        return $response;
+            return $response;
+        });
+        return call_user_func($pipeline, $funcArgs, $controllerCls, $psr4, $controller, $action);
     }
 }

+ 22 - 0
src/Base/Middleware.php

@@ -0,0 +1,22 @@
+<?php
+namespace Qii\Base;
+
+use Closure;
+
+/**
+ * middleware 中间件
+ */
+class Middleware {
+    /**
+     * @var array $middleware 全局middleware
+     */
+    public $middleware = [];
+    /**
+     * @var array $middlewareGroup 中间件
+     */
+    public $middlewareGroup = [];
+    /**
+     * @var array route route中间件
+     */
+    public $routeMiddleware = [];
+}

+ 18 - 0
src/Base/Middleware/Request.php

@@ -0,0 +1,18 @@
+<?php
+namespace Qii\Base\Middleware;
+
+use Closure;
+use \Qii\Base\Request as MiddlewareQuery;
+/**
+ * middleware 中间件
+ */
+interface Request {
+    /**
+     * request middleware 当return false的时候,就会阻止程序往下运行
+     *
+     * @param MiddlewareQuery $request
+     * @param Closure $next
+     * @return mixed
+     */
+    public function handle(MiddlewareQuery $request, Closure $next);
+}

+ 8 - 7
src/Base/Request.php

@@ -2,10 +2,11 @@
 
 namespace Qii\Base;
 
+use Qii\Config\Consts;
+use Qii\Config\Register;
+
 abstract class Request
 {
-    const SCHEME_HTTP = 'http';
-    const SCHEME_HTTPS = 'https';
     public $host = '';
     public $module;
     public $controller;
@@ -44,7 +45,7 @@ abstract class Request
         foreach ($_GET AS $key => $val) {
             $this->setParam($key, $val);
         }
-        $rewriteRule = \Qii::getInstance()->appConfigure(\Qii\Config\Consts::APP_SITE_METHOD);
+        $rewriteRule = \Qii::getInstance()->appConfigure(Consts::APP_SITE_METHOD);
         $this->url = new \Qii\Request\Url($rewriteRule);
         $this->host = IS_CLI ? '' : $_SERVER['HTTP_HOST'];
         $params = (array)$this->url->getParams();
@@ -85,7 +86,7 @@ abstract class Request
      */
     public function defaultController()
     {
-        return \Qii\Config\Register::get(\Qii\Config\Consts::APP_DEFAULT_CONTROLLER, 'index');
+        return Register::get(Consts::APP_DEFAULT_CONTROLLER, 'index');
     }
 
     /**
@@ -93,7 +94,7 @@ abstract class Request
      */
     public function defaultAction()
     {
-        return \Qii\Config\Register::get(\Qii\Config\Consts::APP_DEFAULT_ACTION, 'index');
+        return Register::get(Consts::APP_DEFAULT_ACTION, 'index');
     }
 
     /**
@@ -501,7 +502,7 @@ abstract class Request
     /**
      * 设置dispatcher
      *
-     * @param Qii_Controller_Dispatcher $dispatcher
+     * @param \Qii\Controller\Dispatcher $dispatcher
      */
     public function setDispatcher(\Qii\Base\Dispatcher $dispatcher)
     {
@@ -523,7 +524,7 @@ abstract class Request
      * setRouted
      *
      * @param boolean $flag
-     * @return boolean | Qii_Request_Abstract
+     * @return boolean | \Qii\Request\Abstract
      */
     public function setRouted($flag = true)
     {

+ 219 - 0
src/Base/Route.php

@@ -0,0 +1,219 @@
+<?php
+namespace Qii\Base;
+
+use Qii\Request\Http;
+
+/**
+ * Route
+ */
+class Route
+{
+    /**
+     * group 路由前缀
+     * @var string
+     */
+    public $prefix = '';
+    /**
+     * 路由 middleware
+     * @var array
+     */
+    public $middleware = [];
+    /**
+     * 路由
+     * @var array
+     */
+    private $route = [];
+    /**
+     * 存储转换后的路由和middleware
+     *
+     * @var array $stored
+     */
+    private $stored = [];
+
+    public function __construct() {
+        $this->clean();
+    }
+
+    /**
+     * 清理middleware和路由规则
+     *
+     * @return void
+     */
+    public function clean() {
+        $this->prefix = '';
+        $this->middleware = [];
+        $this->route = [
+            'GET' => [],
+            'POST' => [],
+            'DELETE' => [],
+            'PUT' => [],
+            'HEAD' => [],
+            'ANY' => []
+        ];
+    }
+
+    public static function getInstance() {
+        return \Qii::getInstance("Qii\Base\Route");
+    }
+
+    public function setRoute($route){
+        if (!is_array($route) && !in_array($route, $this->route)) {
+            $this->route[] = $route;
+        }
+        $this->route = array_merge($this->route, $route);
+    }
+
+    public static function prefix($name) {
+        $route = self::getInstance();
+        $route->clean();
+        $route->prefix = $name;
+        return $route;
+    }
+
+    /**
+     * 设置中间件
+     * @param array|string $middleware
+     * @return $this
+     */
+    public function setMiddleware($middleware) {
+        if(!is_array($middleware)) {
+            $this->middleware[] = $middleware;
+        }else{
+            $this->middleware = array_merge($this->middleware, $middleware);
+        }
+        return $this;
+    }
+
+    public function gatherMiddleware(){
+        return array_unique($this->middleware);
+    }
+
+    public function group($callable) {
+        $route = \Qii::getInstance("Qii\Base\Route");
+        call_user_func($callable, $route);
+        $this->stored[] = [$route->middleware, $route->route];
+        return $this;
+    }
+
+    public function get($uri, $callable){
+        $uri = $this->appendPrefix($this->prefix, $uri);
+        $this->route['GET'][] = [$uri, $callable];
+        return $this;
+    }
+
+    public function post($uri, $callable) {
+        $uri = $this->appendPrefix($this->prefix, $uri);
+        $this->route['POST'][] = [$uri, $callable];
+        return $this;
+    }
+
+    /**
+     * 补充前缀
+     * @param string $prefix 前缀
+     * @param string $uri uri
+     * @return mixed|string
+     */
+    protected function appendPrefix($prefix, $uri){
+        if(!$prefix) {
+            return $uri;
+        }
+        return $prefix . '/'. ltrim($uri, '/');;
+    }
+    public function put($uri, $callable) {
+        $uri = $this->appendPrefix($this->prefix, $uri);
+        $this->route['PUT'][] = [$uri, $callable];
+        return $this;
+    }
+
+    public function delete($uri, $callable) {
+        $uri = $this->appendPrefix($this->prefix, $uri);
+        $this->route['DELETE'][] = [$uri, $callable];
+        return $this;
+    }
+
+    public function any($uri, $callable) {
+        $uri = $this->appendPrefix($this->prefix, $uri);
+        $this->route['ANY'][] = [$uri, $callable];
+        return $this;
+    }
+
+    /**
+     * 匹配路由并执行
+     *
+     * @param $request
+     * @return bool
+     */
+    public function match(Http $request) {
+        $route = $this->getStoredRoute();
+        $method = strtoupper($request->getMethod());
+        $uri = $request->getRequestUri();
+        $middleware = [];
+        $callable = '';
+        foreach ($route as $item) {
+            $middleware = $item[0];
+            $subRoute = $item[1];
+            if(!isset($subRoute[$method]) && !is_set($subRoute['ANY'])) {
+                continue;
+            }
+            $routes = array_merge($subRoute[$method], $subRoute['ANY']);
+            $callable = '';
+
+            foreach ($routes as $routeUri) {
+                if ($uri == $routeUri[0]) {
+                    $callable = $routeUri[1];
+                    break;
+                }
+            }
+            if ($callable) {
+                break;
+            }
+        }
+        if (!$callable || count($middleware) == 0) {
+            return true;
+        }
+        $middleware = array_reverse(array_unique($middleware));
+        $pipe = array_reduce($middleware, function ($carry, $item){
+            return function () use ($carry, $item){
+                return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
+            };
+        }, function(){
+            return true;
+        });
+        $result = call_user_func($pipe);
+        if (!$result) return false;
+        return $this->make($callable);
+    }
+
+    /**
+     * 执行 $callable
+     * @param mix $callable 执行方法
+     * @return bool
+     */
+    public function make($callable) {
+        switch ($callable) {
+            case ($callable instanceof  \Closure):
+                echo call_user_func($callable);
+                break;
+            case is_array($callable):
+                $action = (isset($callable[1]) ? $callable[1] : "index"). 'Action';
+                _loadClass($callable[0])->$action();
+                break;
+            case gettype($callable) == 'string':
+                _loadClass($callable)->indexAction();
+                break;
+        }
+        if ($callable) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 获取所有设置的路由
+     *
+     * @return mixed
+     */
+    public function getStoredRoute(){
+        return self::getInstance()->stored;
+    }
+}

+ 1 - 1
src/Driver/Observer.php

@@ -53,7 +53,7 @@ class Observer implements SplSubject
 		if (!isset($context) || !$context || !is_object($context)) throw new \Exception(\Qii::i(1003), __LINE__);
 		
 		$this->context = $context;
-		$this->observers = new \SplObjectStorage();
+		$this->observers = new SplObjectStorage();
 	}
 
 	/**

+ 3 - 5
src/Exceptions/Errors.php

@@ -50,7 +50,7 @@ class Errors extends \Exception
     public static function getError($e)
     {
         $message = array();
-        if (isset($_GET['isAjax']) && $_GET['isAjax'] == 1) {
+        if (\Qii::getInstance()->request->isXmlHttpRequest()) {
             $code = $e->getCode();
             if ($code == 0) $code = 1;
             echo json_encode(array('code' => $code, 'line' => $e->getFile() . ' line :' . $e->getLine(), 'msg' => strip_tags($e->getMessage())), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
@@ -92,11 +92,9 @@ class Errors extends \Exception
             \Qii::getInstance()->request->setActionName($action);
             //dispatcher是run 的时候初始化,所以这里可能会是空
             if (\Qii::getInstance()->dispatcher == null) {
-                \Qii::getInstance()->setDispatcher();
+                return \Qii::getInstance()->setDispatcher()->setRequest(\Qii::getInstance()->request)->dispatch($controller, $action, $e);
             }
-            \Qii::getInstance()->dispatcher->setRequest(\Qii::getInstance()->request);
-            \Qii::getInstance()->dispatcher->dispatch($controller, $action, $e);
-            die();
+            return \Qii::getInstance()->dispatcher->setRequest(\Qii::getInstance()->request)->dispatch($controller, $action, $e);
         }
         ob_start();
         include(join(DS, array(Qii_DIR, 'Exceptions', 'View', 'error.php')));

+ 0 - 2
src/Library/Http.php

@@ -17,8 +17,6 @@ namespace Qii\Library;
         ['hightman\http', Qii_DIR . DS .'Library'. DS . 'Third'. DS .'hightman'],
     ]);
 use hightman\http\Client;
-use hightman\http\Request;
-use hightman\http\Response;
 
 class Http extends Client
 {

+ 1 - 1
src/Library/PHPWord.php → src/Library/PHPSplitWord.php

@@ -2,6 +2,6 @@
 namespace Qii\Library;
 
 _require(Qii_DIR . "/Library/Third/PHPWord.php");
-class PHPWord extends PHPWord
+class PHPSplitWord extends PHPWord
 {
 }

+ 1 - 1
src/Qii.php

@@ -153,7 +153,7 @@ class Qii extends Application
      */
     public function __call($className, $args)
     {
-        return call_user_func_array(array(Psr4::getInstance(), 'loadClass'), $args);
+        return call_user_func_array(array(Psr4::getInstance(), 'loadClass'), func_get_args());
     }
 
     /**

+ 4 - 2
src/Response/Http.php

@@ -10,7 +10,7 @@ class Http extends \Qii\Base\Response
      * Set HTTP response code to use with headers
      *
      * @param int $code
-     * @return Qii_Response_Http
+     * @return Qii\Response\Http
      */
     public function setResponseCode($code)
     {
@@ -44,7 +44,9 @@ class Http extends \Qii\Base\Response
     protected function sendHeaders()
     {
         $httpCodeSent = false;
-
+        if (!$this->_sendheader) {
+            return $this;
+        }
         foreach ($this->_headers as $header) {
             if (!$httpCodeSent && $this->_responseCode) {
                 header(