From 7608d4d0f7a69edbbb89f6dc6e80a6b8083c8835 Mon Sep 17 00:00:00 2001 From: molong Date: Mon, 26 Sep 2016 13:55:47 +0800 Subject: [PATCH] =?UTF-8?q?=E5=86=85=E6=A0=B8=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/convention.php | 6 +- core/library/think/App.php | 47 ++++++++++--- core/library/think/File.php | 41 ++++------- core/library/think/Loader.php | 2 +- core/library/think/Model.php | 26 ++++--- core/library/think/Request.php | 86 +++++++++++++++++++++-- core/library/think/Response.php | 12 ++++ core/library/think/Route.php | 34 ++++++--- core/library/think/Template.php | 2 + core/library/think/db/Query.php | 2 +- core/library/think/db/connector/Pgsql.php | 6 +- core/library/think/model/Merge.php | 4 +- core/library/think/model/Relation.php | 10 ++- core/library/think/response/Redirect.php | 2 +- core/library/think/view/driver/Php.php | 2 + core/library/think/view/driver/Think.php | 6 +- core/library/traits/controller/Jump.php | 4 +- 17 files changed, 213 insertions(+), 79 deletions(-) diff --git a/core/convention.php b/core/convention.php index 133dbd36..f7486a48 100644 --- a/core/convention.php +++ b/core/convention.php @@ -19,8 +19,6 @@ return [ 'auto_bind_module' => false, // 注册的根命名空间 'root_namespace' => [], - // 扩展配置文件 - 'extra_config_list' => ['database', 'validate'], // 扩展函数文件 'extra_file_list' => [THINK_PATH . 'helper' . EXT], // 默认输出类型 @@ -97,6 +95,10 @@ return [ 'url_controller_layer' => 'controller', // 表单请求类型伪装变量 'var_method' => '_method', + // 表单ajax伪装变量 + 'var_ajax' => '_ajax', + // 表单pjax伪装变量 + 'var_pjax' => '_pjax', // +---------------------------------------------------------------------- // | 模板设置 diff --git a/core/library/think/App.php b/core/library/think/App.php index 12748712..92eb59fa 100644 --- a/core/library/think/App.php +++ b/core/library/think/App.php @@ -214,18 +214,39 @@ class App public static function invokeMethod($method, $vars = []) { if (is_array($method)) { - $class = is_object($method[0]) ? $method[0] : new $method[0]; + $class = is_object($method[0]) ? $method[0] : new $method[0](Request::instance()); $reflect = new \ReflectionMethod($class, $method[1]); } else { // 静态方法 $reflect = new \ReflectionMethod($method); } $args = self::bindParams($reflect, $vars); - // 记录执行信息 + self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); return $reflect->invokeArgs(isset($class) ? $class : null, $args); } + /** + * 调用反射执行类的实例化 支持依赖注入 + * @access public + * @param string $class 类名 + * @param array $vars 变量 + * @return mixed + */ + public static function invokeClass($class, $vars = []) + { + $reflect = new \ReflectionClass($class); + $constructor = $reflect->getConstructor(); + if ($constructor) { + $args = self::bindParams($constructor, $vars); + } else { + $args = []; + } + + self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); + return $reflect->newInstanceArgs($args); + } + /** * 绑定参数 * @access public @@ -254,9 +275,9 @@ class App $class = $param->getClass(); if ($class) { $className = $class->getName(); - if (isset($vars[$name]) && $vars[$name] instanceof $className) { - $args[] = $vars[$name]; - unset($vars[$name]); + $bind = Request::instance()->$name; + if ($bind instanceof $className) { + $args[] = $bind; } else { $args[] = method_exists($className, 'instance') ? $className::instance() : new $className(); } @@ -444,12 +465,18 @@ class App $path = APP_PATH . $module; // 加载模块配置 $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT); - + // 读取数据库配置文件 + $filename = CONF_PATH . $module . 'database' . CONF_EXT; + Config::load($filename, 'database'); // 读取扩展配置文件 - if ($config['extra_config_list']) { - foreach ($config['extra_config_list'] as $name => $file) { - $filename = CONF_PATH . $module . $file . CONF_EXT; - Config::load($filename, is_string($name) ? $name : pathinfo($filename, PATHINFO_FILENAME)); + if (is_dir(CONF_PATH . $module . 'extra')) { + $dir = CONF_PATH . $module . 'extra'; + $files = scandir($dir); + foreach ($files as $file) { + if (strpos($file, CONF_EXT)) { + $filename = $dir . DS . $file; + Config::load($filename, pathinfo($file, PATHINFO_FILENAME)); + } } } diff --git a/core/library/think/File.php b/core/library/think/File.php index 7095d6b5..970063b2 100644 --- a/core/library/think/File.php +++ b/core/library/think/File.php @@ -95,27 +95,15 @@ class File extends SplFileObject } /** - * 获取文件的md5散列值 - * @return $this + * 获取文件的哈希散列值 + * @return $string */ - public function md5() + public function hash($type = 'sha1') { - if (!isset($this->hash['md5'])) { - $this->hash['md5'] = md5_file($this->filename); + if (!isset($this->hash[$type])) { + $this->hash[$type] = hash_file($type, $this->filename); } - return $this->hash['md5']; - } - - /** - * 获取文件的sha1散列值 - * @return $this - */ - public function sha1() - { - if (!isset($this->hash['sha1'])) { - $this->hash['sha1'] = sha1_file($this->filename); - } - return $this->hash['sha1']; + return $this->hash[$type]; } /** @@ -356,19 +344,18 @@ class File extends SplFileObject $savename = call_user_func_array($this->rule, [$this]); } else { switch ($this->rule) { - case 'md5': - $md5 = md5_file($this->filename); - $savename = substr($md5, 0, 2) . DS . substr($md5, 2); - break; - case 'sha1': - $sha1 = sha1_file($this->filename); - $savename = substr($sha1, 0, 2) . DS . substr($sha1, 2); - break; case 'date': $savename = date('Ymd') . DS . md5(microtime(true)); break; default: - $savename = call_user_func($this->rule); + if (in_array($this->rule, hash_algos())) { + $hash = $this->hash($this->rule); + $savename = substr($hash, 0, 2) . DS . substr($hash, 2); + } elseif (is_callable($this->rule)) { + $savename = call_user_func($this->rule); + } else { + $savename = date('Ymd') . DS . md5(microtime(true)); + } } } } elseif ('' === $savename) { diff --git a/core/library/think/Loader.php b/core/library/think/Loader.php index 90780d04..ef86720c 100644 --- a/core/library/think/Loader.php +++ b/core/library/think/Loader.php @@ -406,7 +406,7 @@ class Loader } $class = self::parseClass($module, $layer, $name, $appendSuffix); if (class_exists($class)) { - return new $class(Request::instance()); + return App::invokeClass($class); } elseif ($empty && class_exists($emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix))) { return new $emptyClass(Request::instance()); } diff --git a/core/library/think/Model.php b/core/library/think/Model.php index 276f4770..6ac90cc8 100644 --- a/core/library/think/Model.php +++ b/core/library/think/Model.php @@ -442,12 +442,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess $value = (bool) $value; break; case 'timestamp': - $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, $value); + if (!is_null($value)) { + $format = !empty($param) ? $param : $this->dateFormat; + $value = date($format, $value); + } break; case 'datetime': - $format = !empty($param) ? $param : $this->dateFormat; - $value = date($format, strtotime($value)); + if (!is_null($value)) { + $format = !empty($param) ? $param : $this->dateFormat; + $value = date($format, strtotime($value)); + } break; case 'json': $value = json_decode($value, true); @@ -1346,11 +1350,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess public static function __callStatic($method, $params) { + $query = self::getDb(); $model = get_called_class(); - if (!isset(self::$links[$model])) { - self::$links[$model] = (new static())->db(); - } - $query = self::$links[$model]; // 全局作用域 if (static::$useGlobalScope && method_exists($model, 'base')) { call_user_func_array('static::base', [ & $query]); @@ -1358,6 +1359,15 @@ abstract class Model implements \JsonSerializable, \ArrayAccess return call_user_func_array([$query, $method], $params); } + protected static function getDb() + { + $model = get_called_class(); + if (!isset(self::$links[$model])) { + self::$links[$model] = (new static())->db(); + } + return self::$links[$model]; + } + /** * 修改器 设置数据对象的值 * @access public diff --git a/core/library/think/Request.php b/core/library/think/Request.php index a73b4fa5..1a8b6046 100644 --- a/core/library/think/Request.php +++ b/core/library/think/Request.php @@ -117,6 +117,10 @@ class Request protected static $hook = []; // 绑定的属性 protected $bind = []; + // php://input + protected $input; + // 请求缓存 + protected $cache; /** * 架构函数 @@ -133,6 +137,8 @@ class Request if (is_null($this->filter)) { $this->filter = Config::get('default_filter'); } + // 保存 php://input + $this->input = file_get_contents('php://input'); } public function __call($method, $args) @@ -698,7 +704,7 @@ class Request public function put($name = '', $default = null, $filter = null) { if (is_null($this->put)) { - $content = file_get_contents('php://input'); + $content = $this->input; if (strpos($content, '":')) { $this->put = json_decode($content, true); } else { @@ -1180,22 +1186,34 @@ class Request /** * 当前是否Ajax请求 * @access public + * @param bool $ajax true 获取原始ajax请求 * @return bool */ - public function isAjax() + public function isAjax($ajax = false) { - $value = $this->server('HTTP_X_REQUESTED_WITH'); - return (!is_null($value) && strtolower($value) == 'xmlhttprequest') ? true : false; + $value = $this->server('HTTP_X_REQUESTED_WITH', '', 'strtolower'); + $result = ('xmlhttprequest' == $value) ? true : false; + if (true === $ajax) { + return $result; + } else { + return $this->param(Config::get('var_ajax')) ? true : $result; + } } /** * 当前是否Pjax请求 * @access public + * @param bool $pjax true 获取原始pjax请求 * @return bool */ - public function isPjax() + public function isPjax($pjax = false) { - return !is_null($this->server('HTTP_X_PJAX')) ? true : false; + $result = !is_null($this->server('HTTP_X_PJAX')) ? true : false; + if (true === $pjax) { + return $result; + } else { + return $this->param(Config::get('var_pjax')) ? true : $result; + } } /** @@ -1416,11 +1434,21 @@ class Request public function getContent() { if (is_null($this->content)) { - $this->content = file_get_contents('php://input'); + $this->content = $this->input; } return $this->content; } + /** + * 获取当前请求的php://input + * @access public + * @return string + */ + public function getInput() + { + return $this->input; + } + /** * 生成请求令牌 * @access public @@ -1439,6 +1467,45 @@ class Request return $token; } + /** + * 读取或者设置缓存 + * @access public + * @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id + * @param mixed $expire 缓存有效期 + * @return mixed + */ + public function cache($key, $expire = null) + { + if (false !== strpos($key, ':')) { + $param = $this->param(); + foreach ($param as $item => $val) { + if (is_string($val) && false !== strpos($key, ':' . $item)) { + $key = str_replace(':' . $item, $val, $key); + } + } + } + if (Cache::has($key)) { + // 读取缓存 + $content = Cache::get($key); + $response = Response::create($content) + ->code(304) + ->header('Content-Type', Cache::get($key . '_header')); + throw new \think\exception\HttpResponseException($response); + } else { + $this->cache = [$key, $expire]; + } + } + + /** + * 读取缓存设置 + * @access public + * @return array + */ + public function getCache() + { + return $this->cache; + } + /** * 设置当前请求绑定的对象实例 * @access public @@ -1464,4 +1531,9 @@ class Request { return isset($this->bind[$name]) ? $this->bind[$name] : null; } + + public function __isset($name) + { + return isset($this->bind[$name]); + } } diff --git a/core/library/think/Response.php b/core/library/think/Response.php index 35cafe90..d5ab5827 100644 --- a/core/library/think/Response.php +++ b/core/library/think/Response.php @@ -11,9 +11,11 @@ namespace think; +use think\Cache; use think\Config; use think\Debug; use think\Env; +use think\Request; use think\response\Json as JsonResponse; use think\response\Jsonp as JsonpResponse; use think\response\Redirect as RedirectResponse; @@ -111,11 +113,21 @@ class Response } echo $data; + if (200 == $this->code) { + $cache = Request::instance()->getCache(); + if ($cache) { + Cache::set($cache[0], $data, $cache[1]); + Cache::set($cache[0] . '_header', $this->header['Content-Type']); + } + } + if (function_exists('fastcgi_finish_request')) { // 提高页面响应 fastcgi_finish_request(); } + // 监听response_end + Hook::listen('response_end', $this); } /** diff --git a/core/library/think/Route.php b/core/library/think/Route.php index 8e37bfc0..339e4903 100644 --- a/core/library/think/Route.php +++ b/core/library/think/Route.php @@ -985,10 +985,6 @@ class Route case 'namespace': // 绑定到命名空间 return self::bindToNamespace($url, $bind, $depr); - case 'module': - // 如果有模块/控制器绑定 针对路由到 模块/控制器 有效 - $url = (empty(self::$domainBind) ? $bind . '/' : '') . ltrim($url, '/'); - break; } } return false; @@ -1240,17 +1236,20 @@ class Route foreach ($matches[1] as $name) { if (strpos($name, '?')) { $name = substr($name, 0, -1); - $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . '?)'; + $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')?'; } else { $replace[] = '(' . (isset($pattern[$name]) ? $pattern[$name] : '\w+') . ')'; } $value[] = $name; } $val = str_replace($matches[0], $replace, $val); - if (preg_match('/^' . $val . '$/', $m1[$key], $match)) { + if (preg_match('/^' . $val . '$/', isset($m1[$key]) ? $m1[$key] : '', $match)) { array_shift($match); - $match = array_slice($match, 0, count($value)); - $var = array_merge($var, array_combine($value, $match)); + foreach ($value as $k => $name) { + if (isset($match[$k])) { + $var[$name] = $match[$k]; + } + } continue; } else { return false; @@ -1296,6 +1295,7 @@ class Route */ private static function parseRule($rule, $route, $pathinfo, $option = [], $matches = [], $merge = false) { + $request = Request::instance(); // 解析路由规则 if ($rule) { $rule = explode('/', $rule); @@ -1368,13 +1368,13 @@ class Route $bind[$key] = $result; } } - Request::instance()->bind($bind); + $request->bind($bind); } // 解析额外参数 self::parseUrlParams(empty($paths) ? '' : implode('/', $paths), $matches); // 记录匹配的路由信息 - Request::instance()->routeInfo(['rule' => $rule, 'route' => $route, 'option' => $option, 'var' => $matches]); + $request->routeInfo(['rule' => $rule, 'route' => $route, 'option' => $option, 'var' => $matches]); // 检测路由after行为 if (!empty($option['after_behavior'])) { @@ -1402,8 +1402,9 @@ class Route } elseif (0 === strpos($route, '/') || 0 === strpos($route, 'http')) { // 路由到重定向地址 $result = ['type' => 'redirect', 'url' => $route, 'status' => isset($option['status']) ? $option['status'] : 301]; - } elseif (0 === strpos($route, '\\')) { + } elseif (false !== strpos($route, '\\')) { // 路由到方法 + $route = str_replace('/', '@', $route); $method = strpos($route, '@') ? explode('@', $route) : $route; $result = ['type' => 'method', 'method' => $method]; } elseif (0 === strpos($route, '@')) { @@ -1413,6 +1414,17 @@ class Route // 路由到模块/控制器/操作 $result = self::parseModule($route); } + // 开启请求缓存 + if ($request->isGet() && !empty($option['cache'])) { + $cache = $option['cache']; + if (is_array($cache)) { + list($key, $expire) = $cache; + } else { + $key = $pathinfo; + $expire = $cache; + } + $request->cache($key, $expire); + } return $result; } diff --git a/core/library/think/Template.php b/core/library/think/Template.php index f22b3297..c82850c3 100644 --- a/core/library/think/Template.php +++ b/core/library/think/Template.php @@ -127,6 +127,8 @@ class Template $this->config = array_merge($this->config, $config); } elseif (isset($this->config[$config])) { return $this->config[$config]; + } else { + return null; } } diff --git a/core/library/think/db/Query.php b/core/library/think/db/Query.php index 637b0bc5..b498b1ea 100644 --- a/core/library/think/db/Query.php +++ b/core/library/think/db/Query.php @@ -771,7 +771,7 @@ class Query * @param string $type JOIN类型 * @return $this */ - public function view($join, $field = null, $on = null, $type = 'INNER') + public function view($join, $field = true, $on = null, $type = 'INNER') { $this->options['view'] = true; if (is_array($join) && is_null($field)) { diff --git a/core/library/think/db/connector/Pgsql.php b/core/library/think/db/connector/Pgsql.php index 6d653c14..e9866cf3 100644 --- a/core/library/think/db/connector/Pgsql.php +++ b/core/library/think/db/connector/Pgsql.php @@ -59,10 +59,10 @@ class Pgsql extends Connection $info[$val['field']] = [ 'name' => $val['field'], 'type' => $val['type'], - 'notnull' => (bool) ('' === $val['null']), // not null is empty, null is yes + 'notnull' => (bool) ('' !== $val['null']), 'default' => $val['default'], - 'primary' => (strtolower($val['key']) == 'pri'), - 'autoinc' => (strtolower($val['extra']) == 'auto_increment'), + 'primary' => !empty($val['key']), + 'autoinc' => (0 === strpos($val['extra'], 'nextval(')), ]; } } diff --git a/core/library/think/model/Merge.php b/core/library/think/model/Merge.php index 566cf077..e03fd90c 100644 --- a/core/library/think/model/Merge.php +++ b/core/library/think/model/Merge.php @@ -209,7 +209,9 @@ class Merge extends Model // 处理关联模型数据 $data = $this->parseData($name, $this->data); $query = clone $db; - $query->table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data); + if ($query->table($table)->strict(false)->where($this->fk, $this->data[$this->getPk()])->update($data)) { + $result = 1; + } } // 新增回调 $this->trigger('after_update', $this); diff --git a/core/library/think/model/Relation.php b/core/library/think/model/Relation.php index 48d7851f..69b80270 100644 --- a/core/library/think/model/Relation.php +++ b/core/library/think/model/Relation.php @@ -554,7 +554,7 @@ class Relation case self::BELONGS_TO: case self::HAS_MANY: if ($data instanceof Model) { - $data = $data->toArray(); + $data = $data->getData(); } // 保存关联表数据 $data[$this->foreignKey] = $this->parent->{$this->localKey}; @@ -604,7 +604,8 @@ class Relation if (is_array($data)) { // 保存关联表数据 $model = new $this->model; - $id = $model->save($data); + $model->save($data); + $id = $model->getLastInsID(); } elseif (is_numeric($data) || is_string($data)) { // 根据关联表主键直接写入中间表 $id = $data; @@ -681,11 +682,14 @@ class Relation $pk = (new $this->model)->getPk(); $throughKey = $this->throughKey; $modelTable = $this->parent->getTable(); - $result = $this->query->field($alias . '.*')->alias($alias) + $this->query->field($alias . '.*')->alias($alias) ->join($throughTable, $throughTable . '.' . $pk . '=' . $alias . '.' . $throughKey) ->join($modelTable, $modelTable . '.' . $this->localKey . '=' . $throughTable . '.' . $this->foreignKey) ->where($throughTable . '.' . $this->foreignKey, $this->parent->{$this->localKey}); break; + case self::BELONGS_TO_MANY: + // TODO + } $result = call_user_func_array([$this->query, $method], $args); if ($result instanceof \think\db\Query) { diff --git a/core/library/think/response/Redirect.php b/core/library/think/response/Redirect.php index 220eef25..82a5fe32 100644 --- a/core/library/think/response/Redirect.php +++ b/core/library/think/response/Redirect.php @@ -67,7 +67,7 @@ class Redirect extends Response */ public function getTargetUrl() { - return preg_match('/^(https?:|\/)/', $this->data) ? $this->data : Url::build($this->data, $this->params); + return (strpos($this->data, '://') || 0 === strpos($this->data, '/')) ? $this->data : Url::build($this->data, $this->params); } public function params($params = []) diff --git a/core/library/think/view/driver/Php.php b/core/library/think/view/driver/Php.php index 06f49a6a..31fe7807 100644 --- a/core/library/think/view/driver/Php.php +++ b/core/library/think/view/driver/Php.php @@ -143,6 +143,8 @@ class Php { if (is_array($name)) { $this->config = array_merge($this->config, $name); + } elseif (is_null($value)) { + return isset($this->config[$name]) ? $this->config[$name] : null; } else { $this->config[$name] = $value; } diff --git a/core/library/think/view/driver/Think.php b/core/library/think/view/driver/Think.php index e82f5139..33729b44 100644 --- a/core/library/think/view/driver/Think.php +++ b/core/library/think/view/driver/Think.php @@ -130,17 +130,19 @@ class Think } /** - * 配置模板引擎 + * 配置或者获取模板引擎参数 * @access private * @param string|array $name 参数名 * @param mixed $value 参数值 - * @return void + * @return mixed */ public function config($name, $value = null) { if (is_array($name)) { $this->template->config($name); $this->config = array_merge($this->config, $name); + } elseif (is_null($value)) { + return $this->template->config($name); } else { $this->template->$name = $value; $this->config[$name] = $value; diff --git a/core/library/traits/controller/Jump.php b/core/library/traits/controller/Jump.php index a867548e..0fad996d 100644 --- a/core/library/traits/controller/Jump.php +++ b/core/library/traits/controller/Jump.php @@ -44,7 +44,7 @@ trait Jump if (is_null($url) && isset($_SERVER["HTTP_REFERER"])) { $url = $_SERVER["HTTP_REFERER"]; } elseif ('' !== $url) { - $url = preg_match('/^(https?:|\/)/', $url) ? $url : Url::build($url); + $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); } $result = [ 'code' => $code, @@ -83,7 +83,7 @@ trait Jump if (is_null($url)) { $url = 'javascript:history.back(-1);'; } elseif ('' !== $url) { - $url = preg_match('/^(https?:|\/)/', $url) ? $url : Url::build($url); + $url = (strpos($url, '://') || 0 === strpos($url, '/')) ? $url : Url::build($url); } $result = [ 'code' => $code,