Quellcode durchsuchen

Merge branch 'master' of https://code.istudy.wang/root/Qii

* 'master' of https://code.istudy.wang/root/Qii:
  update
  update rules
  update request route response and funcs
  update model
  update model
  remove echo string
  update route
  fix warning
  fixed switch db bug
  fixed merge ini error
  Update: add static path and fix some bugs
  trim echo
  Update demo files
  Update: 更新 composer php 版本号
  update entity and fixed some bugs
  Update: fixed some error
  add set method for request
  update entity
  add remove method

# Conflicts:
#	composer.json
朱金辉 vor 3 Monaten
Ursprung
Commit
eabd69054d
48 geänderte Dateien mit 1489 neuen und 644 gelöschten Zeilen
  1. BIN
      .DS_Store
  2. 1 1
      composer.json
  3. 1 1
      demo/private/actions/database/creator.php
  4. 1 1
      demo/private/actions/database/rules.php
  5. 1 0
      demo/private/controller/api/database.php
  6. 3 0
      demo/private/controller/dirs.php
  7. 8 6
      demo/private/controller/index.php
  8. 2 2
      demo/private/helper/tools.php
  9. 1 1
      demo/private/middleware/site.php
  10. 9 9
      demo/private/model/table.php
  11. 5 0
      demo/public/index.php
  12. BIN
      src/.DS_Store
  13. 54 5
      src/Application.php
  14. 2 2
      src/Autoloader/Factory.php
  15. 3 2
      src/Autoloader/Import.php
  16. 13 6
      src/Autoloader/Psr4.php
  17. 0 5
      src/Base/Action.php
  18. 2 1
      src/Base/Controller.php
  19. 2 2
      src/Base/Dispatcher.php
  20. 61 24
      src/Base/Request.php
  21. 46 12
      src/Base/Response.php
  22. 274 16
      src/Base/Route.php
  23. 1 1
      src/Base/Rules.php
  24. 19 23
      src/Config/Register.php
  25. 58 21
      src/Driver/Base.php
  26. 3 3
      src/Driver/ConnBase.php
  27. 13 7
      src/Driver/Easy.php
  28. 533 125
      src/Driver/Entity/Base.php
  29. 161 7
      src/Driver/Entity/Entity.php
  30. 1 1
      src/Driver/Entity/Verify.php
  31. 10 7
      src/Driver/Model.php
  32. 8 8
      src/Driver/Mysql/Connection.php
  33. 8 8
      src/Driver/Mysqli/Connection.php
  34. 10 10
      src/Driver/Pdo/Connection.php
  35. 1 1
      src/Driver/Pdo/Driver.php
  36. 5 4
      src/Driver/TraitCache.php
  37. 34 9
      src/Driver/TraitDatabase.php
  38. 5 0
      src/Exceptions/Errors.php
  39. 40 2
      src/Functions/Funcs.php
  40. 0 1
      src/Library/Third/hightman/Client.php
  41. 3 2
      src/Library/Validate.php
  42. 39 7
      src/Qii.php
  43. 10 0
      src/Request/Http.php
  44. 0 220
      src/Request/Simple.php
  45. 10 42
      src/Request/Url/Base.php
  46. 23 8
      src/Request/Url/Short.php
  47. 0 28
      src/Response/Http.php
  48. 5 3
      src/Router/Parse/Normal.php

BIN
.DS_Store


+ 1 - 1
composer.json

@@ -19,7 +19,7 @@
     },
     "minimum-stability": "dev",
     "require": {
-        "php": ">=5.4",
+        "php": ">=5.5",
         "ext-gd" : ">=2.1.0"
     }
 }

+ 1 - 1
demo/private/actions/database/creator.php

@@ -32,7 +32,7 @@ class creator extends Action
 		$database = $this->request->get('database', $database);
 		try {
 			$databases = $this->controller->load->model('table')->getDatabases();
-			if (!$database && count($database) > 0) $database = $databases[0];
+			if (!$database && count($databases) > 0) $database = $databases[0];
 			$this->controller->view->assign('databases', $databases);
 			$tables = $this->controller->load->model('table')->getTableLists($database);
 			if (!$tableName && count($tables) > 0) {

+ 1 - 1
demo/private/actions/database/rules.php

@@ -32,7 +32,7 @@ class rules extends Action
 		$database = $this->request->get('database', $database);
 		try {
 			$databases = $this->controller->load->model('table')->getDatabases();
-			if (!$database && count($database) > 0) $database = $databases[0];
+			if (!$database && count($databases) > 0) $database = $databases[0];
 			$this->controller->view->assign('databases', $databases);
 			$tables = $this->controller->load->model('table')->getTableLists($database);
 			if (!$tableName && count($tables) > 0) {

+ 1 - 0
demo/private/controller/api/database.php

@@ -23,6 +23,7 @@ class database extends base
 		$database = $this->request->post('database');
 		$tableName = $this->request->post('tableName');
 		$rules = $this->request->post($tableName);
+
 		if (!$rules) {
 			$data['code'] = 1;
 			echo $this->jsonEncode($data);

+ 3 - 0
demo/private/controller/dirs.php

@@ -14,6 +14,9 @@ class dirs extends base
 	public function indexAction()
 	{
 		$path = $this->request->get('path', './');
+        if($path == '') {
+            $path = './';
+        }
 		if (!is_dir($path)) {
 			$this->showErrorPage('指定文件不存在');
 			return;

+ 8 - 6
demo/private/controller/index.php

@@ -8,13 +8,15 @@ class index extends \Qii\Base\Controller
     public function indexAction()
     {
         $html = [];
-        $html[] = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>示例程序</title><style>ul{list-style:none;}</style></head><body>';
+        $html[] = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>示例程序</title>';
+        $html[] = '<style>ul{list-style:none;font-size:16px;}a{text-decoration: none;color: #1F3D74}</style>';
+        $html[] = '</head><body>';
         $html[] = '<ul>示例程序';
-        $html[] = '<li>><a href="'. _link('dirs.html') .'">文件管理</a></li>';
-        $html[] = '<li>><a href="'. _link('database/creator.html') .'">数据表管理</a></li>';
-        $html[] = '<li>><a href="'. _link('index/dispatch.html') .'">Dispatch</a></li>';
-        $html[] = '<li>><a href="'. _link('index/forward.html') .'">Forward</a></li>';
-        $html[] = '<li>><a href="'. _link('index/display.html') .'">使用指定目录中的模板</a></li>';
+        $html[] = '<li>1. <a href="'. _link('dirs.html') .'">文件管理</a></li>';
+        $html[] = '<li>2. <a href="'. _link('database/creator.html') .'">数据表管理</a></li>';
+        $html[] = '<li>3. <a href="'. _link('index/dispatch.html') .'">Dispatch</a></li>';
+        $html[] = '<li>4. <a href="'. _link('index/forward.html') .'">Forward</a></li>';
+        $html[] = '<li>5. <a href="'. _link('index/display.html') .'">使用指定目录中的模板</a></li>';
         $html[] = '</ul>';
         $html[] = '</body></html>';
         return $this->setResponse(new \Qii\Base\Response(array('format' => 'html', 'body' => join("\n", $html))));

+ 2 - 2
demo/private/helper/tools.php

@@ -117,9 +117,9 @@ class tools
 			$type = self::fileType($fullPath);
 			$array['name'] = iconv('GBK', 'UTF-8', $file);
 			if (!in_array($type, $filetype)) {
-				$array['icon'] = _link('filetype/unknow.png');
+				$array['icon'] = _link('static/images/filetype/unknow.png');
 			} else {
-				$array['icon'] = _link('filetype/' . $type . '.gif');
+				$array['icon'] = _link('static/images/filetype/' . $type . '.gif');
 			}
 			$array['type'] = is_dir($fullPath) ? 'folder' : 'file';
 			$array['url'] = $array['type'] == 'folder' ? '/dirs?path=' . urlencode($fullPath) : '/dirs/file?file=' . urlencode($fullPath);

+ 1 - 1
demo/private/middleware/site.php

@@ -11,7 +11,7 @@ class site implements Middleware
 
     }
     public function handle(Request $request, Closure $next) {
-        echo "This is global middleware";
+        //echo "This is global middleware";
         return $next($request);
     }
 }

+ 9 - 9
demo/private/model/table.php

@@ -6,10 +6,10 @@
  */
 namespace model;
 
-use \Qii\Driver\DBModel;
+use \Qii\Driver\Model;
 use \Qii\Driver\Response;
 
-class table extends DBModel
+class table extends Model
 {
 	public $tablesError;
 
@@ -85,7 +85,7 @@ class table extends DBModel
 			return array();
 		}
 		$tables = array();
-		$sql = "SHOW TABLES IN " . $db;
+		$sql = "SHOW TABLES IN `" . $db . "`";
 		$rs = $this->db->setQuery($sql);
 		while ($row = $rs->fetch()) {
 			$tableName = $row['Tables_in_' . $db];
@@ -128,7 +128,7 @@ class table extends DBModel
 	public function getFieldsLists($db = 'istudy', $table)
 	{
 		$fields = array();
-		$sql = 'DESC ' . $db . '.' . $table;
+		$sql = 'DESC `' . $db . '`.`' . $table . '`';
 		$rs = $this->db->setQuery($sql);
 		while ($row = $rs->fetch()) {
 			$val = array();
@@ -224,11 +224,11 @@ class table extends DBModel
 			$rules->rules = json_encode($rule);
 			$rules->update_time = time();
 			//更新
-			$result = $rules->_update();
+			$rules->_update();
 		} else {
 			$rules->rules = json_encode($rule);
 			$rules->add_time = time();
-			$result = $rules->_save();
+			$rules->_save();
 		}
 		$result = true;
 		if ($rules->getErrors()) {
@@ -391,10 +391,10 @@ class table extends DBModel
 		$data = array();
 		$data['rows'] = array();
 		$data['page'] = array('total' => 0, 'currentPage' => 0, 'totalPage' => 0);
-		$data['page']['total'] = $this->db->getOne("SELECT COUNT(*) FROM {$database}.{$tableName}");
+		$data['page']['total'] = $this->db->getOne("SELECT COUNT(*) FROM `{$database}`.`{$tableName}`");
 		$data['page']['currentPage'] = $currentPage;
 		$data['page']['totalPage'] = ceil($data['page']['total'] / $pageSize);
-		$sql = "SELECT * FROM {$database}.{$tableName} LIMIT " . $start . ',' . $pageSize;
+		$sql = "SELECT * FROM `{$database}`.`{$tableName}` LIMIT " . $start . ',' . $pageSize;
 		$rs = $this->db->setQuery($sql);
 		$rulesCount = isset($rules['rules']['end']) && count($rules['rules']['end']) > 0 ? $rules['rules']['end'] : 0;
 		while ($row = $rs->fetch()) {
@@ -527,7 +527,7 @@ class table extends DBModel
 	 */
 	public function backupTable($database, $tableName)
 	{
-		$sql = "SELECT * FROM {$database}.{$tableName}";
+		$sql = "SELECT * FROM {$database}.`{$tableName}`";
 		$rs = $this->db->setQuery($sql);
 		$data = array();
 		$backupSQL = "USE {$database};\n";

+ 5 - 0
demo/public/index.php

@@ -1,4 +1,6 @@
 <?php
+use Qii\Base\Route;
+
 require_once('../../src/Qii.php');
 //composer安装的将上边那行 require_once('../../src/Qii.php'); 换成 require("../../vendor/autoload.php");
 $app = \Qii::getInstance();
@@ -10,6 +12,9 @@ $app->setCachePath('tmp');
 //app相关配置
 $app->setAppConfigure('configure/app.ini');
 
+//设置静态文件目录
+$app->setStaticPath(['static', 'uploads']);
+
 //设置指定前缀是否使用namespace,仅限于使用框架本省的autoload
 $app->setUseNamespace('Bootstrap', false);
 //设置logger,用于错误日志收集

BIN
src/.DS_Store


+ 54 - 5
src/Application.php

@@ -52,6 +52,7 @@ class Application
      */
     public static $paths = array('configure', 'controller', 'model', 'middleware', 'helper', 'view', 'plugins', 'tmp');
 
+    public static $staticPath = array();
     /**
      * 系统注册的 router
      * @var array
@@ -77,6 +78,8 @@ class Application
 
     public $viewEngine = 'smarty';
 
+    protected $completeHooker = [];
+
     public function __construct()
     {
         $this->helper = Psr4::getInstance()->loadClass('\Qii\Autoloader\Helper');
@@ -190,7 +193,20 @@ class Application
     {
         return self::$workspace;
     }
-
+    /**
+     * 静态文件目录不做处理,直接返回404
+     *
+     * @param array $path
+     * @return void
+     */
+    public function setStaticPath($path) {
+        if(!is_array($path)) {
+            self::$staticPath[] = $path;
+            return $this;
+        }
+        self::$staticPath = array_merge(self::$staticPath, $path);
+        return $this;
+    }
     /**
      * 获取网站的配置文件
      * @return Config\Mix
@@ -309,7 +325,7 @@ class Application
     /**
      * 全局 Middleware
      * @param array $middleware
-     * @return void
+     * @return $this
      */
     public function setGlobalMiddleware($middleware) {
         if (!is_array($middleware)) {
@@ -375,11 +391,10 @@ class Application
         $loader = new Loader($engine);
         return $loader->initialization($policy);
     }
-
     /**
      * 获取缓存的策略
-     * @param String $cache 缓存的内容
-     * @return multitype:multitype:Ambigous <>
+     * @param string $cache 缓存的内容
+     * @return array
      */
     public function getCachePolicy($cache)
     {
@@ -545,6 +560,14 @@ class Application
         if ($next === false) {
             return $this;
         }
+        //静态文件夹直接返回错误,不做任何处理
+        if($this->request->getRequestUri() == '/favicon.ico' || count(self::$staticPath) > 0
+            && in_array(explode('\\', $this->request->controller,  2)[0], self::$staticPath)) {
+            header("HTTP/1.1 404 Not Found");
+            header("Status: 404 Not Found");
+            die('404 Not Found');
+        }
+
         //route middleware
         $next = call_user_func(
             array_reduce(['Route'], function($carry, $item){
@@ -597,4 +620,30 @@ class Application
         $this->request->setDispatched(true);
         return $this;
     }
+
+    /**
+     * 运行结束后执行的方法
+     *
+     * @param callable $callable 方法
+     * @return void
+     */
+    public function setCompleteRun($callable = null) {
+        if(is_callable($callable)) {
+            $this->completeHooker[] = $callable;
+        }
+    }
+
+    /**
+     * 执行complete hooker
+     *
+     * @param object $controllerCls 当前controller类
+     * @return void
+     */
+    public function runComplete($controllerCls) {
+        if(is_array($this->completeHooker)) {
+            foreach ($this->completeHooker as $callable) {
+                $callable($controllerCls);
+            }
+        }
+    }
 }

+ 2 - 2
src/Autoloader/Factory.php

@@ -41,8 +41,8 @@ class Factory
         }
         $refClass = new \ReflectionClass($className);
         $instance = $refClass->newInstanceArgs($args);
-        if ($refClass->hasMethod('_initialize')) {
-            call_user_func_array(array($instance, '_initialize'), $args);
+        if ($refClass->hasMethod('initialization')) {
+            call_user_func_array(array($instance, 'initialization'), $args);
         }
         return Factory::$instance[$className] = $instance;
     }

+ 3 - 2
src/Autoloader/Import.php

@@ -35,7 +35,7 @@ class Import
     /**
      * 包含文件
      * @param string $file 文件路径
-     * @return mix
+     * @return array | mixed
      */
     public static function includes($file)
     {
@@ -45,7 +45,8 @@ class Import
             }, $file);
         }
         $file = str_replace(array('\\', '/'), DS, $file);
-        if (self::getIncludeFiles($file) !== null) self::getIncludeFiles($file);
+        $config = self::getIncludeFiles($file);
+        if ($config !== null) $config;
         if(!file_exists($file)) {
             throw new FileNotFound($file, 404);
         }

+ 13 - 6
src/Autoloader/Psr4.php

@@ -325,11 +325,11 @@ class Psr4
             return;
         }
         //去掉第一个斜杠
-        $class = $args[0];
         $class = preg_replace("/^\\\\/", "", $class);
         $class = $this->getClassName($class);
 
         if (class_exists($class, false)) {
+
             return call_user_func_array(array($this, 'instance'), $args);
         }
         if ($this->loadFileByClass($class)) {
@@ -464,20 +464,27 @@ class Psr4
         $className = $this->getClassName($class);
         //如果实例化的参数发生变化,就重新实例化
         $paramsHash = md5(print_r($args, true));
-        if (isset(self::$_loadedClass[$className]) && isset(self::$_loadedClassParams[$className])
-            && self::$_loadedClassParams[$className] == $paramsHash) return self::$_loadedClass[$className];
-        
-        self::$_loadedClassParams[$className] = $paramsHash;
+        if (isset(self::$_loadedClass[$className .'-'. $paramsHash])) return self::$_loadedClass[$className  .'-'. $paramsHash];
+        self::$_loadedClassParams[$className][] = $paramsHash;
 
         if (!class_exists($className, false)) {
             throw new CallUndefinedClass(\Qii::i('1105', $className), __LINE__);
         }
         $refClass = new \ReflectionClass($className);
-        self::$_loadedClass[$className] = $instance = $refClass->newInstanceArgs($args);
+        self::$_loadedClass[$className .'-'. $paramsHash] = $instance = $refClass->newInstanceArgs($args);
         //如果有_initialize方法就自动调用_initialize方法,并将参数传递给_initialize方法
         if ($refClass->hasMethod('_initialize')) {
             call_user_func_array(array($instance, '_initialize'), $args);
         }
         return $instance;
     }
+
+    /**
+     * 查看当前已经初始化的数据
+     * 
+     * @return array
+     */
+    public static function getloadedClass() {
+        return ['class' => array_keys(self::$_loadedClass), 'params' => self::$_loadedClassParams];
+    }
 }

+ 0 - 5
src/Base/Action.php

@@ -9,11 +9,6 @@ class Action extends Controller
     public $controllerId;
     public $actionId;
     public $response;
-    public function __construct()
-    {
-        parent::__construct();
-    }
-
     public function __call($method, $args)
     {
     	return call_user_func_array(array($this->controller, $method), $args);

+ 2 - 1
src/Base/Controller.php

@@ -126,7 +126,7 @@ abstract class Controller
      */
     final public function enableDB()
     {
-        return $this->db = new Model();
+        return $this->db = _loadClass('\Qii\Driver\Model');
     }
     
     /**
@@ -292,6 +292,7 @@ abstract class Controller
     public function __destruct()
     {
         $this->afterRun();
+        \Qii::getInstance()->runComplete($this);
         if ($this->request && $this->request->isForward()) {
             $this->request->setForward(false);
             \Qii::getInstance()->dispatcher->setRequest($this->request);

+ 2 - 2
src/Base/Dispatcher.php

@@ -80,6 +80,7 @@ class Dispatcher
     public function dispatch($controller = '', $action = '')
     {
         $args = func_get_args();
+
         $controller = $controller != '' ? $controller : $this->request->getControllerName();
         $action = $action != '' ? $action : $this->request->getActionName();
 
@@ -97,6 +98,7 @@ class Dispatcher
         $psr4 = Psr4::getInstance();
         $this->controllerCls = call_user_func_array(array($psr4, 'loadClass'), $funcArgs);
         //load beforeRun 不放到具体的Controller里边执行,要不这里的属性controllerCls获取不到值
+        //step beforeRun -> middleware -> initialization -> run
         if(method_exists($this->controllerCls, 'beforeRun') && is_callable(array($this->controllerCls, 'beforeRun'))) {
             !$this->controllerCls->beforeRun() && exit();
         }
@@ -121,9 +123,7 @@ class Dispatcher
             $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;
         $realAction = $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);

+ 61 - 24
src/Base/Request.php

@@ -5,7 +5,6 @@ namespace Qii\Base;
 use Qii\Config\Consts;
 use Qii\Config\Register;
 use Qii\Request\Url;
-
 abstract class Request
 {
     public $host = '';
@@ -37,6 +36,8 @@ abstract class Request
      */
     public $url;
 
+    private static $context = array();
+
     /**
      * 初始化参数,获取链接中对应的参数并存放到$this->params中
      *
@@ -77,6 +78,25 @@ abstract class Request
     }
 
     /**
+     * request context
+     * @param string $key
+     * @param string $val
+     * @return void
+     */
+    public function setContext($key, $val) {
+        self::$context[$key] = $val;
+    }
+
+    /**
+     * 获取context
+     * @param string $key
+     * @return mixed|null
+     */
+    public function getContext($key) {
+        return isset(self::$context[$key]) ? self::$context[$key]: null;
+    }
+
+    /**
      * 获取POST数据
      */
     public function post($name = null, $default = null)
@@ -172,6 +192,16 @@ abstract class Request
     }
 
     /**
+     * isPut
+     *
+     * @param void
+     * @return boolean
+     */
+    public function isDelete()
+    {
+        return (strtoupper($this->method) == 'DELETE');
+    }
+    /**
      * isHead
      *
      * @param void
@@ -183,6 +213,19 @@ abstract class Request
     }
 
     /**
+     * 获取header信息
+     * @param $key
+     * @return array|false|mixed|string|null
+     */
+    public function head($key = null) {
+        $header = getallheaders();
+        $key = ucwords($key);
+        if($key != null) {
+            return isset($header[$key]) ? $header[$key] : null;
+        }
+        return $header;
+    }
+    /**
      * isOptions
      *
      * @param void
@@ -254,20 +297,18 @@ abstract class Request
      *
      * @param mixed $name
      * @param mixed $value
-     * @return boolean | Qii_Request_Abstract
+     * @return $this
      */
     public function setParam($name, $value = null)
     {
         if (is_null($value)) {
             if (is_array($name)) {
                 $this->params = array_merge($this->params, $name);
-                return $this;
             }
         } elseif (is_string($name)) {
             $this->params[$name] = $value;
-            return $this;
         }
-        return false;
+        return $this;
     }
 
     /**
@@ -288,16 +329,15 @@ abstract class Request
     /**
      * setParams
      *
-     * @param array
-     * @return boolean | Qii_Request_Abstract
+     * @param array $params
+     * @return $this
      */
     public function setParams($params)
     {
         if (is_array($params)) {
             $this->params = $params;
-            return $this;
         }
-        return false;
+        return $this;
     }
 
     /**
@@ -315,7 +355,7 @@ abstract class Request
      * setException
      *
      * @param Exception $exception
-     * @return boolean | Qii_Request_Abstract
+     * @return $this
      */
     public function setException($exception)
     {
@@ -323,9 +363,8 @@ abstract class Request
             && ($exception instanceof Exception)
         ) {
             $this->_exception = $exception;
-            return $this;
         }
-        return false;
+        return $this;
     }
 
     /**
@@ -382,13 +421,13 @@ abstract class Request
      * setModuleName
      *
      * @param string $name
-     * @return boolean | Qii_Request_Abstract
+     * @return $this
      */
     public function setModuleName($name)
     {
         if (!is_string($name)) {
             trigger_error('Expect a string module name', E_USER_WARNING);
-            return false;
+            return $this;
         }
         $this->module = $name;
         return $this;
@@ -398,7 +437,7 @@ abstract class Request
      * setControllerName
      *
      * @param string $name
-     * @return boolean | Qii_Request_Abstract
+     * @return $this
      */
     public function setControllerName($name)
     {
@@ -414,13 +453,13 @@ abstract class Request
      * setActionName
      *
      * @param string $name
-     * @return boolean | Qii_Request_Abstract
+     * @return $this
      */
     public function setActionName($name)
     {
         if (!is_string($name)) {
             trigger_error('Expect a string action name', E_USER_WARNING);
-            return false;
+            return $this;
         }
         $this->action = $name;
         return $this;
@@ -452,15 +491,14 @@ abstract class Request
      * setBaseUri
      *
      * @param string $baseUri
-     * @return boolean | Qii\Request\Abstract
+     * @return $this
      */
     public function setBaseUri($baseUri)
     {
         if ($baseUri && is_string($baseUri)) {
             $this->_baseUri = $baseUri;
-            return $this;
         }
-        return false;
+        return $this;
     }
 
     /**
@@ -478,15 +516,14 @@ abstract class Request
      * setRequestUri
      *
      * @param string $uri
-     * @return boolean | Qii_Request_Abstract
+     * @return $this
      */
     public function setRequestUri($uri)
     {
         if (is_string($uri)) {
             $this->uri = $uri;
-            return $this;
         }
-        return false;
+        return $this;
     }
 
     /**
@@ -665,7 +702,7 @@ abstract class Request
      */
     public function getExtension() {
         $extension = pathinfo($this->uri, PATHINFO_EXTENSION);
-        return $extension?? "html";
+        return $extension != "" ? $extension : "html";
     }
 
     /**

+ 46 - 12
src/Base/Response.php

@@ -42,6 +42,33 @@ class Response
      */
     private $format;
 
+    protected $_responseCode = 200;
+
+    /**
+     * Set HTTP response code to use with headers
+     *
+     * @param int $code
+     * @return Qii\Response\Http
+     */
+    public function setResponseCode($code)
+    {
+        if (!is_int($code) || (100 > $code) || (599 < $code)) {
+            throw new \Qii\Exceptions\Response('Invalid HTTP response code');
+        }
+
+        $this->_responseCode = $code;
+        return $this;
+    }
+
+    /**
+     * Retrieve HTTP response code
+     *
+     * @return int
+     */
+    public function getResponseCode()
+    {
+        return $this->_responseCode;
+    }
 
     public function __construct($data = array(), $headers = array())
     {
@@ -169,23 +196,28 @@ class Response
         }
         return false;
     }
-    
     /**
      * Send the response, including all headers
      *
-     * @return void
+     * @return mixed
      */
     public function response()
     {
+        $code = $this->getResponseCode();
+        $headerIsSent = false;
         if ($this->data && isset($this->data['body'])) {
             switch ($this->data['format']) {
                 case self::FORMAT_JSON:
+                    $this->setHeader('Status', $code);
                     $this->setHeader('Content-Type', 'text/json');
                     $this->sendHeaders();
-                    $this->_sendHeader = true;
+                    $headerIsSent = true;
                     echo json_encode($this->data['body'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
                     break;
                 default:
+                    $this->setHeader('Status', $code);
+                    $this->sendHeaders();
+                    $headerIsSent = true;
                     $body = $this->data['body'];
                     if (is_array($this->data['body'])) {
                         $body = '';
@@ -199,17 +231,16 @@ class Response
                             $body = Response::$render->fetch($this->data['body']['tpl']);
                         }
                     }
-                    $this->setBody($body);
+                    //$this->setBody($body);
                     echo(IS_CLI ? (new Cli())->stdout($body) : $body);
                     break;
             }
-            return;
         }
-        if ($this->_sendHeader) {
+        if(!$headerIsSent) {
             $this->sendHeaders();
         }
         foreach ($this->body as $body) {
-            echo IS_CLI ? new Cli($body) : $body;
+            echo IS_CLI ? (new Cli())->stdout($body) : $body;
         }
     }
 
@@ -333,11 +364,14 @@ class Response
      */
     protected function sendHeaders()
     {
-        foreach ($this->headers as $key => $header) {
-            header(
-                $header['name'] . ': ' . $header['value'],
-                $header['replace']
-            );
+        //如果设置为不发送头信息,这里就不发
+        if($this->_sendHeader) {
+            foreach ($this->headers as $key => $header) {
+                header(
+                    $header['name'] . ': ' . $header['value'],
+                    $header['replace']
+                );
+            }
         }
         $this->clearHeaders();
         return $this;

+ 274 - 16
src/Base/Route.php

@@ -1,10 +1,27 @@
 <?php
 namespace Qii\Base;
 
+use Qii\Config\Consts;
+use Qii\Config\Register;
+use Qii\Exceptions\InvalidFormat;
+use Qii\Exceptions\Variable;
 use Qii\Request\Http;
 
 /**
  * Route
+ * 使用方法,在路由文件夹中创建路由文件,内容如下:
+ * Route::prefix("/test")->setMiddleware([test\middleware\verify::class])->group(function($route) {
+ *      // /test/testa -> test\index::indexAction
+ *      $route->any('/test/testa', test\index::class);
+ *      // /test2/login.html 会调用 test\index::loginAction
+ *      $route->any('/test2/login', [test\index::class, 'login']);
+ * });
+
+ * Route::prefix('/test')->setMiddleware(['admin\system\middleware\user'])->group(function ($route){
+ *      // /test/manage/atest.html 会调用 remote\manage::atest,以下两种写法都可以
+ *      $route->any('/manage/*', [remote\manage::class, '$2']);
+ *      $route->any('/manage/*', remote\manage::class.':$2');
+ * });
  */
 class Route
 {
@@ -29,6 +46,17 @@ class Route
      * @var array $stored
      */
     private $stored = [];
+    /**
+     * 存储默认 prefix
+     * @var array $default
+     */
+    private $default = [];
+    /**
+     * 是否启用正则匹配
+     *
+     * @var bool $enableExpress 默认为启用
+     */
+    private $enableExpress = true;
 
     public function __construct() {
         $this->clean();
@@ -52,10 +80,20 @@ class Route
         ];
     }
 
+    /**
+     * 实例化Route
+     * @return mixed|object|string|null
+     */
     public static function getInstance() {
         return \Qii::getInstance("Qii\Base\Route");
     }
 
+    /**
+     * 设置路由规则
+     *
+     * @param array $route
+     * @return void
+     */
     public function setRoute($route){
         if (!is_array($route) && !in_array($route, $this->route)) {
             $this->route[] = $route;
@@ -63,6 +101,12 @@ class Route
         $this->route = array_merge($this->route, $route);
     }
 
+    /**
+     * 设置URL前缀
+     *
+     * @param string $name
+     * @return mixed|object|string|null
+     */
     public static function prefix($name) {
         $route = self::getInstance();
         $route->clean();
@@ -149,16 +193,37 @@ class Route
         $uri = $request->getRequestUri();
         $middleware = [];
         $callable = '';
+        $args = null;
         foreach ($route as $item) {
             $middleware = $item[0];
             $subRoute = $item[1];
             if(!isset($subRoute[$method]) && !isset($subRoute['ANY'])) {
                 continue;
             }
-            $routes = array_merge((array) $subRoute[$method], (array) $subRoute['ANY']);
+            $subRoute['ANY'] = isset($subRoute['ANY']) ? (array) $subRoute['ANY'] : array();
+            $subRoute[$method] = isset($subRoute[$method]) ? (array) $subRoute[$method] : array();
+            $routes = array_merge($subRoute[$method], $subRoute['ANY']);
             $callable = '';
-
             foreach ($routes as $routeUri) {
+                //如果有设置默认,把默认规则拿出来
+                if(preg_match("/(\:default)/", $routeUri[0])) {
+                    $arr = explode(":", $routeUri[0]);
+                    $this->default[$arr[0]][$method] = [$middleware, $routeUri];
+                }
+                //启用正则匹配
+                if($this->enableExpress) {
+                    list($route, $args) = $this->parse($uri, $routeUri);
+                    if($route) {
+                        if(is_array($args)) {
+                            //将$args的值设置到get参数里
+                            foreach ($args as $key => $value) {
+                                $request->set($key, urldecode($value));
+                            }
+                        }
+                        $callable = $route[1];
+                        break;
+                    }
+                }
                 if ($uri == $routeUri[0]) {
                     $callable = $routeUri[1];
                     break;
@@ -169,42 +234,206 @@ class Route
             }
         }
         if (!$callable || count($middleware) == 0) {
+            //如果未命中规则,看看是否有默认规则
+            if(count($this->default) > 0) {
+                foreach ($this->default as $path => $value) {
+                    if(substr($uri, 0, strlen($path)) == $path && isset($value[$method])) {
+                        $middleware = $value[$method][0];
+                        $callable = $value[$method][1][1];
+                    }
+                }
+            }
+            if(!$callable) {
+                return $next($request);
+            }
+        }
+        if (!$this->make($callable, $middleware, $request, $args)){
             return $next($request);
         }
-        //命中后,执行相应程序后就不网下周
-        $middleware = array_reverse(array_unique($middleware));
-        $result = array_reduce($middleware, function ($carry, $item){
+        return false;
+    }
+
+    /**
+     * 匹配规则
+     * /user/get/{uid:num}/{name:string}/{sex:string}
+     *
+     * @param string $uri 请求uri
+     * @param array $route 路由规则
+     * @return null[]
+     */
+    public function parse($uri, $route) {
+        //去掉后缀名,不匹配后缀名
+        $ext = pathinfo($uri, PATHINFO_EXTENSION);;
+        if($ext) {
+            $ext = ".". $ext;
+            if(substr($uri, -1 * strlen($ext)) == $ext) {
+                $uri = substr($uri, 0,-1 * strlen($ext));
+            }
+        }
+        $routeExt = pathinfo($route[0], PATHINFO_EXTENSION);
+        if($routeExt) {
+            $routeExt = ".". pathinfo($route[0], PATHINFO_EXTENSION);
+            if(substr($route[0], -1 * strlen($routeExt)) == $routeExt) {
+                $route[0] = substr($route[0], 0,-1 * strlen($routeExt));
+            }
+        }
+        if(!preg_match("/\{.*?\}/i", $route[0]) && $uri == $route[0]) {
+            //没有参数的匹配完整就直接返回route和参数值null
+            return [$route, null];
+        }
+        //优先精确匹配
+        if(preg_match("/\{.*?\}/i", $route[0])) {
+            $uriArray = explode("/", $uri);
+            $routeArray = explode("/", $route[0]);
+            //如果uri的长度比route不相等,且前缀不匹配就直接退出规则匹配
+            if(count($uriArray) != count($routeArray) && !preg_match("/(". str_replace("/", "\/", $uri).").*?/", $route[0])) {
+                return [null, null];
+            }
+
+            $args = array();
+            $firstMatch = -1;
+            foreach ($routeArray as $index => $val) {
+                if(preg_match("/\{.*?\}/i", $val)) {
+                    if($firstMatch == -1) {
+                        $firstMatch = $index;
+                    }
+                    $key = str_replace(array("{", "}"), "", $val);
+                    $value = $uriArray[$index];
+                    //如果key未xx:yy则yy表示xx的值类型,会做强制转换,支持的类型为number、string、bool、float
+                    if(strpos($key, ":") > 0) {
+                        $keyArr = explode(":", $key);
+                        $key = $keyArr[0];
+                        $value =  $this->convertValueTo($uriArray[$index], $keyArr[1]);
+                    }
+                    $args[$key] = $value;
+                }
+            }
+            //首次变量的位置之前的内容不匹配,页直接返回不匹配
+            $uriMatch = join("/", array_slice($uriArray, 0, $firstMatch));
+            $routeMatch = join("/", array_slice($routeArray, 0, $firstMatch));
+            if($uriMatch != $routeMatch) {
+                return [null, null];
+            }
+            return [$route, $args];
+        }
+
+        if($route[0] == $uri . "*") {
+            return [$route, null];
+        }
+        //匹配number
+        if(preg_match("/(:number)$/i", $route[0])) {
+            $exp = str_replace("/", "\\/",  preg_replace("/(\:number)$/i", "", $route[0]));
+            $exp = "/(".$exp.")\d+?$/";
+            preg_match($exp, $uri, $match);
+            if($match) {
+                return [$route, null];
+            }
+        }
+
+        //匹配任何字符
+        if(preg_match("/(\:any|\*)$/i", $route[0])) {
+            $exp = "(". preg_replace("/(\:any|\*)$/i", "", $route[0]) .")";
+            if(preg_match($exp, $uri)) {
+                return [$route, null];
+            }
+        }
+        return [null, null];
+    }
+
+    /**
+     * 执行middleware
+     *
+     * @param array $middleware
+     * @return mixed|true
+     */
+    protected function handleMiddleware($middleware) {
+        //命中后,执行相应程序后就不往下执行
+        $middleware = array_reverse(array_unique((array) $middleware));
+        //获取全局middleware
+        return array_reduce($middleware, function ($carry, $item){
             return function () use ($carry, $item){
                 return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
             };
         }, function(){
             return true;
         })();
-        if (!$result) return false;
-        if (!$this->make($callable)){
-            return $next($request);
-        }
-        return false;
     }
 
     /**
      * 执行 $callable
+     *
      * @param mix $callable 执行方法
+     * @param array $middleware 中间件
+     * @param HTTP $request request
+     * @param array | null $args 正则匹配后拿到的参数及值
      * @return bool
+     * @throws Variable
      */
-    public function make($callable) {
+    public function make($callable, $middleware, $request, $args = null) {
+        if($args === null) {
+            $args = array($request);
+        }
+        if(is_array($args)) {
+            array_unshift($args, $request);
+        }
+        $res = null;
         switch ($callable) {
             case ($callable instanceof  \Closure):
-                echo call_user_func($callable);
+                if(count($middleware) > 0 && !$this->handleMiddleware($middleware)) {
+                    return true;
+                }
+                $res = call_user_func_array($callable, $args);
                 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();
+                //支持路由格式
+                // $route->any('/default/*', ['class', '$2']); 这里的$2值的是request->get(2)这个值
+                if(is_array($callable)) {
+                    $action = (isset($callable[1]) ? $callable[1] : Register::get("APP_DEFAULT_ACTION"));
+
+                    $controllerCls = _loadClass($callable[0]);
+                }else{
+                    if(strpos($callable, ":") > 0) {
+                        $split = explode(":", $callable);
+                        $callable = $split[0];
+                        $action = $split[1];
+                    }else{
+                        $action = Register::get("APP_DEFAULT_ACTION");
+                    }
+                    $controllerCls = _loadClass($callable);
+                }
+
+                preg_match('/\$\d$/', $action, $match);
+                if($match) {
+                    $key = ltrim($action, '$');
+                    $action = $request->get($key, Register::get("APP_DEFAULT_ACTION"));
+                }
+                $action = $action . Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);
+
+                if(preg_match("/^\d/", $action)) {
+                    throw new InvalidFormat('方法名不能以数字开头');
+                }
+
+                if(method_exists($controllerCls, 'beforeRun') && !$controllerCls->beforeRun()) {
+                    return true;
+                }
+                if(count($middleware) > 0 && !$this->handleMiddleware($middleware)) {
+                    return true;
+                }
+                if(method_exists($controllerCls, 'initView')) {
+                    $controllerCls->initView();
+                }
+                if(method_exists($controllerCls, 'initialization')) {
+                    $controllerCls->initialization();
+                }
+                $res = call_user_func_array(array($controllerCls, $action), $args);
                 break;
         }
+        if($res instanceof Response) {
+            echo $res->response();
+        }else if($res !== null){
+            echo $res;
+        }
         if ($callable) {
             return true;
         }
@@ -212,6 +441,35 @@ class Route
     }
 
     /**
+     * 将值转换为指定类型
+     *
+     * @param $value
+     * @param $to
+     * @return bool|mixed|string
+     */
+    protected function convertValueTo($value, $to) {
+        if(!in_array($to, ['number', 'string', 'bool'])) {
+            return $value;
+        }
+        switch ($to) {
+            case 'number':
+                //取字符串中连续数字
+                preg_match('/(\d+)/',$value,$r);
+                if($r) {
+                    return $r[0];
+                }
+                return 0;
+            case 'string':
+                return (string) $value;
+            case 'bool':
+                if(in_array($value, ['true', 'false'])) {
+                    return $value == 'true';
+                }
+                return (bool) $value;
+        }
+        return "";
+    }
+    /**
      * 获取所有设置的路由
      *
      * @return mixed

+ 1 - 1
src/Base/Rules.php

@@ -484,7 +484,7 @@ class Rules
                 continue;
             }
             if(count($value) == 1) {
-                $value[1] = $value[0] .'格式不正确';
+                $value[1] = (isset($value[0]) ? $value[0] : '') .'格式不正确';
                 $value[2] = true;
             }else if(count($value) == 2) {
                 $value[2] = true;

+ 19 - 23
src/Config/Register.php

@@ -41,9 +41,9 @@ class Register
 	/**
 	 * 设置键值
 	 *
-	 * @param String $key
-	 * @param String $val
-	 * @param Bool overwrite 是否覆盖之前保存的值,如果之前保存了值,要想再保存需要额外设置它为true,否则不让保存
+	 * @param string $key
+	 * @param mixed $val
+	 * @param bool overwrite 是否覆盖之前保存的值,如果之前保存了值,要想再保存需要额外设置它为true,否则不让保存
 	 */
 	public static function set($key, $val, $overwrite = true)
 	{
@@ -219,26 +219,22 @@ class Register
 		Register::set($cacheName, $config);
 	}
 
-	/**
-	 * 合并ini文件生成的数组
-	 * @param String $iniFile ini文件名
-	 * @param Array $array
-	 */
-	public static function mergeAppConfigure($iniFile, $array)
-	{
-		if (!is_array($array)) return;
-		$config = Register::getIniConfigure($iniFile);
-
-		$environs = Register::get(Consts::APP_ENVIRONS, array());
-
-		$cacheName = basename($iniFile);
-		if (isset($environs[$cacheName])) {
-			$environ = $environs[$cacheName];
-			$cacheName = $environ . '.' . $cacheName;
-		}
-		$config = array_merge($config, $array);
-		Register::set($cacheName, $config);
-	}
+    /**
+     * 合并ini文件生成的数组
+     * @param String $iniFile ini文件名
+     * @param Array $array
+     */
+    public static function mergeAppConfigure($iniFile, $array, $write = true)
+    {
+        if (!is_array($array)) return;
+        $config = Register::getIniConfigure($iniFile);
+        $merged = self::array_merge_recursive_distinct($config, $array);
+        if($write) {
+            $cacheName = Register::getCacheName($iniFile);
+            Register::set($cacheName, $merged);
+        }
+        return $merged;
+    }
 
 	/**
 	 * 获取配置ini文件

+ 58 - 21
src/Driver/Base.php

@@ -138,9 +138,9 @@ class Base
     final function selectOne($table)
     {
         //验证table是否合法
-        if (!is_string($table) || !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}$/", $table)) {
+        if (!$this->verifyTable($table)) {
             if (gettype($table) == 'string') {
-                throw new TableException("表名不能包含怪字符且不能以数字开头");
+                throw new TableException("表名不能包含怪字符且不能以数字开头,获取到的是". $table);
             }
             throw new TableException("表名必须是字符串加下划线,目标字符为". gettype($table));
         }
@@ -201,7 +201,10 @@ class Base
      */
     protected function createInsertReplaceObj($dataArray)
     {
-        if (sizeof($dataArray) > 0 || get_object_vars($dataArray) > 0) {
+        if(gettype($dataArray) == 'object') {
+            $dataArray = get_object_vars($dataArray);
+        }
+        if (sizeof($dataArray) > 0) {
             $keys = array();
             $values = array();
             foreach ($dataArray AS $key => $value) {
@@ -355,7 +358,7 @@ class Base
         if(count($set) == 0) {
             return $this;
         }
-        $operator = array("equal" => "%s`%s` = '%s'", "plus" => "%s`%s` = %s`%s`+%s", "minus" => "%s`%s` = %s`%s`-%s", "multiply" => "%s`%s` = %s`%s`*%s", "divide" => "%s`%s` = %s`%s`/%s");
+        $operator = array("equal" => "%s`%s` = '%s'", "unequal" => "%s`%s` != '%s'", "plus" => "%s`%s` = %s`%s`+%s", "minus" => "%s`%s` = %s`%s`-%s", "multiply" => "%s`%s` = %s`%s`*%s", "divide" => "%s`%s` = %s`%s`/%s");
         foreach($set as $key => $val)
         {
             $val = $this->setQuote($val);
@@ -666,6 +669,9 @@ class Base
         if (ini_get("magic_quotes_gpc")) {
             return $word;
         }
+        if(in_array(gettype($word), array("object", "resource","resource (closed)"))) {
+            throw new \Exception('期待参数为数组或字符串,获取到的是:'. gettype($word)."(". json_encode($word) .")");
+        }
         return is_array($word) ? array_map('addslashes', $word) : addslashes($word);
     }
     /**
@@ -833,7 +839,7 @@ class Base
      */
     protected function getFieldAlias($name, $connector = ".")
     {
-        $aliases = explode('.', $name);
+        $aliases = explode('.', $name, 2);
         if(count($aliases) == 1) {
             return array('alias' => '', 'name' => $name);
         }
@@ -849,18 +855,9 @@ class Base
      */
     protected function getTableAlias($name)
     {
-        if (!is_string($name) || (
-            // table
-            !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}$/", $name) &&
-            // `table`
-            !preg_match("/^`[a-zA-Z_]+[a-zA-Z0-9_]{0,}`$/", $name)) &&
-            // table alias
-            !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) &&
-            // `table` alias
-            !preg_match("/^`[a-zA-Z_]+[a-zA-Z0-9_]{0,}`\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name)
-        ) {
+        if (!$this->verifyTable($name)) {
             if (gettype($name) == 'string') {
-                throw new TableException("表名不能包含怪字符且不能以数字开头");
+                throw new TableException("表名不能包含怪字符且不能以数字开头,获取到的是". $name);
             }
             throw new TableException("表名必须是字符串加下划线,目标字符为". gettype($name));
         }
@@ -932,6 +929,11 @@ class Base
         $lastIsValue = null;
         foreach($group as $index => $val)
         {
+            //in比较特殊,它可以是字符串,可以是数组,结果是数组就单独处理不走操作符的处理逻辑
+            if(substr($index, -3)  == ':in' && is_array($val)) {
+                $tmpWhere[$index] = $val;
+                continue;
+            }
             $isOperator = $this->isOperator($val);//如果是操作符,上一个不是操作符就清空tmpWhere,并放入slices
             $isValue = !$isOperator;
             if($lastIsValue && $isOperator)
@@ -944,7 +946,7 @@ class Base
             {
                 $tmpWhere[$index] = $val;
                 $slices[] = array_values($tmpWhere);
-                unset($tmpWhere);
+                $tmpWhere = array();
             }else {
                 $tmpWhere[$index] = $val;
             }
@@ -963,7 +965,7 @@ class Base
     /**
      * 判断是否是操作符号
      *
-     * @param $val 值
+     * @param mixed $val 值
      * @return bool
      */
     protected function isOperator($val)
@@ -972,6 +974,9 @@ class Base
             return false;
         }
         if(!is_array($val)) {
+            if(in_array(gettype($val), array("array", "object", "resource","resource (closed)"))) {
+                throw new \Exception("期待参数为字符串,获取到的为: " . gettype($val) ."(". json_encode($val) . ")");
+            }
             $val = strtolower($val);
         }
         return in_array($val, $this->operateVal) || in_array($val, $this->operateTable);
@@ -1008,7 +1013,7 @@ class Base
             $extraParams = array_slice(func_get_args(), 2);
         }
         $where = array();
-        $operator = array('equal' => "%s`%s` = '%s'",'in' => "%s`%s` in (%s)", 'unequal' => "%s`%s` != '%s'", 'greater' => "%s`%s` > '%s'", 'greaterEqual' => "%s`%s` >= '%s'", 'less' => "%s`%s` < '%s'", 'lessEqual' => "%s`%s` <= '%s'", 'like' => "%s`%s` like '%%%s%%'");
+        $operator = array('equal' => "%s`%s` = '%s'",'in' => "%s`%s` in (%s)", 'unequal' => "%s`%s` != '%s'", 'greater' => "%s`%s` > '%s'", 'greaterEqual' => "%s`%s` >= '%s'", 'gte' => "%s`%s` >= '%s'", 'less' => "%s`%s` < '%s'", 'lessEqual' => "%s`%s` <= '%s'", 'lte' => "%s`%s` <= '%s'", 'like' => "%s`%s` like '%%%s%%'");
         $lastIsOperator = false;
         $lastIsValue = null;
         $i = 0;
@@ -1042,10 +1047,17 @@ class Base
             $operate = $this->getFieldOperator($aliases['name']);
             $opt = $operate['operator'] ? $operate['operator'] : $defaultOperate;
             $name =  $operate['field'];
-            if($opt == 'in' && is_array($val))
+            if($opt == 'in')
             {
-                $where[] = sprintf($operator[$opt], $alias, $name, "'". join("','", $val) . "'");
+                if(is_array($val)) {
+                    $where[] = sprintf($operator[$opt], $alias, $name, "'". join("','", $val) . "'");
+                }else{
+                    $where[] = sprintf($operator[$opt], $alias, $name, $val);
+                }
             }else{
+                if(!isset($operator[$opt])) {
+                    throw new \Exception("Unknow operator " . $opt, __LINE__);
+                }
                 $where[] = sprintf($operator[$opt], $alias, $name, $this->setQuote($val));
             }
             $lastIsValue = $isValue;
@@ -1194,6 +1206,31 @@ class Base
     }
 
     /**
+     * 验证数据表名是否符合规范
+     * 表名不能以数字开头,
+     *
+     * @param string $name
+     * @return bool
+     */
+    final public function verifyTable($name) {
+        if (!is_string($name) || (
+            // table
+           !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}$/", $name) &&
+           // `table`
+           !preg_match("/^`[a-zA-Z_]+[a-zA-Z0-9_]{0,}`$/", $name)) &&
+            // table alias
+            !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) &&
+            // `table` alias
+            !preg_match("/^`[a-zA-Z_]+[a-zA-Z0-9_]{0,}`\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) &&
+            // database.table
+            !preg_match("/^[a-zA-Z]+\.[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) &&
+            !preg_match("/^[a-zA-Z]+\.[a-zA-Z]+[a-zA-Z0-9_]{0,}\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name)
+        ) {
+            return false;
+        }
+        return true;
+    }
+    /**
      * 如果不存在指定的方法则调用提示错误
      *
      * @param string $method

+ 3 - 3
src/Driver/ConnBase.php

@@ -40,7 +40,7 @@ class ConnBase
 
 	/**
 	 * 通过sql语句判断是读还是写操作
-	 * @param $sql  读/写的sql语句
+	 * @param string $sql  读/写的sql语句
 	 */
 	protected function prepare($sql)
 	{
@@ -66,10 +66,10 @@ class ConnBase
 		if (isset($this->_connections[$default])) return $this->_connections[$default];
 		switch ($default) {
 			case 'READ':
-				$connection = $this->_connections['READ'] = $this->getReadConnection();
+                $this->_connections['READ'] = $connection = $this->getReadConnection();
 				break;
 			default:
-                $connection = $this->_connections['WRITE'] = $this->getWriteConnection();
+                $this->_connections['WRITE'] = $connection = $this->getWriteConnection();
 				break;
 		}
 

+ 13 - 7
src/Driver/Easy.php

@@ -198,7 +198,7 @@ class Easy
 	 */
 	public function getValues()
 	{
-		return $this->fields->getValues();
+		return $this->fields->getValueAsArray();
 	}
 
 	/**
@@ -298,7 +298,7 @@ class Easy
 			if (count($rule) > 0 && !$this->validateFields(array($key => $rule))) {
 				return $this->_response;
 			}
-			if ($this->fields->isField($key)) $where[] = "`{$key}` = '" . $this->db->setQuote($this->fields->getField($key)) . "'";
+			if ($this->fields->hasField($key)) $where[] = "`{$key}` = '" . $this->db->setQuote($this->fields->getField($key)) . "'";
 		}
 		$result = $this->db->limit(1)->where(join(' AND ', $where))->select($this->getTableName());
 		if(!$result)
@@ -338,7 +338,7 @@ class Easy
 			$this->_response = Response::Exist('_save', array('_result' => \Qii::i(1511, join(',', $this->getPrivateValue()))));
 			return $this->_response;
 		}
-		$result = $this->db->insertObject($this->getTableName(), $this->fields->getValues());
+		$result = $this->db->insertObject($this->getTableName(), $this->fields->getValueAsArray());
 		if ($this->db->isError()) {
 			return $this->db->getResponse();
 		}
@@ -355,10 +355,16 @@ class Easy
 		$this->checkInstance();
 		if (!$this->validateFields($this->easyRules->getRulesByOperate('update'))) return $this->_response;
 		$this->resetPrivateKey('update');
-		if (count($this->_exist()) == 0) {
+		if (count($this->_exist()->getResult()) == 0) {
 			return $this->_response = Response::NotExist('_update', \Qii::i(1512, join(',', $this->getPrivateValue())));
 		}
-		$result = $this->db->updateObject($this->getTableName(), $this->fields->getValues(), $this->privateKeys);
+        $values = $this->fields->getValueAsArray();
+        $where = [];
+        foreach ($this->privateKeys as $field) {
+            $where[$field] = $values[$field];
+            unset($values[$field]);
+        }
+		$result = $this->db->updateObject($this->getTableName(), $this->fields->getValueAsArray(), $where);
 		if ($this->db->isError()) {
 			return $this->_response = $this->db->getResponse();
 		}
@@ -374,10 +380,10 @@ class Easy
 		$this->checkInstance();
 		if (!$this->validateFields($this->easyRules->getRulesByOperate('remove'))) return $this->_response;
 		$this->resetPrivateKey('remove');
-		if (count($this->_exist()) == 0) {
+		if (count($this->_exist()->getResult()) == 0) {
 			return $this->_response = Response::NotExist('_remove', \Qii::i(1512, join(',', $this->getPrivateValue())));
 		}
-		$result = $this->db->deleteObject($this->getTableName(), $this->fields->getValues());
+		$result = $this->db->deleteObject($this->getTableName(), $this->fields->getValueAsArray());
 		if ($this->db->isError()) {
 			return $this->_response = $this->db->getResponse();
 		}

+ 533 - 125
src/Driver/Entity/Base.php

@@ -1,25 +1,228 @@
 <?php
 namespace Qii\Driver\Entity;
 
-
-
-use Qii\Driver\Response;
+use \Qii\Driver\Response;
 
 class Base {
-    public static $propertys;
+    /**
+     * 字段及值
+     *
+     * @var array $properties
+     */
+    public static $properties;
+    public static $config;
+    /**
+     * hooker for fields 查询字段/ where 查询条件 / order 排序 / cache 缓存
+     * @var array $hooker
+     */
+    private $hooker = [];
+    /**
+     * 默认过期时间
+     *
+     * @var int $defaultExpired
+     */
+    private $defaultExpired = 600;
+
     public function __construct(){
     }
-    public function defaultFieldsValue(){}
+
+    /**
+     * 默认值,仅在新增数据的时候支持,更新不支持
+     *
+     * @return array
+     */
+    public function defaultFieldsValue(){
+        return array();
+    }
+
+    /**
+     * 返回 table 的名字,以便分库分表的时候指定使用
+     *
+     * @return mixed
+     * @throws \Exception
+     */
     public function getTable() {
         throw new \Exception('请先设置需要保存的数据表');
     }
 
     /**
-     * 返回使用的db信息
+     * set hooker for method
+     * 目前 where 和 order 及 fields 支持
+     * @param $key
+     * @param $hooker
+     * @param $args
+     * @return $this
+     */
+    protected function setHooker($key, $hooker, $args) {
+        $this->hooker[$key]['func'] = $hooker;
+        $this->hooker[$key]['args'] = $args;
+        return $this;
+    }
+
+    /**
+     * 设置where hooker
+     * @param object $hooker
+     * @return $this
+     * @throws \Exception
+     */
+    final public function setWhereHooker($hooker, $args = null) {
+        $this->checkCallable($hooker);
+        $this->setHooker('where', $hooker, $args);
+        return $this;
+    }
+    /**
+     * 获取where条件
+     *
+     * @return array|mixed
+     */
+    public function getWhereHooker() {
+        $where = $this->properties();
+        if(isset($this->hooker['where']) && $this->hooker['where'] && is_callable($this->hooker['where']['func'])) {
+            $where = call_user_func($this->hooker['where']['func'], $this->properties(), $this->hooker['where']['args']);
+        }
+        return $where;
+    }
+
+    /**
+     * 设置order by hooker
+     * @param object $hooker
+     * @return $this
+     * @throws \Exception
+     */
+    final public function setOrderHooker($hooker, $args = null) {
+        $this->checkCallable($hooker);
+        $this->setHooker('order', $hooker, $args);
+        return $this;
+    }
+
+    /**
+     * 设置 limit hooker
+     *
+     * @param object $hooker
+     * @return $this
+     * @throws \Exception
+     */
+    final public function setLimitHooker($hooker, $args = null) {
+        $this->checkCallable($hooker);
+        $this->setHooker('limit', $hooker, $args);
+        return $this;
+    }
+    /**
+     * 获取limit
+     * @return int|mixed
+     */
+    final public function getLimitHooker() {
+        if(isset($this->hooker['limit']) && $this->hooker['limit'] && is_callable($this->hooker['limit']['func'])) {
+            return call_user_func($this->hooker['limit']['func'], [], $this->hooker['limit']['args']);
+        }
+        return [];
+    }
+
+    /**
+     * 设置 cache hooker
+     *
+     * @param callable $hooker
+     * @param array| mixed $args
+     * @return $this
+     * @throws \Exception
+     */
+    final public function setCacheHooker($hooker, $args = null) {
+        $this->checkCallable($hooker);
+        $this->setHooker('cache', $hooker, $args);
+        return $this;
+    }
+
+    /**
+     * 获取Cache Hooker
+     * @return mixed|null[]
+     */
+    final public function getCacheHooker() {
+        if(isset($this->hooker['cache']) && $this->hooker['cache'] && is_callable($this->hooker['cache']['func'])) {
+            return call_user_func($this->hooker['cache']['func'], [], $this->hooker['cache']['args']);
+        }
+        return [null, null, null];
+    }
+
+    /**
+     * 缓存设置
+     *
+     * @param array $config 缓存配置
+     * @return void
+     */
+    final public function setCacheConfig($config) {
+        if(!is_array($config)) {
+            return;
+        }
+        self::$config['cache'] = $config;
+    }
+
+    /**
+     * 获取缓存的配置信息
+     *
+     * @return array
+     */
+    final public function getCacheConfig() {
+        if(is_array(self::$config) && isset(self::$config['cache']) && is_array(self::$config['cache'])) {
+            return self::$config['cache'];
+        }
+        return array();
+    }
+    /**
+     * 如果hooker不可调用即抛出异常
+     *
+     * @param object $hooker
+     * @return void
+     * @throws \Exception
+     */
+    private function checkCallable($hooker) {
+        if($hooker && !is_callable($hooker)) {
+            throw new \Exception('Hooker is uncallable');
+        }
+    }
+    /**
+     * 设置order by hooker
+     * @param object $hooker
+     * @return $this
+     * @throws \Exception
+     */
+    final public function setQueryFieldsHooker($hooker, $args = null) {
+        if(!is_callable($hooker)) {
+            throw new \Exception('Hooker is un callable');
+        }
+        $this->setHooker('fields', $hooker, $args);
+        return $this;
+    }
+
+    /**
+     * 通过field hooker返回查询的字段
      *
+     * @return mixed|string
+     */
+    final public function getFieldsHooker() {
+        if(isset($this->hooker['fields']) && $this->hooker['fields'] && is_callable($this->hooker['fields']['func'])) {
+            return call_user_func($this->hooker['fields']['func'], [], $this->hooker['fields']['args']);
+        }
+        return '*';
+    }
+
+    /**
+     * 获取指定field的值
+     * @param string $field
      * @return mixed
      */
-    public function db() {
+    final public function getFieldVal($field) {
+        if(!preg_match('/^[a-zA-Z]/', $field)) {
+            throw new \Exception('Field is illegal');
+        }
+        $property = $this->entity()->convertToProperty($field);
+        return $this->{$property};
+    }
+    /**
+     * 返回使用的db信息
+     *
+     * @return \Qii\Driver\Base
+     */
+    final public function db() {
         return _loadClass('\Qii\Driver\Model');
     }
 
@@ -27,7 +230,7 @@ class Base {
      * 返回 entity 信息
      * @return mixed
      */
-    public function entity() {
+    final public function entity() {
         return _loadClass('\Qii\Driver\Entity\Entity');
     }
 
@@ -81,10 +284,30 @@ class Base {
         }
         return explode($separator, $string, $limit);
     }
+
+    /**
+     * 添加数据验证字段
+     *
+     * $fields = [];
+     * return $this->valid($fields);
+     *
+     * @return mixed
+     * @throws \Exception
+     */
     public function validFieldsForAdd(){
         throw new \Exception('请设置插入数据验证的字段,格式如:["Id", "Title"],Id和Title为entity的属性');
     }
 
+
+    /**
+     * 更新数据验证字段
+     *
+     * $fields = [];
+     * return $this->valid($fields);
+     *
+     * @return mixed
+     * @throws \Exception
+     */
     public function validFieldsForUpdate() {
         throw new \Exception('请设置更新数据验证的字段,格式如:["Id", "Title"],Id和Title为entity的属性');
     }
@@ -93,7 +316,7 @@ class Base {
      *
      * @return array
      */
-    public function properties() {
+    final public function properties() {
         $class = get_called_class();
         $method = new \ReflectionClass($class);
         $properties = $method->getproperties();
@@ -112,11 +335,32 @@ class Base {
     }
 
     /**
+     * 清除变量内容
+     * @return $this
+     */
+    final public function clear() {
+        $class = get_called_class();
+        $method = new \ReflectionClass($class);
+        $properties = $method->getproperties();
+        $fields = [];
+        foreach($properties as $property) {
+            if($property->isPublic()) {
+                $name = $property->getName();
+                if(!isset($this->$name)) {
+                    continue;
+                }
+                $this->$name = null;
+            }
+        }
+        return $this;
+    }
+
+    /**
      * 获取默认值
      *
      * @return array
      */
-    public function getDefaultValue() {
+    final public function getDefaultValue() {
         $default = [];
         $defaultValue = $this->defaultFieldsValue();
         if(!$defaultValue || !is_array($defaultValue) || count($defaultValue) == 0) {
@@ -135,8 +379,8 @@ class Base {
      * @return mixed
      * @throws \Exception
      */
-    public function count() {
-        return $this->db()->fields(' COUNT(1) as count')->where($this->properties())->selectOne($this->getTable());
+    final public function count() {
+        return $this->db()->fields(' COUNT(1) as count')->where($this->getWhereHooker())->selectOne($this->getTable());
     }
 
     /**
@@ -145,31 +389,31 @@ class Base {
      * @param $key
      * @return bool
      */
-    public function hasProperty($key) {
+    final public function hasProperty($key) {
         $class = get_called_class();
         $key = $this->entity()->convertToProperty($key);
-        if(isset(self::$propertys[$class])) {
-            return isset(self::$propertys[$class][$key]);
+        if(isset(self::$properties[$class])) {
+            return isset(self::$properties[$class][$key]);
         }
         $method = new \ReflectionClass($class);
         $properties = $method->getproperties();
-        $fields = [];
+        $fields = array();
         foreach($properties as $property) {
             if($property->isPublic()) {
                 $name = $property->getName();
                 $fields[$name] = '';
             }
         }
-        self::$propertys[$class] = $fields;
+        self::$properties[$class] = $fields;
         return isset($fields[$key]);
     }
 
     /**
      * bind value
      * @param $values
-     * @return $this|false
+     * @return $this
      */
-    public function bindValues($values) {
+    final public function bindValues($values) {
         if(!is_array($values)) {
             return $this;
         }
@@ -181,17 +425,21 @@ class Base {
         }
         return $this;
     }
-
     /**
      * 获取数据
      *
-     * @return mixed
+     * @return Object
      * @throws \Exception
      */
-    public function get() {
-        $info = $this->db()->where($this->properties())->selectRow($this->getTable());
+    final public function get() {
         $class = get_called_class();
         $obj = new $class();
+        $response = $this->info();
+        if($response->isError()) {
+            return $obj;
+        }
+        $info = $response->getResult()['body'];
+
         if(!$info) {
             return $obj;
         }
@@ -201,32 +449,48 @@ class Base {
         }
         return $obj;
     }
-
-
+    /**
+     * 与get不同的这个返回的是Response
+     * @return mixed|\Qii\Driver\Qii\Driver\Response
+     */
+    final public function info() {
+        try{
+            $info = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->selectRow($this->getTable());
+            return Response::Success(
+                static::class .'::'. __FUNCTION__,
+                ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '获取成功', 'body' => $info]]
+            );
+        }catch (\Exception $e) {
+            return Response::Fail(static::class .'::'. __FUNCTION__,
+                ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => $this->db()->getMessage(), 'body' => []]
+                ]);
+        }
+    }
     /**
      * 是否相关数据存在
      *
      * @return bool
      */
-    public function exist() {
-        if(!$this->properties()) {
+    final public function exist() {
+        $where = $this->getWhereHooker();
+        if(!$where) {
             return false;
         }
-        return (bool) $this->db()->where($this->properties())->selectOne($this->getTable());
+        return (bool) $this->db()->where($where)->selectOne($this->getTable());
     }
     /**
      * 保存数据
      *
-     * @return mixed|Qii\Driver\Response
+     * @return mixed|Response
      * @throws \Exception
      */
-    public function add() {
+    final public function add() {
         $valid = $this->validFieldsForAdd();
         if($valid->isError()) {
             return $valid;
         }
         //如果设置了 unique 就先验证唯一性
-        list($uniqueWhere, $uniqueOr, $exclude, $primary) = $this->condition();
+        list($uniqueWhere, $exclude, $primary) = $this->condition();
         unset($exclude);
 
         //如果 $where $or 为空,看看主键是否有设置值,有设置值说明验证主键就可以,反之设置了主键,主键值设置了,只验证主键就可以了
@@ -239,8 +503,8 @@ class Base {
             }
         }
 
-        if(count($uniqueWhere) > 0 || count($uniqueOr) > 0) {
-            $exist = $this->db()->limit(1)->where($uniqueWhere)->orTerms($uniqueOr)->selectRow($this->getTable());
+        if(count($uniqueWhere) > 0) {
+            $exist = $this->db()->limit(1)->where($uniqueWhere)->selectRow($this->getTable());
             if($exist) {
                 return Response::Exist(static::class .'::'. __FUNCTION__,
                     ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '数据已经存在', 'body' => $exist]]
@@ -260,50 +524,72 @@ class Base {
             ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '添加成功', 'body' => $res]]
         );
     }
+
+    /**
+     * 删除指定数据
+     *
+     * @return false|Response
+     */
+    public function remove() {
+        if(!$this->properties()) {
+            return Response::FailRemove(static::class .'::'. __FUNCTION__,
+                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => '未指定删除属性,不允许删除', 'body' => 0]
+                ]);
+        }
+        $properties = [];
+        foreach ($this->properties() as $key => $val) {
+            $properties[] = $this->entity()->convertToProperty($key);
+        }
+
+        $valid = $this->valid($properties);
+        if($valid->isError()) {
+            return $valid;
+        }
+        $affectedRows = $this->db()->where($this->getWhereHooker())->delete($this->getTable());
+        if($this->db()->isError()) {
+            return Response::FailRemove(static::class .'::'. __FUNCTION__,
+                ['_result' => ['code' => Response::FAIL_FOR_SAVE, 'msg' => $this->db()->getMessage(), 'body' => 0]
+                ]);
+        }
+        return Response::Success(static::class .'::'. __FUNCTION__,
+            ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '删除成功,总共删除了'. $affectedRows . '条记录', 'body' => $affectedRows]]
+        );
+    }
     /**
      * 更新
      *
-     * @return mixed | Qii\Driver\Response
+     * @return mixed | Response
      * @throws \Exception
      */
-    public function update(){
+    final public function update(){
         $valid = $this->validFieldsForUpdate();
         if($valid->isError()) {
             return $valid;
         }
-        return $this->updateFields();
         //检查是否有重复的
-        list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition();
+        list($uniqueWhere, $exclude, $primaryKey) = $this->condition();
         // 检查 unique 是否已经存在相关数据
         $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);
-        $diffOr = array_diff_assoc($uniqueOr, $primaryKey);
-        /*print_r(
-            [
-                'where' => $uniqueWhere,
-                'or' => $uniqueOr,
-                'exclude' => $exclude,
-                'primary' => $primaryKey
-            ]
-        );*/
-        /*$diffExclude = array_diff_assoc($exclude, $primaryKey);
-
-        print_r(
-            [
-                'diffWhere' => $diffWhere,
-                'diffOr' => $diffOr,
-                'diffExclude' => $diffExclude
-            ]
-        );*/
-
-        if(count($diffWhere) > 0 || count($diffOr) > 0) {
-            $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->getTable());
+        if(count($diffWhere) > 0) {
+            $unique = $this->db()->limit(1)->where($diffWhere)->exclude($exclude)->selectRow($this->getTable());
             if($unique) {
                 return Response::Exist(static::class .'::'. __FUNCTION__,
                     ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '已经存在相关记录', 'body' => $unique]]
                 );
             }
         }
-
+        $where = [];
+        $whereHooker = $this->getWhereHooker();
+        foreach ($whereHooker as $key => $val) {
+            $keyArr = explode(':', $key);
+            if(isset($primaryKey[$keyArr[0]])) {
+                unset($primaryKey[$keyArr[0]]);
+                $where[$key] = $val;
+            }
+        }
+        if(count($where) > 0) {
+            $primaryKey = $where;
+        }
         //检查更新的数据是否存在,以主键为主
         $exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->getTable());
         if($exit === null || $exit === false) {
@@ -312,10 +598,10 @@ class Base {
             );
         }
         //获取默认值
+        //更新的时候不使用默认值
+        //$values = array_merge($this->getDefaultValue(), $this->properties());
 
-        $values = array_merge($this->getDefaultValue(), $this->properties());
-
-        $affectedRows = $this->db()->updateObject($this->getTable(), $values, $primaryKey);
+        $affectedRows = $this->db()->updateObject($this->getTable(), $this->properties(), $primaryKey);
         if($this->db()->isError()) {
             return Response::FailUpdate(static::class .'::'. __FUNCTION__,
                 ['_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []]]
@@ -326,37 +612,25 @@ class Base {
         );
     }
 
-    public function updateFields() {
+    /**
+     * 更新字段
+     *
+     * @return mixed|Response
+     * @throws \Exception
+     */
+    final public function updateFields() {
         $properties = $this->properties();
         $fields = $this->entity()->convertToProperties(array_keys($properties));
         $valid = $this->valid($fields);
         if($valid->isError()) {
             return $valid;
         }
-        list($uniqueWhere, $uniqueOr, $exclude, $primaryKey) = $this->condition();
-        /*print_r(
-            [
-                'where' => $uniqueWhere,
-                'or' => $uniqueOr,
-                'exclude' => $exclude,
-                'primary' => $primaryKey
-            ]
-        );*/
+        list($uniqueWhere, $exclude, $primaryKey) = $this->condition();
         // 检查 unique 是否已经存在相关数据
         $diffWhere = array_diff_assoc($uniqueWhere, $primaryKey);
-        $diffOr = array_diff_assoc($uniqueOr, $primaryKey);
-        /*$diffExclude = array_diff_assoc($exclude, $primaryKey);
-
-        print_r(
-            [
-                'diffWhere' => $diffWhere,
-                'diffOr' => $diffOr,
-                'diffExclude' => $diffExclude
-            ]
-        );*/
-
-        if(count($diffWhere) > 0 || count($diffOr) > 0) {
-            $unique = $this->db()->limit(1)->where($diffWhere)->orTerms($diffOr)->exclude($exclude)->selectRow($this->getTable());
+
+        if(count($diffWhere) > 0) {
+            $unique = $this->db()->limit(1)->where($diffWhere)->exclude($exclude)->selectRow($this->getTable());
             if($unique) {
                 return Response::Exist(static::class .'::'. __FUNCTION__,
                     ['_result' => ['code' => Response::DOES_EXIST, 'msg' => '已经存在相关记录', 'body' => $unique]]
@@ -364,6 +638,23 @@ class Base {
             }
         }
 
+        /*$whereHooker = $this->getWhereHooker();
+        if(!empty($whereHooker)) {
+            $primaryKey = $whereHooker;
+        }*/
+        $where = [];
+        $whereHooker = $this->getWhereHooker();
+        foreach ($whereHooker as $key => $val) {
+            $keyArr = explode(':', $key);
+            if(isset($primaryKey[$keyArr[0]])) {
+                unset($primaryKey[$keyArr[0]]);
+                $where[$key] = $val;
+            }
+        }
+        if(count($where) > 0) {
+            $primaryKey = $where;
+        }
+
         //检查更新的数据是否存在,以主键为主
         $exit = $this->db()->limit(1)->where($primaryKey)->selectOne($this->getTable());
         if($exit === null || $exit === false) {
@@ -373,9 +664,10 @@ class Base {
         }
         //获取默认值
 
-        $values = array_merge($this->getDefaultValue(), $this->properties());
+        //更新的时候不使用默认值
+        //$values = array_merge($this->getDefaultValue(), $this->properties());
 
-        $affectedRows = $this->db()->updateObject($this->getTable(), $values, $primaryKey);
+        $affectedRows = $this->db()->updateObject($this->getTable(), $this->properties(), $primaryKey);
         if($this->db()->isError()) {
             return Response::FailUpdate(static::class .'::'. __FUNCTION__,
                 ['_result' => ['code' => Response::FAIL_FOR_UPDATE, 'msg' => $this->db()->getMessage(), 'body' => []]]
@@ -389,12 +681,12 @@ class Base {
     /**
      * 增加或减少某一个字段的值
      *
-     * @return mixed|Qii\Driver\Response
+     * @return mixed|Response
      * @throws \Exception
      */
-    public function incr() {
-        list($where, $or, $exclude, $primary) = $this->condition();
-        unset($where, $or, $exclude);
+    final public function incr() {
+        list($where, $exclude, $primary) = $this->condition();
+        unset($where, $exclude);
         $property = $this->properties();
         $incr = [];
         foreach ($property as $key => $value) {
@@ -417,6 +709,17 @@ class Base {
                 $sets[$key . ':minus'] = $val * -1;
             }
         }
+        $whereHooker = $this->getWhereHooker();
+        if(!empty($whereHooker)) {
+            foreach ($whereHooker as $key => $where) {
+                //去掉where条件里边的sets的值
+                $keys = $this->explode(":", $key);
+                if(isset($diff[$keys[0]])) {
+                    unset($whereHooker[$key]);
+                }
+            }
+            $primary = $whereHooker;
+        }
         $affectedRows = $this->db()->set($sets)->where($primary)->update($this->getTable());
 
         if($this->db()->isError()) {
@@ -432,11 +735,15 @@ class Base {
     /**
      * 获取第一条数据
      *
-     * @return mixed|\Qii\Driver\Response
+     * @return mixed|Response
      * @throws \Exception
      */
-    public function first() {
+    final public function first() {
         $orderBy = $this->getOrderBy();
+        //未设置order by 就以主键为准
+        if(empty($orderBy)) {
+            $orderBy = $this->primaryCondition();
+        }
         foreach ($orderBy as $key => $value) {
             $orderBy[$key] = 'ASC';
         }
@@ -451,15 +758,19 @@ class Base {
     /**
      * 获取第一条数据
      *
-     * @return mixed|\Qii\Driver\Response
+     * @return mixed|Response
      * @throws \Exception
      */
-    public function last() {
+    final public function last() {
         $orderBy = $this->getOrderBy();
+        //未设置order by 就以主键为准
+        if(empty($orderBy)) {
+            $orderBy = $this->primaryCondition();
+        }
         foreach ($orderBy as $key => $value) {
             $orderBy[$key] = 'DESC';
         }
-        $row = $this->db()->orderBy($orderBy)->limit(1)->selectRow($this->getTable());
+        $row = $this->db()->fields($this->getFieldsHooker())->orderBy($orderBy)->limit(1)->selectRow($this->getTable());
         if($this->db()->isError()) {
             return Response::Fail(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => 'Query 失败', 'body' => []]]);
         }
@@ -474,24 +785,56 @@ class Base {
      * @return array
      * @throws \Exception
      */
-    public function lists($page = 1, $pageSize = 20) {
+    final public function lists($page = 1, $pageSize = 20) {
         $count = $this->count();
         if(!$this->initPages($data, $count, $page, $pageSize)) {
             return $data;
         }
-        $order = $this->getOrderBy();
-        $data['lists'] = $this->db()->where($this->properties())->orderBy($order)->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
-
+        $data['lists'] = $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($data['pages']['limitStart'], $pageSize)->selectRows($this->getTable());
         return $data;
     }
 
     /**
+     * 获取所有数据,这里不分页,但支持限条数,通过 setLimitHooker来实现
+     * $this->setLimitHooker(function(){
+     * return [x,y];//从第x条开始获取y条; return [1];//获取一条 return [0];//获取所有的
+     * });
+     * @return mixed
+     * @throws \Exception
+     */
+    final public function listAll() {
+        return $this->rsCondition()->selectRows($this->getTable());
+    }
+    /**
+     * 返回指针
+     * @return mixed
+     * @throws \Exception
+     */
+    final public function rs() {
+        return $this->rsCondition()->rs($this->getTable());
+    }
+
+    /**
+     * 通过此方法获取 rs
+     * @return mixed
+     */
+    private function rsCondition() {
+        $limit = $this->getLimitHooker();
+        if(empty($limit) || !is_array($limit)) {
+            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy());
+        }
+        if(count($limit) == 1) {
+            return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0]);
+        }
+        return $this->db()->fields($this->getFieldsHooker())->where($this->getWhereHooker())->orderBy($this->getOrderBy())->limit($limit[0], $limit[1]);
+    }
+    /**
      * 验证相关字段
      *
      * @param array $fields 字段列表
      * @return Response
      */
-    public function valid($fields = array()) {
+    final public function valid($fields = array()) {
         if(!is_array($fields)) {
             return Response::FailValidate(static::class .'::'. __FUNCTION__, ['_result' => ['code' => Response::FAIL_FOR_VALIDATE, 'msg' => '字段必须是数组', 'fields' => []]]);
         }
@@ -505,9 +848,12 @@ class Base {
                 if(!in_array($key, $fields)) {
                     continue;
                 }
+
                 $field = $this->entity()->convertToField($key);
+                $value = isset($values[$field]) ? $values[$field] : null;
+                $limit = isset($val[2]) ? $val[2]: '';
 
-                $verify = new Verify($val[0], $values[$field] ?? null, strtolower($valid), $val[1], $val[2] ?? '');
+                $verify = new Verify($val[0], $value, strtolower($valid), $val[1], $limit);
                 $res = $verify->valid();
                 if($res->isError()) {
                     $result[] = $res->getResult() .',获取的是 '. $values[$field];
@@ -526,10 +872,10 @@ class Base {
     /**
      * 合并多个 valid 结果 $this->>valids($this->valid(['Uid']), $this->valid(['Nickname', 'Email']));
      * @param ...
-     * @return mixed|Qii\Driver\Response
+     * @return mixed|Response
      * @throws \Exception
      */
-    public function valids() {
+    final public function valids() {
         $validArr = func_get_args();
         if(count($validArr) == 0) {
             return Response::Success(static::class .'::'. __FUNCTION__, ['_result' => true]);
@@ -566,28 +912,37 @@ class Base {
      */
     protected function uniqueCondition() {
         $uniqueWhere = [];
-        $uniqueOr = [];
         $unique = $this->uniqueKey();
         $useWhere = true;
-
+        $condition = [];
+        if(empty($unique)) {
+            return $condition;
+        }
         if(!is_array($unique)) {
             $unique = $this->explode(',', $unique);
-            $useWhere = false;
-            if(count($unique) == 1) {
-                $useWhere  = true;
+            foreach ($unique as $val) {
+                $condition[] = $val;
             }
-        }
-
-        foreach ($unique as $key) {
-            $key = $this->entity()->convertToField($key);
-            $property = $this->entity()->convertToProperty($key);
-            if($useWhere) {
-                $uniqueWhere[$key] = $this->$property;
+        }else{
+            //检查是否是一维数组,一维数组联合唯一
+            if(count($unique) == count($unique, 1)) {
+                $condition[] = $unique;
             }else{
-                $uniqueOr[$key] = $this->$property;
+                $condition = $unique;
+            }
+
+        }
+        foreach ($condition as $item) {
+            $uniqueWhere[] = "OR";
+            $arr = [];
+            foreach ($item as $key) {
+                $key = $this->entity()->convertToField($key);
+                $property = $this->entity()->convertToProperty($key);
+                $arr[$key] = $this->$property;
             }
+            $uniqueWhere[] = $arr;
         }
-        return [$uniqueWhere, $uniqueOr];
+        return $uniqueWhere;
     }
 
     /**
@@ -635,13 +990,13 @@ class Base {
      *
      * @return array [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition]
      */
-    public function condition() {
+    final public function condition() {
         //如果设置了 unique 就先验证唯一性,保存的时候验证 uniqueKey,更新的时候验证uniqueKey并排primaryKey
         //保存数据的时候验证uniqueKey;更新时验证 uniqueKey,并且排除 primaryKey,如果uniqueKey == primaryKey则不去做唯一性验证
-        list($uniqueWhere, $uniqueOr) = $this->uniqueCondition();
+        $uniqueWhere = $this->uniqueCondition();
         $excludeCondition = $this->excludeCondition();
         $primaryCondition = $this->primaryCondition();
-        return [$uniqueWhere, $uniqueOr, $excludeCondition, $primaryCondition];
+        return [$uniqueWhere, $excludeCondition, $primaryCondition];
     }
 
     /**
@@ -649,9 +1004,12 @@ class Base {
      *
      * @return array
      */
-    public function getOrderBy() {
+    final public function getOrderBy() {
         $order = array();
-        $orderBy = $this->orderBy();
+        $orderBy = (array) $this->orderBy();
+        if(isset($this->hooker['order']) && is_callable($this->hooker['order']['func'])) {
+            $orderBy = (array) call_user_func($this->hooker['order']['func'], $order, $this->hooker['order']['args']);
+        }
         foreach ($orderBy as $key => $val) {
             $field = $this->entity()->convertToField($key);
             $val = strtoupper($val);
@@ -660,7 +1018,7 @@ class Base {
             }
             $order[$field] = $val;
         }
-        return $order;
+        return (array) $order;
     }
 
     /**
@@ -709,14 +1067,64 @@ class Base {
         $data['pages']['pageSize'] = (int) $pageSize;
         return true;
     }
+    /**
+     * response as object
+     *
+     * @return mixed|\Qii\Driver\Qii\Driver\Response
+     */
+    final public function response() {
+        $properties = $this->properties();
+        if(!$properties) {
+            return Response::Fail(static::class .'::'. __FUNCTION__,
+                ['_result' => ['code' => Response::FAIL_FOR_SELECT, 'msg' => '未找到相关数据', 'body' => []]
+                ]
+            );
+        }
 
+        return Response::Success(static::class .'::'. __FUNCTION__,
+            ['_result' => ['code' => Response::DO_SUCCESS, 'msg' => '获取成功', 'body' => $properties]]
+        );
+
+    }
     /**
-     * 默认转发到 db model上
+     * 方法名 + ByCache/ByRedis/ByMemcache/会将结果缓存,这里使用的缓存类是通过hooker设置的,否则使用对应的
+     * ByClean 则会移除当前缓存, 默认转发到 db model上
      * @param string $method method
      * @param array $args
      * @return mixed
      */
     public function __call($method, $args) {
+        $cache = '';
+        preg_match("/(byredis|bymemcache|bycache|byclean)$/i", $method, $matches);
+        if($matches && count($matches) > 0) {
+            $cache = strtolower($matches[0]);
+        }
+        if($cache && in_array($cache, ['bymemcache', 'byredis', 'bycache', 'byclean'])){
+            list($func, $cacheID, $config) = $this->getCacheHooker();
+            $policy = $this->getCacheConfig();
+            if($func == null) {
+                $func = $cache == 'bymemcache' ? $this->db()->setCache('memcached', $policy) : $this->db()->setCache('redis', $policy);
+            }
+            $method = substr($method, 0, strlen($cache) * -1);
+            if (method_exists($this, $method)) {
+                $key = get_called_class() .':'. $method .':'. substr(md5(serialize($this->properties())), -16);
+                if($cache == 'byclean') {
+                    return $func->del($key);
+                }
+                if(is_object($func) && method_exists($func, 'get')) {
+                    $res = $func->get($key);
+                    if ($res) {
+                        return unserialize($res);
+                    }
+                }
+                $expiredAt = isset($config['life_time']) ? (int) $config['life_time'] :
+                    (isset($policy['life_time']) ? $policy['life_time'] : $this->defaultExpired);
+                $res = call_user_func_array(array($this, $method), $args);
+                $str = serialize($res);
+                $func->set($key, $str, ['life_time' => $expiredAt]);
+                return $res;
+            }
+        }
         return call_user_func_array(array($this->db(), $method), $args);
     }
 }

+ 161 - 7
src/Driver/Entity/Entity.php

@@ -2,6 +2,7 @@
 namespace Qii\Driver\Entity;
 
 use Qii\Driver\Model;
+use Qii\Driver\Response;
 
 /**
  * 通过数据表生成 entity
@@ -26,6 +27,9 @@ class Entity {
     public function __construct(){
     }
 
+    public function db() {
+        return _loadClass('\Qii\Driver\Model');
+    }
     /**
      * 设置 entity 的名字空间
      * @param string $namespace 名字空间
@@ -68,7 +72,7 @@ class Entity {
      * @throws \Exception
      */
     public function generateProperties($table, $className = '') {
-        $db = new Model();
+        $db = $this->db();
         $tableInfo = $db->getTableInfo($table);
         if($db->isError()) {
             throw new \Exception($db->getError());
@@ -105,6 +109,10 @@ class Entity {
                 $doc[] = "\t/**";
                 $doc[] = "\t * @var float ". $convertField . $comment;
                 $doc[] = "\t */";
+            }else if($tableInfo['rules']['decimal'] && isset($tableInfo['rules']['decimal'][$field])) {
+                $doc[] = "\t/**";
+                $doc[] = "\t * @var decimal ". $convertField . $comment;
+                $doc[] = "\t */";
             }
             if($tableInfo['rules']['default'] && isset($tableInfo['rules']['default'][$field])) {
                 $default = $tableInfo['rules']['default'][$field];
@@ -123,7 +131,35 @@ class Entity {
         $classAndProperty[] = "\n";
         $classAndProperty[] = "use Qii\Driver\Entity\Inf;";
         $classAndProperty[] = "use Qii\Driver\Entity\Base;";
+        $classAndProperty[] = "use Qii\Driver\Response;";
         $classAndProperty[] = "\n";
+        $classAndProperty[] = <<<DOC
+/**
+ * @method \$this setWhereHooker(callable \$func)
+ * @method \$this setOrderHooker(callable \$func)
+ * @method \$this setLimitHooker(callable \$func)
+ * @method \$this setCacheHooker(callable \$func)
+ * @method \$this setQueryFieldsHooker(callable \$func)
+ * @method \$this clear()
+ * @method array properties()
+ * @method bool exit()
+ * @method \$this get()
+ * @method Response info()
+ * @method Response add()
+ * @method Response remove()
+ * @method Response update()
+ * @method Response updateFields()
+ * @method Response incr()
+ * @method Response first()
+ * @method Response last()
+ * @method array lists(int \$page, int \$pageSize)
+ * @method array listAll()
+ * @method Object rs()
+ * @method Response valids
+ * @method array initPages(array \$data, int \$count, int \$page, int \$pageSize)
+ */
+DOC;
+
         $classAndProperty[] = 'class '. $class . ' extends Base implements Inf{';
         $dynamicProperty = [];
         foreach ($properties as $property) {
@@ -150,7 +186,6 @@ class Entity {
      * 动态变化的日期,初始化的时候赋值
      */
 DOC;
-            ;
             $classAndProperty[] = "\tpublic function __construct(){";
             $classAndProperty[] = "\n";
             $classAndProperty[] = "\t}";
@@ -180,6 +215,7 @@ DOC;
 \t}
 DOC;
 
+
         //生成 table rules
         $classAndProperty[] = $this->generateRules($tableInfo);
         $classAndProperty[] = '}';
@@ -195,7 +231,7 @@ DOC;
     public function rules() { 
 DOC;
         $constantJoin[] = "\t\t". '$rules = [];';
-        $next = array_reduce(array_reverse(['required', 'int', 'minlength', 'maxlength', 'timestamp', 'datetime']), function($carry, $item){
+        $next = array_reduce(array_reverse(['required', 'int', 'minlength', 'maxlength', 'decimal', 'timestamp', 'datetime']), function($carry, $item){
             return function() use ($carry, $item){
                 $tableInfo = func_get_args()[0];
                 $constantJoin = func_get_args()[1];
@@ -209,6 +245,9 @@ DOC;
                     case 'int':
                         $join = $this->generateInt($currentRules, $comments);
                         break;
+                    case 'decimal':
+                        $join = $this->generateDecimal($currentRules, $comments);
+                        break;
                     case 'minlength':
                         $join = $this->generateMinLength($currentRules, $comments);
                         break;
@@ -227,27 +266,111 @@ DOC;
         }, function($tableInfo, $constantJoin){
             return $constantJoin;
         })($tableInfo, $constantJoin);
+
         $next[] = "\t\treturn ". '$rules;';
         $next[] = "\t". '}';
+        //主键自动添加
+        $primaryKey = array();
+        if(isset($tableInfo['rules']['pri'])) {
+            $primaryKey = $tableInfo['rules']['pri'];
+        }
+        //保存验证的时候使用 required 并去掉pri和default里边的内容
+        $defaults = array();
+        if(isset($tableInfo['rules']['default'])) {
+            $defaults = array_keys($tableInfo['rules']['default']);
+        }
+        $validForSave = array();
+        if(isset($tableInfo['rules']) && isset($tableInfo['rules']['required'])) {
+            foreach ($tableInfo['rules']['required'] as $field) {
+                if(!in_array($field, $primaryKey) && !in_array($field, $defaults)) {
+                    $validForSave[] = $this->convertToProperty($field);
+                }
+            }
+        }
+        $uniKey = array();
+        if(isset($tableInfo['rules']) && isset($tableInfo['rules']['uniq'])) {
+            foreach ($tableInfo['rules']['uniq'] as $uniq) {
+                $uniKey[] = $this->convertToProperties($uniq);
+            }
+        }
+
+        $next[] = <<<DOC
+    /**
+     * unique (unique 如果是 array 则表示 联合唯一,如果是string则是任意唯一,多个单独唯一使用字符串以逗号隔开, 主键除外)
+     * array(array1, array2) , array1 和array 2单独唯一
+     * @return mixed
+     * @throws \Exception
+     */
+    public function uniqueKey(){
+DOC;
+        if(count($uniKey) > 0) {
+            $uniq = array();
+            foreach ($uniKey as $val) {
+                $uniq[] = "array('". join("','", $val). "')";
+            }
+            $next[] = "\t\treturn array(". join(", ", $uniq). ");";
+        }else{
+            $next[] = <<<DOC
+        return array();
+DOC;
+        }
+
         $next[] = <<<DOC
+    }
 
     /**
+     * 主键
+     *
+     * @return mixed
+     * @throws \Exception
+     */
+    public function primaryKey(){
+DOC;
+        $exclude = "\t\treturn array();";
+        if($primaryKey) {
+            $next[] = "\t\treturn array('". join("','", $this->convertToProperties($primaryKey)). "');";
+            $exclude = "\t\treturn array('". join("','", $this->convertToProperties($primaryKey)). "');";
+        }else{
+            $next[] = <<<DOC
+        throw new \Exception('请设置主键');
+DOC;
+        }
+
+        $next[] = <<<DOC
+    }
+
+    /**
+     * 更新数据的时候,验证唯一需要排除的值,此处仅支持,单个或联合排除,不支持单个排除
+     *
+     * @return array
+     */
+    public function exclude(){
+DOC;
+        $next[] = $exclude;
+        $next[] = <<<DOC
+    }
+    /**
      * 添加时验证的字段,自行添加
      */
     public function validFieldsForAdd(){
-        \$fields = [];
+DOC;
+        if(count($validForSave) > 0) {
+            $next[] = "\t\$fields = array('". join("', '", $validForSave)."');";
+        }else{
+            $next[] = "\t\$fields = array();";
+        }
+        $next[] = <<<DOC
         return \$this->valid(\$fields);
     }
     
     /**
-     * 验证更新时的字段,自行添加
+     * 更新时验证的字段,自行添加
      */
     public function validFieldsForUpdate() {
-        \$fields = [];
+        \$fields = array();
         return \$this->valid(\$fields);
     }
 DOC;
-
         return join("\n", $next);
     }
 
@@ -351,6 +474,13 @@ DOC;
         $constantJoin[] = "\t\t];";
         return $constantJoin;
     }
+
+    /**
+     * int 类型数据
+     * @param $rules
+     * @param $comments
+     * @return array
+     */
     public function generateInt($rules, $comments = array()) {
         $constantJoin = [];
 
@@ -391,6 +521,30 @@ DOC;
 
         return array_merge($constantJoin, $intProperty['min'], $intProperty['max'], $intProperty['number']);
     }
+    public function generateDecimal($rules, $comments = array()) {
+        $constantJoin = [];
+
+        if(!$rules || !is_array($rules)) {
+            return $constantJoin;
+        }
+        $constantJoin = [];
+        $constantJoin[] = "\t\t". '$rules["decimal"] = [';
+        $joinProperty = [];
+        foreach ($rules as $key => $val) {
+            $comment = '';
+            if(isset($comments[$key]) && $comments[$key] != "") {
+                $comment = $comments[$key];
+            }
+
+            $message = ($comment != '' ? $comment : $key) . "格式示例: ". str_repeat(6, min(1, (int)$val[0] - (int)$val[1])) . ".". str_repeat(6, (int)$val[1]);
+            $joinProperty[] = <<<DOC
+    \t\t["{$this->convertToProperty($key)}", "{$message}", "{$val[0]},{$val[1]}"]
+DOC;
+        }
+        $constantJoin[] = join(",\n", $joinProperty);
+        $constantJoin[] = "\t\t];";
+        return $constantJoin;
+    }
     /**
      * 转换为属性,属性会将下划线+小写/大写 转换成大写
      *

+ 1 - 1
src/Driver/Entity/Verify.php

@@ -60,7 +60,7 @@ class Verify {
                 $verify = $this->Validator->verify(
                     [$this->Key => $this->Value],
                     [$this->Key => [$this->Valid => $this->Limit]],
-                    [$this->Key => ['float' => $this->Message]]
+                    [$this->Key => [$this->Valid => $this->Message]]
                 );
                 break;
         }

+ 10 - 7
src/Driver/Model.php

@@ -5,6 +5,7 @@ use Qii\Autoloader\Import;
 use Qii\Autoloader\Psr4;
 use Qii\Config\Consts;
 use Qii\Config\Register;
+use Qii\Exceptions\Variable;
 
 /**
  * 数据库分发器
@@ -27,7 +28,6 @@ use Qii\Config\Register;
  * $fields = array('id' => 1, 'name' => 'test');
  * $test->save($fields);
  */
-
 class Model
 {
     const VERSION = '1.2';
@@ -65,21 +65,24 @@ class Model
      */
     private $model = array();
     /**
-     * @var \Qii\Request\Abstract $_request 请求类
+     * @var \Qii\Request\Http $_request 请求类
      */
     protected $_request;
     /**
-     * @var $_helper helper类
+     * @var \Qii\Autoloader\Helper $_helper helper类
      */
     protected $_helper;
 
-    public function __construct()
+    public function __construct($dbInfo = array())
     {
+        if(!is_array($dbInfo)) {
+            throw new Variable('配置信息必须为数组');
+        }
         $this->_load = Psr4::getInstance()->loadClass('\Qii\Autoloader\Loader');
         $this->_language = Psr4::getInstance()->loadClass('\Qii\Language\Loader');
         $this->_request = Psr4::getInstance()->loadClass('Qii\Request\Http');
         $this->_helper = Psr4::getInstance()->loadClass('Qii\Autoloader\Helper');
-        $this->_dbInfo = Register::getAppConfigure(Register::get(Consts::APP_DB));
+        $dbInfo = Register::mergeAppConfigure(Register::getAppConfigure(Consts::APP_DB), $dbInfo);
         if (isset($this->_dbInfo['driver'])) {
             $this->_driver = $this->_dbInfo['driver'];
         }
@@ -96,10 +99,10 @@ class Model
         $this->db = Psr4::getInstance()->loadClass(
             '\Qii\Driver\\' . ucWords($this->_driver) . '\Driver',
             Psr4::getInstance()->loadClass(
-                '\Qii\Driver\\' . ucWords($this->_driver) . '\Connection'
+                '\Qii\Driver\\' . ucWords($this->_driver) . '\Connection', $dbInfo
             )
         );
-        $this->db->_debugSQL = isset($this->_dbInfo['debug']) ? $this->_dbInfo['debug'] : false;
+        $this->db->_debugSQL = isset($dbInfo['debug']) ? $dbInfo['debug'] : false;
         return $this;
     }
 

+ 8 - 8
src/Driver/Mysql/Connection.php

@@ -31,16 +31,16 @@ class Connection extends ConnBase implements ConnIntf
      */
     public function getReadConnection()
     {
-        if (self::$_readConnection != null) {
-            return self::$_readConnection;
-        }
         list($dbInfo, $useSlave) = $this->getDBInfoAndSalve();
+        if (isset(self::$_readConnection[$dbInfo['db']]) && self::$_readConnection[$dbInfo['db']] != null) {
+            return self::$_readConnection[$dbInfo['db']];
+        }
 
         if ($useSlave) {
             try {
                 $connection = mysql_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
                 if (!$connection) throw new Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysql_error())), true);
-                return self::$_readConnection = $connection;
+                return self::$_readConnection[$dbInfo['db']] = $connection;
             } catch (Exception  $e) {
                 return $this->getWriteConnection();
             }
@@ -54,14 +54,14 @@ class Connection extends ConnBase implements ConnIntf
      */
     public function getWriteConnection()
     {
-        if (self::$_writeConnection != null) {
-            return self::$_writeConnection;
-        }
         $dbInfo = $this->_dbInfo['master'];
+        if (isset(self::$_writeConnection[$dbInfo['db']]) && self::$_writeConnection[$dbInfo['db']] != null) {
+            return self::$_writeConnection[$dbInfo['db']];
+        }
         try {
             $connection = mysql_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
             if (!$connection) throw new Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysql_error())), true);
-            return self::$_writeConnection = $connection;
+            return self::$_writeConnection[$dbInfo['db']] = $connection;
         } catch (Exception  $e) {
             throw new Errors(\Qii::i(1500, $dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db'], toUTF8($e->getMessage())));
         }

+ 8 - 8
src/Driver/Mysqli/Connection.php

@@ -31,17 +31,17 @@ class Connection extends ConnBase implements ConnIntf
 	 */
 	public function getReadConnection()
 	{
-        if (self::$_readConnection != null) {
-            return self::$_readConnection;
-        }
         list($dbInfo, $useSlave) = $this->getDBInfoAndSalve();
+        if (isset(self::$_readConnection[$dbInfo['db']]) && self::$_readConnection[$dbInfo['db']] != null) {
+            return self::$_readConnection[$dbInfo['db']];
+        }
 
 		if ($useSlave) {
 			try {
 				$connection = mysqli_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
 				if (!$connection) throw new Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysqli_connect_error())), true);
 				mysqli_select_db($connection, $dbInfo['db']);
-				return self::$_readConnection = $connection;
+				return self::$_readConnection[$dbInfo['db']] = $connection;
 			} catch (Exception  $e) {
 				return $this->getWriteConnection();
 			}
@@ -55,15 +55,15 @@ class Connection extends ConnBase implements ConnIntf
 	 */
 	public function getWriteConnection()
 	{
-        if (self::$_writeConnection != null) {
-            return self::$_writeConnection;
+        $dbInfo = $this->_dbInfo['master'];
+        if (isset(self::$_writeConnection[$dbInfo['db']]) && self::$_writeConnection[$dbInfo['db']] != null) {
+            return self::$_writeConnection[$dbInfo['db']];
         }
-		$dbInfo = $this->_dbInfo['master'];
 		try {
 			$connection = @mysqli_connect($dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db']);
 			if (!$connection) throw new Errors(\Qii::i(1501, iconv("GBK", "UTF-8//TRANSLIT", mysqli_connect_error())), true);
 			mysqli_select_db($connection, $dbInfo['db']);
-			return self::$_writeConnection = $connection;
+			return self::$_writeConnection[$dbInfo['db']] = $connection;
 		} catch (Exception  $e) {
 			throw new Errors(\Qii::i(1500, $dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db'], toUTF8($e->getMessage())), __LINE__);
 		}

+ 10 - 10
src/Driver/Pdo/Connection.php

@@ -16,8 +16,9 @@ class Connection extends ConnBase implements ConnIntf
 {
 	const VERSION = '1.2';
 
-	public function __construct()
+	public function __construct($dbInfo = array())
 	{
+        Register::mergeAppConfigure(Register::getAppConfigure(Consts::APP_DB), $dbInfo);
 		$this->_dbInfo = Register::getAppConfigure(Register::get(Consts::APP_DB));
 		if(!isset($this->_dbInfo['use_db_driver'])) $this->_dbInfo['use_db_driver'] = 'mysql';
 	}
@@ -28,10 +29,10 @@ class Connection extends ConnBase implements ConnIntf
 	 */
 	public function getReadConnection()
 	{
-        if (self::$_readConnection != null) {
-            return self::$_readConnection;
-        }
         list($dbInfo, $useSlave) = $this->getDBInfoAndSalve();
+        if (isset(self::$_readConnection[$dbInfo['db']]) && self::$_readConnection[$dbInfo['db']] != null) {
+            return self::$_readConnection[$dbInfo['db']];
+        }
 		if ($useSlave) {
 			try {
 				if ($this->_dbInfo['use_db_driver'] == 'mssql') {
@@ -39,8 +40,7 @@ class Connection extends ConnBase implements ConnIntf
 				} else {
 					$dsn = $this->_dbInfo['use_db_driver'] . ":host=" . $dbInfo['host'] . ";dbname=" . $dbInfo['db'];
 				}
-				self::$_readConnection = new \PDO($dsn, $dbInfo['user'], $dbInfo['password']);
-                return self::$_readConnection;
+				return self::$_readConnection[$dbInfo['db']] = new \PDO($dsn, $dbInfo['user'], $dbInfo['password']);
 			} catch (Exception  $e) {
 				return $this->getWriteConnection();
 			}
@@ -54,17 +54,17 @@ class Connection extends ConnBase implements ConnIntf
 	 */
 	public function getWriteConnection()
 	{
-	    if (self::$_writeConnection != null) {
-	        return self::$_writeConnection;
+        $dbInfo = $this->_dbInfo['master'];
+	    if (isset(self::$_writeConnection[$dbInfo['db']]) && self::$_writeConnection[$dbInfo['db']] != null) {
+	        return self::$_writeConnection[$dbInfo['db']];
         }
-		$dbInfo = $this->_dbInfo['master'];
 		try {
 			if ($this->_dbInfo['use_db_driver'] == 'mssql') {
 				$dsn = 'odbc:Driver={SQL Server};Server=' . $dbInfo['host'] . ';Database=' . $dbInfo['db'] . ';';
 			} else {
 				$dsn = $this->_dbInfo['use_db_driver'] . ":host=" . $dbInfo['host'] . ";dbname=" . $dbInfo['db'];
 			}
-			return self::$_writeConnection = new \PDO($dsn, $dbInfo['user'], $dbInfo['password']);
+			return self::$_writeConnection[$dbInfo['db']] = new \PDO($dsn, $dbInfo['user'], $dbInfo['password']);
 		} catch (Exception  $e) {
 			throw new Errors(\Qii::i(1500, $dbInfo['host'], $dbInfo['user'], $dbInfo['password'], $dbInfo['db'], toUTF8($e->getMessage())), __LINE__);
 		}

+ 1 - 1
src/Driver/Pdo/Driver.php

@@ -117,7 +117,7 @@ class Driver extends Base implements Intf
         }
         $this->sql = $sql;
         $this->db['CURRENT'] = $this->connection->getConnectionBySQL($sql);
-        $rs = $this->db['CURRENT']->query($sql);
+        $rs = @$this->db['CURRENT']->query($sql);
         $this->setError();
         if (!$rs) {
             $error = $this->getError('error');

+ 5 - 4
src/Driver/TraitCache.php

@@ -15,8 +15,8 @@ trait TraitCache
      */
     final public function setCache($cache, $policy)
     {
-        Import::requires(QII_DIR . DS . 'Qii' . DS . 'Cache.php');
-        $this->cache = Psr4::loadClass('\Qii\Cache', $cache)->initialization($policy);//载入cache类文件
+        $this->cache = \Qii::getInstance()->setCache($cache, $policy);
+        return $this->cache;
     }
 
     /**
@@ -24,11 +24,12 @@ trait TraitCache
      *
      * @param String $id
      * @param Array $value
+     * @param array $policy ['life_time' => 过期时间]
      * @return Bool
      */
-    final public function cache($id, $value)
+    final public function cache($id, $value, $policy = array())
     {
-        return $this->cache->set($id, $value);
+        return $this->cache->set($id, $value, $policy);
     }
 
     /**

+ 34 - 9
src/Driver/TraitDatabase.php

@@ -71,8 +71,12 @@ trait TraitDatabase
      */
     public function getTableInfo($table, $database = null, $autoIncr = null)
     {
+        static $tableInfo;
+        if(isset($tableInfo[$table])) {
+            return $tableInfo[$table];
+        }
         if (!$database) $database = $this->currentDB;
-        $sql = "SELECT * from information_schema.COLUMNS where table_name = '" . $table . "' and table_schema = '" . $database . "'";
+        $sql = "SELECT * from information_schema.COLUMNS where table_name = '" . $table . "' and table_schema = '" . $database . "' ORDER BY ORDINAL_POSITION ASC";
         $data = array(
             'table' => $table,
             'fields' => array(),
@@ -81,7 +85,6 @@ trait TraitDatabase
                 'required' => array()
             )
         );
-
         $rs = $this->setQuery($sql);
         $maxRange = [
             'bigint' => [-1 * pow(2, 63), pow(2, 63)-1],
@@ -91,21 +94,26 @@ trait TraitDatabase
             'tinyint' => [-128, 127],
             'integer' => [-1 * pow(2, 31), pow(2, 31)-1]
         ];
+        $data['type'] = [];
         while ($row = $rs->fetch()) {
             $data['fields'][] = $row['COLUMN_NAME'];
+            $data['type'][$row['DATA_TYPE']][] = $row['COLUMN_NAME'];
             if ($row['EXTRA']) {
                 $data['rules']['extra'][$row['EXTRA']][] = $row['COLUMN_NAME'];
             }
             if ($row['COLUMN_KEY'] == 'PRI') {
                 $data['rules']['pri'][] = $row['COLUMN_NAME'];
                 $data['rules']['required'][] = $row['COLUMN_NAME'];
+            } else if($row['COLUMN_KEY'] == 'UNI'){
+                //$data['rules']['uni'][] = $row['COLUMN_NAME'];
+                $data['rules']['required'][] = $row['COLUMN_NAME'];
             }else if ($row['IS_NULLABLE'] == 'NO') {
                 $data['rules']['required'][] = $row['COLUMN_NAME'];
             }
             if (in_array($row['DATA_TYPE'], array('varchar', 'char'))) {
                 $data['rules']['maxlength'][$row['COLUMN_NAME']] = $row['CHARACTER_MAXIMUM_LENGTH'];
             }
-            if (in_array($row['DATA_TYPE'], array('mediumtext', 'TINYTEXT', 'text', 'longtext'))) {
+            if (in_array($row['DATA_TYPE'], array('mediumtext', 'tinytext', 'text', 'longtext'))) {
                 $data['rules']['text'][] = $row['COLUMN_NAME'];
             }
             if (in_array($row['DATA_TYPE'], array('bigint', 'int', 'mediumint', 'smallint', 'tinyint', 'integer'))) {
@@ -115,10 +123,13 @@ trait TraitDatabase
                     $data['rules']['number'][] = $row['COLUMN_NAME'];
                 }
             }
-            if (in_array($row['DATA_TYPE'], array('float', 'double', 'decimal'))) {
+            if (in_array($row['DATA_TYPE'], array('float', 'double'))) {
                 $data['rules']['float'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
             }
-            if (in_array($row['DATA_TYPE'], array('timestamp', 'datatime'))) {
+            if (in_array($row['DATA_TYPE'], array('decimal'))) {
+                $data['rules']['decimal'][$row['COLUMN_NAME']] = $this->getValueFromBrackets($row['COLUMN_TYPE']);
+            }
+            if (in_array($row['DATA_TYPE'], array('timestamp', 'datetime'))) {
                 $data['rules']['timestamp'][] = $row['COLUMN_NAME'];
             }
             if (in_array($row['DATA_TYPE'], array('enum', 'set'))) {
@@ -131,7 +142,22 @@ trait TraitDatabase
                 $data['comment'][$row['COLUMN_NAME']] = $row['COLUMN_COMMENT'];
             }
         }
+
+        $sql = "SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE table_name = '" . $table . "'";
+        $rs = $this->setQuery($sql);
         $data['sql'] = $this->getTableSQL($table, $database, $autoIncr);
+        preg_match_all("/UNIQUE\sKEY\s`(.*?)`\s\(`(.*?)`\)/", $data['sql'], $matches);
+        $uniqKeys = [];
+        if(count($matches) == 3) {
+            foreach ($matches[2] as $key => $match) {
+                //$uniqKeys[$matches[1][$key]] = explode("`,`", $match);
+                $uniqKeys[] = explode("`,`", $match);
+            }
+        }
+        if(count($uniqKeys) > 0) {
+            $data['rules']['uniq'] = $uniqKeys;
+        }
+        $tableInfo[$table] = $data;
         return $data;
     }
 
@@ -252,13 +278,12 @@ trait TraitDatabase
      */
     public function getTable($table)
     {
+        //去掉表名及数据库名的`符号,重新添加,避免重复
+        $table = str_replace("`", '', $table);
         list($database, $tableName) = array_pad(explode('.', $table), 2, '');
-
         if ($tableName) {
-            $tableName = stristr($tableName, '`') || stristr($tableName, " ") ? $tableName : '`'. $tableName .'`';
             return "`{$database}`.`{$tableName}`";
         }
-        $table = stristr($table, '`') || stristr($tableName, " ")  ? $table : '`'. $table .'`';
-        return $table;
+        return '`'. $table .'`';
     }
 }

+ 5 - 0
src/Exceptions/Errors.php

@@ -2,6 +2,7 @@
 
 namespace Qii\Exceptions;
 
+use Qii\Autoloader\Factory;
 use Qii\Autoloader\Import;
 use Qii\Autoloader\Psr4;
 use Qii\Config\Consts;
@@ -49,6 +50,10 @@ class Errors extends \Exception
      */
     public static function getError($e)
     {
+        //设置状态码
+        $code = !in_array($e->getCode(), array(404, 400, 200)) ? 500 : $e->getCode();
+        Factory::getInstance('\Qii\Response\Http')->setResponseCode($code);
+
         $message = array();
         if (\Qii::getInstance()->request == null) {
             \Qii::getInstance()->request = \Qii::getInstance()->getRequest();

+ 40 - 2
src/Functions/Funcs.php

@@ -2,7 +2,45 @@
 
 use Qii\Autoloader\Import;
 use Qii\Autoloader\Psr4;
-
+use Qii\Language\Loader;
+if (!function_exists('getallheaders')) {
+    /**
+     * Get all HTTP header key/values as an associative array for the current request.
+     *
+     * @return string[string] The HTTP header key/value pairs.
+     */
+    function getallheaders()
+    {
+        $headers = array();
+        $copy_server = array(
+            'CONTENT_TYPE'   => 'Content-Type',
+            'CONTENT_LENGTH' => 'Content-Length',
+            'CONTENT_MD5'    => 'Content-Md5',
+        );
+        foreach ($_SERVER as $key => $value) {
+            if (substr($key, 0, 5) === 'HTTP_') {
+                $key = substr($key, 5);
+                if (!isset($copy_server[$key]) || !isset($_SERVER[$key])) {
+                    $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
+                    $headers[$key] = $value;
+                }
+            } elseif (isset($copy_server[$key])) {
+                $headers[$copy_server[$key]] = $value;
+            }
+        }
+        if (!isset($headers['Authorization'])) {
+            if (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
+                $headers['Authorization'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
+            } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
+                $basic_pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : '';
+                $headers['Authorization'] = 'Basic ' . base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $basic_pass);
+            } elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
+                $headers['Authorization'] = $_SERVER['PHP_AUTH_DIGEST'];
+            }
+        }
+        return $headers;
+    }
+}
 /**
  * Qii ...
  * @return null|Qii|Qii\Autoloader\Psr4
@@ -42,7 +80,7 @@ function _log() {
  */
 function _language($language, $dir = '')
 {
-	\Qii\Language\Loader::getInstance()->load($language, $dir);
+	Loader::getInstance()->load($language, $dir);
 }
 
 /**

+ 0 - 1
src/Library/Third/hightman/Client.php

@@ -130,7 +130,6 @@ class Client
      */
     public function clearHeader()
     {
-        parent::clearHeader();
         $this->applyDefaultHeader();
     }
 

+ 3 - 2
src/Library/Validate.php

@@ -530,8 +530,9 @@ class Validate
     public function decimal($str, $decimal)
     {
         $decimalArr = explode(',', $decimal);
-
-        return preg_match("/^(([1-9]\d*)|0{1,". $decimalArr[0] ."})(\.\d{".$decimalArr[1]."})?$/", $str);
+        $left = $decimalArr[0] - $decimalArr[1];
+        $right = $decimalArr[1];
+        return preg_match("/^\d{1,".$left."}\.\d{".$right."}$/", $str);;
     }
 
     /**

+ 39 - 7
src/Qii.php

@@ -25,12 +25,19 @@ define('OS', strtoupper(substr(PHP_OS, 0, 3)));
 
 define('IS_CLI', php_sapi_name() == 'cli' ? true : false);
 if (IS_CLI) {
-    $args = is_array($argv) && count($argv) > 0 ? $argv : array();
+    $args = isset($argv) && is_array($argv) && count($argv) > 0 ? $argv : array();
     if(count($args) == 0 && isset($_SERVER['argv'])
         && is_array($_SERVER['argv']) && count($_SERVER['argv']) > 0) {
         $args = $_SERVER['argv'];
     }
-    define('PATH_INFO', array_pop($args));
+    $cliAgs = QiiHandleCliArgs($args);
+    $_SERVER['__CLI_ARGS'] = $cliAgs;
+    $_SERVER['REQUEST_URI'] = $cliAgs[1] . ($cliAgs[3] ? '?'. $cliAgs[3] : '');
+    $_SERVER['QUERY_STRING'] = $cliAgs[3];
+    $_SERVER['SCRIPT_NAME'] = $cliAgs[0];
+    $_GET = $cliAgs[2];
+    $_POST = $cliAgs[2];
+    define('PATH_INFO', $cliAgs[1]);
 } else {
     define('PATH_INFO', isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '');
 }
@@ -54,14 +61,39 @@ Import::requires(
     )
 );
 
-
+function QiiHandleCliArgs($parameter) {
+    $fileName = array_shift($parameter);
+    $uri = array_shift($parameter);
+    if(substr($uri, 0, 1) != '/') {
+        $uri = "/". $uri;
+    }
+    //解析parameter
+    $params = array();
+    $query = [];
+    foreach ($parameter as $value) {
+        preg_match("/\{(?P<name>(.*?))(\=|\:)(?P<value>(.*).+)\}/", $value, $matches);
+        if($matches && isset($matches['name'])) {
+            $params[$matches['name']] = $matches['value'];
+            $query[] = $matches['name'] .'='. urlencode($matches['value']);
+        }else {
+            preg_match("/(?P<name>(.*?))(\=|\:)(?P<value>(.*).+)/", $value, $matches);
+            if($matches && isset($matches['name'])) {
+                $params[$matches['name']] = $matches['value'];
+                $query[] = $matches['name'] . '=' . urlencode($matches['value']);
+            }else{
+                $parameter[$value] = '';
+                $query[] = $value .'=';
+            }
+        }
+    }
+    return array($fileName, $uri, $params, join("&", $query));
+}
 class Qii extends Application
 {
     public function __construct()
     {
         parent::__construct();
     }
-
     /**
      * Instance func
      *
@@ -83,7 +115,7 @@ class Qii extends Application
      */
     public static function setPrivate($name, $value)
     {
-        Psr4::getInstance()->loadClass('\Qii\Config\Arrays')->setPrivate($name, $value);
+        return Psr4::getInstance()->loadClass('\Qii\Config\Arrays')->setPrivate($name, $value);
     }
 
     /**
@@ -208,8 +240,8 @@ if (!function_exists('catch_fatal_error')) {
 $namespace = _include(QII_DIR . DS . 'Config' . DS . 'Namespace.php');
 Psr4::getInstance()
     ->register()
-    ->setUseNamespaces($namespace['setUseNamespace'] ?? [])
-    ->addNamespaces($namespace['addNamespace'] ?? []);
+    ->setUseNamespaces(isset($namespace['setUseNamespace']) ? $namespace['setUseNamespace']: [])
+    ->addNamespaces(isset($namespace['addNamespace']) ? $namespace['addNamespace'] : []);
 
 //加载默认语言包
 Factory::getInstance('\Qii\Language\Loader')->load('error', QII_DIR . DS . 'Language');

+ 10 - 0
src/Request/Http.php

@@ -219,6 +219,16 @@ final class Http extends Request
     }
 
     /**
+     * set to params
+     *
+     * @param string $key 关键字
+     * @param mix $val 值
+     * @return void
+     */
+    public function set($key, $val) {
+        $this->params[$key] = $val;
+    }
+    /**
      * isXmlHttpRequest
      *
      * @param void

+ 0 - 220
src/Request/Simple.php

@@ -1,220 +0,0 @@
-<?php
-namespace Qii\Request;
-
-use Qii\Base\Request;
-
-final class Simple extends Request
-{
-	/**
-	 * __construct
-	 *
-	 * @param string $module
-	 * @param string $controller
-	 * @param string $action
-	 * @param string $method
-	 * @param array $params
-	 */
-	public function __construct ($module, $controller, $action, $method, $params = null)
-	{
-		if ($params && !is_array($params)) {
-			trigger_error('Expects the params is an array', E_USER_ERROR);
-			return false;
-		}
-
-		if (is_string($method)) {
-			$this->method = $method;
-		} else {
-			if (isset($_SERVER['REQUEST_METHOD'])) {
-				$this->method = $_SERVER['REQUEST_METHOD'];
-			} else {
-				if (!strncasecmp(PHP_SAPI, 'cli', 3)) {
-					$this->method = 'CLI';
-				} else {
-					$this->method = 'Unknown';
-				}
-			}
-		}
-
-		if ($module || $controller || $action) {
-			if ($module && is_string($module)) {
-				$this->module = $module;
-			} else {
-				$this->module = YAF_G('default_module');
-			}
-
-			if ($controller && is_string($controller)) {
-				$this->controller = $controller;
-			} else {
-				$this->controller = YAF_G('default_controller');
-			}
-
-			if ($action && is_string($action)) {
-				$this->action = $action;
-			} else {
-				$this->controller = YAF_G('default_action');
-			}
-
-			$this->routed = true;
-		} else {
-			$argv = $this->getServer('argv');
-			if (is_array($argv)) {
-				foreach($argv as $value) {
-					if (is_string($value)) {
-						if (strncasecmp($value, 'request_uri=', 12)) {
-							continue;
-						}
-						$query = substr($value, 12);
-						break;
-					}
-				}
-			}
-
-			if (empty($query)) {
-				$this->uri = '';
-			} else {
-				$this->uri = $query;
-			}
-		}
-
-		if ($params && is_array($params)) {
-			$this->params = $params;
-		} else {
-			$this->params = array();
-		}
-        parent::__construct();
-	}
-	
-	/**
-	 * getQuery
-	 *
-	 * @param string $name
-	 * @param mixed $default
-	 * @return mixed
-	 */
-	public function getQuery($name = null, $default = null)
-	{
-		if (is_null($name)) {
-			return $_GET;
-		} elseif (isset($_GET[$name])) {
-			return $_GET[$name];
-		}
-		return $default;
-	}
-
-	/**
-	 * getRequest
-	 *
-	 * @param string $name
-	 * @param mixed $default
-	 * @return mixed
-	 */
-	public function getRequest($name = null, $default = null)
-	{
-		if (is_null($name)) {
-			return $_REQUEST;
-		} elseif (isset($_REQUEST[$name])) {
-			return $_REQUEST[$name];
-		}
-		return $default;
-	}
-
-	/**
-	 * getPost
-	 *
-	 * @param string $name
-	 * @param mixed $default
-	 * @return mixed
-	 */
-	public function getPost($name = null, $default = null)
-	{
-		if (is_null($name)) {
-			return $_POST;
-		} elseif (isset($_POST[$name])) {
-			return $_POST[$name];
-		}
-		return $default;
-	}
-
-	/**
-	 * getCookie
-	 *
-	 * @param string $name
-	 * @param mixed $default
-	 * @return mixed
-	 */
-	public function getCookie($name = null, $default = null)
-	{
-		if (is_null($name)) {
-			return $_COOKIE;
-		} elseif (isset($_COOKIE[$name])) {
-			return $_COOKIE[$name];
-		}
-		return $default;
-	}
-
-	/**
-	 * getFiles
-	 *
-	 * @param string $name
-	 * @param mixed $default
-	 * @return mixed
-	 */
-	public function getFiles($name = null, $default = null)
-	{
-		if (is_null($name)) {
-			return $_FILES;
-		} elseif (isset($_FILES[$name])) {
-			return $_FILES[$name];
-		}
-		return $default;
-	}
-
-	/**
-	 * get [params -> post -> get -> cookie -> server]
-	 *
-	 * @param string $name
-	 * @param mixed $default
-	 * @return mixed
-	 */	
-	public function get($name, $default = null)
-	{
-		if (isset($this->params[$name])) {
-			return $this->params[$name];
-		} elseif (isset($_POST[$name])) {
-			return $_POST[$name];
-		} elseif (isset($_GET[$name])) {
-			return $_GET[$name];
-		} elseif (isset($_COOKIE[$name])) {
-			return $_COOKIE[$name];
-		} elseif (isset($_SERVER[$name])) {
-			return $_SERVER[$name];
-		}
-		return $default;
-	}
-
-	/**
-	 * isXmlHttpRequest
-	 *
-	 * @param void
-	 * @return boolean
-	 */	
-	public function isXmlHttpRequest()
-	{
-		$header = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? $_SERVER['X-Requested-With'] : '';
-		if (is_string($header) && strncasecmp('XMLHttpRequest', $header, 14) == 0) {
-			return true;
-		}
-		return false;
-	}
-
-	/**
-	 * __clone
-	 *
-	 * @param void
-	 */
-	private function __clone()
-	{
-		
-	}
-
-}

+ 10 - 42
src/Request/Url/Base.php

@@ -257,56 +257,24 @@ abstract class Base
      */
     public function CliParams($key = '')
     {
-        $argv = array();
-        if (isset($_SERVER['argv'])) $argv = $_SERVER['argv'];
-        //修正部分服务器Rewrite 后再加参数不识别的问题(直接进入命令行的模式)
-        if ($argv && $_SERVER['PHP_SELF'] == $_SERVER['SCRIPT_NAME']) {
-            if (count($argv) == 1) return;
-            array_shift($argv);
-            $path = $argv[0];
-            if($query = stristr($path, '?')) {
-                $path = str_replace(stristr($path, '?'), '', $path);
-            }
-            $args = (array)$this->parseArgs($path);
-            //处理GET或POST方法 数据结构 key1=value1 key2=value2 键和值中间不能有空格
-            if($query && strlen($query) > 1) {
-                $queryArr = explode("&", substr($query, 1));
-                foreach ($queryArr as $value) {
-                    list($index, $val) = explode('=', $value, 2);
-                    $args[$index] = $val;
-                }
-            }
-            if ($_SERVER['argc'] > 2) {
-                for ($i = 1; $i < $_SERVER['argc'] - 1; $i++) {
-                    list($index, $val) = explode('=', $argv[$i], 2);
-                    $args[$index] = $val;
-                }
-            }
-            
-            $this->pathArgs = $args;
-            if ($args && $key != '') {
-                return isset($args[$key]) ? $args[$key] : '';
-            }
+        $args = array();
+        if(!isset($_SERVER['__CLI_ARGS'])) {
             return $args;
+        }else{
+            $args = $_SERVER['__CLI_ARGS'][2];
+        }
+        if ($args && $key != '') {
+            return isset($args[$key]) ? $args[$key] : null;
         }
         return array();
     }
 
     public function getCliPathArgs() {
-        $argv = array();
         $args = array();
-        if (isset($_SERVER['argv'])) $argv = $_SERVER['argv'];
-        //修正部分服务器Rewrite 后再加参数不识别的问题(直接进入命令行的模式)
-        if ($argv && $_SERVER['PHP_SELF'] == $_SERVER['SCRIPT_NAME']) {
-            if (count($argv) == 1) return $args;
-            array_shift($argv);
-            $path = $argv[0];
-            if(stristr($path, '?')) {
-                $path = str_replace(stristr($path, '?'), '', $path);
-            }
-            $args = (array)$this->parseArgs($path);
+        if(!isset($_SERVER['__CLI_ARGS'])) {
+            return $args;
         }
-        return $args;
+        return (array) $this->parseURI($_SERVER['__CLI_ARGS'][1]);
     }
 
     /**

+ 23 - 8
src/Request/Url/Short.php

@@ -7,6 +7,27 @@ class Short extends Base implements Intf
 	{
 		parent::__construct($mode);
 	}
+
+    /**
+     * 只匹配URI中的内容
+     *
+     * @param $params
+     * @return array|void
+     * @throws \Qii\Exceptions\Unsupported
+     */
+    public function parseURI($params) {
+        $this->checkMode($this->_mode);
+        if ($params == '') return;
+        $params = ltrim($params, "/");
+        $argvArray = explode("/", $params);
+        $data = array();
+        if (is_array($argvArray)) {
+            foreach ($argvArray AS $arg) {
+                $data[] = $arg;
+            }
+        }
+        return $data;
+    }
 	/**
 	 * 解析uri获取参数 => 值
 	 * @param string $params uri
@@ -16,13 +37,7 @@ class Short extends Base implements Intf
 	{
 		$this->checkMode($this->_mode);
 		if ($params == '') return;
-		$argvArray = explode("/", $params);
-		$data = array();
-		if (is_array($argvArray)) {
-			foreach ($argvArray AS $arg) {
-				$data[] = $arg;
-			}
-		}
+        $data = $this->parseURI($params);
 		foreach ($_GET AS $key => $val) {
 			$data[$key] = $val;
 		}
@@ -36,7 +51,7 @@ class Short extends Base implements Intf
 	public function decodeArgs($urlArray, $k = '')
 	{
 		$this->checkMode($this->_mode);
-		if (!empty($k)) return $urlArray[$k] == 'NULL' ? '' : $urlArray[$k];
+		if (!empty($k)) return $urlArray[$k] == 'NULL' || $urlArray[$k] == null ? '' : $urlArray[$k];
 		return $urlArray;
 	}
 }

+ 0 - 28
src/Response/Http.php

@@ -5,34 +5,6 @@ use Qii\Exceptions\Response;
 
 class Http extends \Qii\Base\Response
 {
-    protected $_sendHeader = true;
-    protected $_responseCode = 200;
-
-    /**
-     * Set HTTP response code to use with headers
-     *
-     * @param int $code
-     * @return Qii\Response\Http
-     */
-    public function setResponseCode($code)
-    {
-        if (!is_int($code) || (100 > $code) || (599 < $code)) {
-            throw new Response('Invalid HTTP response code');
-        }
-
-        $this->_responseCode = $code;
-        return $this;
-    }
-
-    /**
-     * Retrieve HTTP response code
-     *
-     * @return int
-     */
-    public function getResponseCode()
-    {
-        return $this->_responseCode;
-    }
 
     /**
      * 判断是否可以输出header

+ 5 - 3
src/Router/Parse/Normal.php

@@ -1,6 +1,8 @@
 <?php
 namespace Qii\Router\Parse;
 
+use Qii\Config\Register;
+
 /**
  * Route规则文件
  * 兼容以前版本的匹配规则
@@ -172,10 +174,10 @@ class Normal
         {
             if(preg_match("/\{[\d]{1,}\}/", $val)) {
                 $index = str_replace(array('{', '}'), '', $val);
-                $replacements[$key] = $dirInfo[$index] ?? 'index';
+                $replacements[$key] = isset($dirInfo[$index]) && $dirInfo[$index] != '' ? $dirInfo[$index] : Register::get("APP_DEFAULT_ACTION");
                 $rulesVal = preg_replace("/\{[\d]{1,}\}/", $replacements[$key], $rulesVal, 1);
             }else if($val == '*'){
-                $replacements[$key] = $dirInfo[$maxIndex] ?? 'index';
+                $replacements[$key] = $dirInfo[$maxIndex] ? $dirInfo[$maxIndex] : 'index';
                 //一次只替换一个,用于匹配多次
                 $rulesVal = preg_replace("/[\*]{1}/", $replacements[$key], $rulesVal, 1);
                 $maxIndex++;
@@ -183,7 +185,7 @@ class Normal
         }
         list($controller, $action) = explode(":", $rulesVal);
         $match['controller'] = $controller;
-        $match['action'] = $action ?? 'index';
+        $match['action'] = $action ? $action: Register::get("APP_DEFAULT_ACTION");
         $match['replacements'] = $replacements;
         $match['rulesVal'] = $rulesVal;