更新内核

This commit is contained in:
2016-11-18 11:14:26 +08:00
parent 9074eb1d89
commit 860da138c9
36 changed files with 754 additions and 330 deletions

View File

@@ -16,6 +16,7 @@ use think\Env;
use think\Exception;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\RouteNotFoundException;
use think\Hook;
use think\Lang;
use think\Loader;
@@ -126,6 +127,8 @@ class App
// 监听app_begin
Hook::listen('app_begin', $dispatch);
// 请求缓存检查
$request->cache($config['request_cache'], $config['request_cache_expire']);
switch ($dispatch['type']) {
case 'redirect':
@@ -280,7 +283,14 @@ class App
if ($bind instanceof $className) {
$args[] = $bind;
} else {
$args[] = method_exists($className, 'instance') ? $className::instance() : new $className();
if (method_exists($className, 'invoke')) {
$method = new \ReflectionMethod($className, 'invoke');
if ($method->isPublic() && $method->isStatic()) {
$args[] = $className::invoke(Request::instance());
continue;
}
}
$args[] = method_exists($className, 'instance') ? $className::instance() : new $className;
}
} elseif (1 == $type && !empty($vars)) {
$args[] = array_shift($vars);
@@ -362,34 +372,29 @@ class App
// 监听module_init
Hook::listen('module_init', $request);
try {
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
if (is_null($instance)) {
throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1));
}
// 获取当前操作名
$action = $actionName . $config['action_suffix'];
if (!preg_match('/^[A-Za-z](\w)*$/', $action)) {
// 非法操作
throw new \ReflectionException('illegal action name:' . $actionName);
}
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
if (is_null($instance)) {
throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1));
}
// 获取当前操作名
$action = $actionName . $config['action_suffix'];
$vars = [];
if (is_callable([$instance, $action])) {
// 执行操作方法
$call = [$instance, $action];
Hook::listen('action_begin', $call);
$data = self::invokeMethod($call);
} catch (\ReflectionException $e) {
} elseif (is_callable([$instance, '_empty'])) {
// 空操作
$call = [$instance, '_empty'];
$vars = [$action];
} else {
// 操作不存在
if (method_exists($instance, '_empty')) {
$reflect = new \ReflectionMethod($instance, '_empty');
$data = $reflect->invokeArgs($instance, [$action]);
self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info');
} else {
throw new HttpException(404, 'method not exists:' . (new \ReflectionClass($instance))->getName() . '->' . $action);
}
throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
}
return $data;
Hook::listen('action_begin', $call);
return self::invokeMethod($call, $vars);
}
/**
@@ -545,7 +550,7 @@ class App
$must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];
if ($must && false === $result) {
// 路由无效
throw new HttpException(404, 'Route Not Found');
throw new RouteNotFoundException();
}
}
if (false === $result) {

View File

@@ -185,6 +185,35 @@ class Cache
return self::$handler->clear($tag);
}
/**
* 读取缓存并删除
* @access public
* @param string $name 缓存变量名
* @return mixed
*/
public static function pull($name)
{
self::init();
self::$readTimes++;
self::$writeTimes++;
return self::$handler->pull($name);
}
/**
* 如果不存在则写入缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久
* @return mixed
*/
public static function remember($name, $value, $expire = null)
{
self::init();
self::$readTimes++;
return self::$handler->remember($name, $value, $expire);
}
/**
* 缓存标签
* @access public

View File

@@ -59,6 +59,7 @@ class Config
self::$config[$range] = [];
}
if (is_file($file)) {
$name = strtolower($name);
$type = pathinfo($file, PATHINFO_EXTENSION);
if ('php' == $type) {
return self::set(include $file, $name, $range);

View File

@@ -20,9 +20,13 @@ class Controller
{
use \traits\controller\Jump;
// 视图类实例
/**
* @var \think\View 视图类实例
*/
protected $view;
// Request实例
/**
* @var \think\Request Request实例
*/
protected $request;
// 验证失败是否抛出异常
protected $failException = false;

View File

@@ -42,6 +42,9 @@ use think\paginator\Collection as PaginatorCollection;
* @method integer execute(string $sql, array $bind = [], boolean $fetch = false, boolean $getLastInsID = false, string $sequence = null) static SQL执行
* @method PaginatorCollection paginate(integer $listRows = 15, mixed $simple = false, array $config = []) static 分页查询
* @method mixed transaction(callable $callback) static 执行数据库事务
* @method void startTrans() static 启动事务
* @method void commit() static 用于非自动提交状态下面的查询提交
* @method void rollback() static 事务回滚
* @method boolean batchQuery(array $sqlArray) static 批处理执行SQL语句
*/
class Db

View File

@@ -39,7 +39,7 @@ class File extends SplFileObject
public function __construct($filename, $mode = 'r')
{
parent::__construct($filename, $mode);
$this->filename = $this->getRealPath();
$this->filename = $this->getRealPath() ?: $this->getPathname();
}
/**

View File

@@ -365,8 +365,9 @@ class Loader
*/
public static function model($name = '', $layer = 'model', $appendSuffix = false, $common = 'common')
{
if (isset(self::$instance[$name . $layer])) {
return self::$instance[$name . $layer];
$guid = $name . $layer;
if (isset(self::$instance[$guid])) {
return self::$instance[$guid];
}
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name, 2);
@@ -384,7 +385,7 @@ class Loader
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
}
self::$instance[$name . $layer] = $model;
self::$instance[$guid] = $model;
return $model;
}
@@ -427,9 +428,9 @@ class Loader
if (empty($name)) {
return new Validate;
}
if (isset(self::$instance[$name . $layer])) {
return self::$instance[$name . $layer];
$guid = $name . $layer;
if (isset(self::$instance[$guid])) {
return self::$instance[$guid];
}
if (strpos($name, '/')) {
list($module, $name) = explode('/', $name);
@@ -447,7 +448,7 @@ class Loader
throw new ClassNotFoundException('class not exists:' . $class, $class);
}
}
self::$instance[$name . $layer] = $validate;
self::$instance[$guid] = $validate;
return $validate;
}

View File

@@ -101,6 +101,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $failException = false;
// 全局查询范围
protected $useGlobalScope = true;
// 是否采用批量验证
protected $batchValidate = false;
/**
* 初始化过的模型.
@@ -649,7 +651,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
if (false === $this->trigger('before_write', $this)) {
return false;
}
$pk = $this->getPk();
if ($this->isUpdate) {
// 自动更新
$this->autoCompleteData($this->update);
@@ -680,7 +682,6 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$where = $this->updateWhere;
}
$pk = $this->getPk();
if (is_string($pk) && isset($data[$pk])) {
if (!isset($where[$pk])) {
unset($where);
@@ -710,10 +711,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$result = $this->db()->insert($this->data);
// 获取自动增长主键
if ($result) {
if ($result && is_string($pk) && !isset($this->data[$pk])) {
$insertId = $this->db()->getLastInsID($sequence);
$pk = $this->getPk();
if (is_string($pk) && $insertId) {
if ($insertId) {
$this->data[$pk] = $insertId;
}
}
@@ -743,7 +743,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 数据批量验证
$validate = $this->validate;
foreach ($dataSet as $data) {
if (!$this->validate($validate)->validateData($data)) {
if (!$this->validateData($data, $validate)) {
return false;
}
}
@@ -759,9 +759,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
}
foreach ($dataSet as $key => $data) {
if (!empty($auto) && isset($data[$pk])) {
$result[$key] = self::update($data);
$result[$key] = self::update($data, [], $this->field);
} else {
$result[$key] = self::create($data);
$result[$key] = self::create($data, $this->field);
}
}
$db->commit();
@@ -856,9 +856,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* @access public
* @param array|string|bool $rule 验证规则 true表示自动读取验证器类
* @param array $msg 提示信息
* @param bool $batch 批量验证
* @return $this
*/
public function validate($rule = true, $msg = [])
public function validate($rule = true, $msg = [], $batch = false)
{
if (is_array($rule)) {
$this->validate = [
@@ -868,6 +869,7 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
} else {
$this->validate = true === $rule ? $this->name : $rule;
}
$this->batchValidate = $batch;
return $this;
}
@@ -887,12 +889,15 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
* 自动验证数据
* @access protected
* @param array $data 验证数据
* @param mixed $rule 验证规则
* @param bool $batch 批量验证
* @return bool
*/
protected function validateData($data)
protected function validateData($data, $rule = null, $batch = null)
{
if (!empty($this->validate)) {
$info = $this->validate;
$info = is_null($rule) ? $this->validate : $rule;
if (!empty($info)) {
if (is_array($info)) {
$validate = Loader::validate();
$validate->rule($info['rule']);
@@ -907,7 +912,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$validate->scene($scene);
}
}
if (!$validate->check($data)) {
$batch = is_null($batch) ? $this->batchValidate : $batch;
if (!$validate->batch($batch)->check($data)) {
$this->error = $validate->getError();
if ($this->failException) {
throw new ValidateException($this->error);
@@ -972,12 +979,16 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/**
* 写入数据
* @access public
* @param array $data 数据数组
* @param array $data 数据数组
* @param array|true $field 允许字段
* @return $this
*/
public static function create($data = [])
public static function create($data = [], $field = null)
{
$model = new static();
if (!empty($field)) {
$model->allowField($field);
}
$model->isUpdate(false)->save($data, []);
return $model;
}
@@ -985,13 +996,17 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
/**
* 更新数据
* @access public
* @param array $data 数据数组
* @param array $where 更新条件
* @param array $data 数据数组
* @param array $where 更新条件
* @param array|true $field 允许字段
* @return $this
*/
public static function update($data = [], $where = [])
public static function update($data = [], $where = [], $field = null)
{
$model = new static();
$model = new static();
if (!empty($field)) {
$model->allowField($field);
}
$result = $model->isUpdate(true)->save($data, $where);
return $model;
}
@@ -1358,19 +1373,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
public static function __callStatic($method, $params)
{
$query = self::getDb();
$query = (new static())->db();
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

View File

@@ -121,6 +121,8 @@ class Request
protected $input;
// 请求缓存
protected $cache;
// 缓存是否检查
protected $isCheckCache;
/**
* 架构函数
@@ -248,7 +250,7 @@ class Request
$options['baseUrl'] = $info['path'];
$options['pathinfo'] = '/' == $info['path'] ? '/' : ltrim($info['path'], '/');
$options['method'] = $server['REQUEST_METHOD'];
$options['domain'] = $info['scheme'] . '://' . $server['HTTP_HOST'];
$options['domain'] = isset($info['scheme']) ? $info['scheme'] . '://' . $server['HTTP_HOST'] : '';
$options['content'] = $content;
self::$instance = new self($options);
return self::$instance;
@@ -607,7 +609,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function param($name = '', $default = null, $filter = null)
public function param($name = '', $default = null, $filter = '')
{
if (empty($this->param)) {
$method = $this->method(true);
@@ -644,7 +646,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function route($name = '', $default = null, $filter = null)
public function route($name = '', $default = null, $filter = '')
{
if (is_array($name)) {
$this->param = [];
@@ -661,7 +663,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function get($name = '', $default = null, $filter = null)
public function get($name = '', $default = null, $filter = '')
{
if (empty($this->get)) {
$this->get = $_GET;
@@ -681,7 +683,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function post($name = '', $default = null, $filter = null)
public function post($name = '', $default = null, $filter = '')
{
if (empty($this->post)) {
$this->post = $_POST;
@@ -701,7 +703,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function put($name = '', $default = null, $filter = null)
public function put($name = '', $default = null, $filter = '')
{
if (is_null($this->put)) {
$content = $this->input;
@@ -727,7 +729,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function delete($name = '', $default = null, $filter = null)
public function delete($name = '', $default = null, $filter = '')
{
return $this->put($name, $default, $filter);
}
@@ -740,7 +742,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function patch($name = '', $default = null, $filter = null)
public function patch($name = '', $default = null, $filter = '')
{
return $this->put($name, $default, $filter);
}
@@ -752,7 +754,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function request($name = '', $default = null, $filter = null)
public function request($name = '', $default = null, $filter = '')
{
if (empty($this->request)) {
$this->request = $_REQUEST;
@@ -772,7 +774,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function session($name = '', $default = null, $filter = null)
public function session($name = '', $default = null, $filter = '')
{
if (empty($this->session)) {
$this->session = Session::get();
@@ -791,7 +793,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function cookie($name = '', $default = null, $filter = null)
public function cookie($name = '', $default = null, $filter = '')
{
if (empty($this->cookie)) {
$this->cookie = $_COOKIE;
@@ -810,7 +812,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function server($name = '', $default = null, $filter = null)
public function server($name = '', $default = null, $filter = '')
{
if (empty($this->server)) {
$this->server = $_SERVER;
@@ -888,7 +890,7 @@ class Request
* @param string|array $filter 过滤方法
* @return mixed
*/
public function env($name = '', $default = null, $filter = null)
public function env($name = '', $default = null, $filter = '')
{
if (empty($this->env)) {
$this->env = $_ENV;
@@ -947,7 +949,7 @@ class Request
* @param string|array $filter 过滤函数
* @return mixed
*/
public function input($data = [], $name = '', $default = null, $filter = null)
public function input($data = [], $name = '', $default = null, $filter = '')
{
if (false === $name) {
// 获取原始数据
@@ -976,13 +978,17 @@ class Request
}
// 解析过滤器
$filter = $filter ?: $this->filter;
if (is_string($filter)) {
$filter = explode(',', $filter);
if (is_null($filter)) {
$filter = [];
} else {
$filter = (array) $filter;
$filter = $filter ?: $this->filter;
if (is_string($filter)) {
$filter = explode(',', $filter);
} else {
$filter = (array) $filter;
}
}
$filter[] = $default;
if (is_array($data)) {
array_walk_recursive($data, [$this, 'filterValue'], $filter);
@@ -1241,8 +1247,7 @@ class Request
if (false !== $pos) {
unset($arr[$pos]);
}
$ip = trim($arr[0]);
$ip = trim(current($arr));
} elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (isset($_SERVER['REMOTE_ADDR'])) {
@@ -1370,7 +1375,7 @@ class Request
* 设置或者获取当前的模块名
* @access public
* @param string $module 模块名
* @return string|$this
* @return string|Request
*/
public function module($module = null)
{
@@ -1386,7 +1391,7 @@ class Request
* 设置或者获取当前的控制器名
* @access public
* @param string $controller 控制器名
* @return string|$this
* @return string|Request
*/
public function controller($controller = null)
{
@@ -1402,7 +1407,7 @@ class Request
* 设置或者获取当前的操作名
* @access public
* @param string $action 操作名
* @return string
* @return string|Request
*/
public function action($action = null)
{
@@ -1418,7 +1423,7 @@ class Request
* 设置或者获取当前的语言
* @access public
* @param string $lang 语言名
* @return string
* @return string|Request
*/
public function langset($lang = null)
{
@@ -1472,15 +1477,34 @@ class Request
}
/**
* 读取或者设置缓存
* 设置当前地址的请求缓存
* @access public
* @param string $key 缓存标识,支持变量规则 ,例如 item/:name/:id
* @param mixed $expire 缓存有效期
* @return mixed
* @return void
*/
public function cache($key, $expire = null)
{
if ($this->isGet()) {
if (false !== $key && $this->isGet() && !$this->isCheckCache) {
// 标记请求缓存检查
$this->isCheckCache = true;
if (false === $expire) {
// 关闭当前缓存
return;
}
if ($key instanceof \Closure) {
$key = call_user_func_array($key, [$this]);
} elseif (true === $key) {
// 自动缓存功能
$key = '__URL__';
} elseif (strpos($key, '|')) {
list($key, $fun) = explode('|', $key);
}
// 特殊规则替换
if (false !== strpos($key, '__')) {
$key = str_replace(['__MODULE__', '__CONTROLLER__', '__ACTION__', '__URL__'], [$this->module, $this->controller, $this->action, md5($this->url())], $key);
}
if (false !== strpos($key, ':')) {
$param = $this->param();
foreach ($param as $item => $val) {
@@ -1488,9 +1512,6 @@ class Request
$key = str_replace(':' . $item, $val, $key);
}
}
} elseif ('__URL__' == $key) {
// 当前URL地址作为缓存标识
$key = md5($this->url());
} elseif (strpos($key, ']')) {
if ('[' . $this->ext() . ']' == $key) {
// 缓存某个后缀的请求
@@ -1499,6 +1520,9 @@ class Request
return;
}
}
if (isset($fun)) {
$key = $fun($key);
}
if (strtotime($this->server('HTTP_IF_MODIFIED_SINCE')) + $expire > $_SERVER['REQUEST_TIME']) {
// 读取缓存
@@ -1515,7 +1539,7 @@ class Request
}
/**
* 读取缓存设置
* 读取请求缓存设置
* @access public
* @return array
*/

View File

@@ -130,6 +130,9 @@ class Response
// 监听response_end
Hook::listen('response_end', $this);
// 清空当次请求有效的数据
Session::flush();
}
/**

View File

@@ -24,13 +24,13 @@ class Route
{
// 路由规则
private static $rules = [
'GET' => [],
'POST' => [],
'PUT' => [],
'DELETE' => [],
'PATCH' => [],
'HEAD' => [],
'OPTIONS' => [],
'get' => [],
'post' => [],
'put' => [],
'delete' => [],
'patch' => [],
'head' => [],
'options' => [],
'*' => [],
'alias' => [],
'domain' => [],
@@ -40,21 +40,22 @@ class Route
// REST路由操作方法定义
private static $rest = [
'index' => ['GET', '', 'index'],
'create' => ['GET', '/create', 'create'],
'edit' => ['GET', '/:id/edit', 'edit'],
'read' => ['GET', '/:id', 'read'],
'save' => ['POST', '', 'save'],
'update' => ['PUT', '/:id', 'update'],
'delete' => ['DELETE', '/:id', 'delete'],
'index' => ['get', '', 'index'],
'create' => ['get', '/create', 'create'],
'edit' => ['get', '/:id/edit', 'edit'],
'read' => ['get', '/:id', 'read'],
'save' => ['post', '', 'save'],
'update' => ['put', '/:id', 'update'],
'delete' => ['delete', '/:id', 'delete'],
];
// 不同请求类型的方法前缀
private static $methodPrefix = [
'GET' => 'get',
'POST' => 'post',
'PUT' => 'put',
'DELETE' => 'delete',
'get' => 'get',
'post' => 'post',
'put' => 'put',
'delete' => 'delete',
'patch' => 'patch',
];
// 子域名
@@ -68,6 +69,8 @@ class Route
private static $domainRule;
// 当前域名
private static $domain;
// 当前路由执行过程中的参数
private static $option = [];
/**
* 注册变量规则
@@ -126,7 +129,7 @@ class Route
* 设置路由绑定
* @access public
* @param mixed $bind 绑定信息
* @param string $type 绑定类型 默认为module 支持 namespace class
* @param string $type 绑定类型 默认为module 支持 namespace class controller
* @return mixed
*/
public static function bind($bind, $type = 'module')
@@ -148,8 +151,9 @@ class Route
} elseif ('' === $name) {
return self::$rules['name'];
} elseif (!is_null($value)) {
self::$rules['name'][$name][] = $value;
self::$rules['name'][strtolower($name)][] = $value;
} else {
$name = strtolower($name);
return isset(self::$rules['name'][$name]) ? self::$rules['name'][$name] : null;
}
}
@@ -198,7 +202,7 @@ class Route
unset($rule['__rest__']);
}
self::registerRules($rule, strtoupper($type));
self::registerRules($rule, strtolower($type));
}
// 批量注册路由
@@ -241,7 +245,7 @@ class Route
$pattern = array_merge(self::getGroup('pattern'), $pattern);
}
$type = strtoupper($type);
$type = strtolower($type);
if (strpos($type, '|')) {
$option['method'] = $type;
@@ -300,12 +304,12 @@ class Route
$rule = substr($rule, 0, -1);
}
if ('/' != $rule) {
if ('/' != $rule || $group) {
$rule = trim($rule, '/');
}
$vars = self::parseVar($rule);
if (isset($name)) {
$key = $group ? $group . '/' . $rule : $rule;
$key = $group ? $group . ($rule ? '/' . $rule : '') : $rule;
self::name($name, [$key, $vars, self::$domain]);
}
if ($group) {
@@ -328,7 +332,7 @@ class Route
}
if ('*' == $type) {
// 注册路由快捷方式
foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as $method) {
foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
if (self::$domain) {
self::$rules['domain'][self::$domain][$method][$rule] = true;
} else {
@@ -339,6 +343,27 @@ class Route
}
}
/**
* 设置当前执行的参数信息
* @access public
* @param array $options 参数信息
* @return mixed
*/
protected static function setOption($options = [])
{
self::$option[] = $options;
}
/**
* 获取当前执行的所有参数信息
* @access public
* @return array
*/
public static function getOption()
{
return self::$option;
}
/**
* 获取当前的分组信息
* @access public
@@ -423,15 +448,16 @@ class Route
$options['complete_match'] = true;
$key = substr($key, 0, -1);
}
$key = trim($key, '/');
$vars = self::parseVar($key);
$item[] = ['rule' => $key, 'route' => $route, 'var' => $vars, 'option' => $options, 'pattern' => $patterns];
// 设置路由标识
self::name($route, [$name . '/' . $key, $vars, self::$domain]);
self::name($route, [$name . ($key ? '/' . $key : ''), $vars, self::$domain]);
}
self::$rules['*'][$name] = ['rule' => $item, 'route' => '', 'var' => [], 'option' => $option, 'pattern' => $pattern];
}
foreach (['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'] as $method) {
foreach (['get', 'post', 'put', 'delete', 'patch', 'head', 'options'] as $method) {
if (!isset(self::$rules[$method][$name])) {
self::$rules[$method][$name] = true;
} elseif (is_array(self::$rules[$method][$name])) {
@@ -576,7 +602,8 @@ class Route
} elseif (strpos($val[1], ':id') && isset($option['var'][$rule])) {
$val[1] = str_replace(':id', ':' . $option['var'][$rule], $val[1]);
}
$item = ltrim($rule . $val[1], '/');
$item = ltrim($rule . $val[1], '/');
$option['rest'] = $key;
self::rule($item . '$', $route . '/' . $val[2], $val[0], $option, $pattern);
}
}
@@ -625,9 +652,9 @@ class Route
public static function setMethodPrefix($method, $prefix = '')
{
if (is_array($method)) {
self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method, CASE_UPPER));
self::$methodPrefix = array_merge(self::$methodPrefix, array_change_key_case($method));
} else {
self::$methodPrefix[strtoupper($method)] = $prefix;
self::$methodPrefix[strtolower($method)] = $prefix;
}
}
@@ -635,7 +662,7 @@ class Route
* rest方法定义和修改
* @access public
* @param string $name 方法名称
* @param array $resourece 资源
* @param array $resource 资源
* @return void
*/
public static function rest($name, $resource = [])
@@ -682,7 +709,7 @@ class Route
if (is_array($rules)) {
self::$rules = $rules;
} elseif ($rules) {
return true === $rules ? self::$rules : self::$rules[$rules];
return true === $rules ? self::$rules : self::$rules[strtolower($rules)];
} else {
$rules = self::$rules;
unset($rules['pattern'], $rules['alias'], $rules['domain'], $rules['name']);
@@ -698,7 +725,7 @@ class Route
* @param string $method 请求类型
* @return void
*/
public static function checkDomain($request, &$currentRules, $method = 'GET')
public static function checkDomain($request, &$currentRules, $method = 'get')
{
// 域名规则
$rules = self::$rules['domain'];
@@ -808,7 +835,7 @@ class Route
return $result;
}
}
$method = $request->method();
$method = strtolower($request->method());
// 获取当前请求类型的路由规则
$rules = self::$rules[$method];
// 检测域名部署
@@ -823,14 +850,16 @@ class Route
if ('|' != $url) {
$url = rtrim($url, '|');
}
if (isset($rules[$url])) {
$item = str_replace('|', '/', $url);
if (isset($rules[$item])) {
// 静态路由规则检测
$rule = $rules[$url];
$rule = $rules[$item];
if (true === $rule) {
$rule = self::getRouteExpress($url);
$rule = self::getRouteExpress($item);
}
if (!empty($rule['route']) && self::checkOption($rule['option'], $request)) {
return self::parseRule($url, $rule['route'], $url, $rule['option']);
self::setOption($rule['option']);
return self::parseRule($item, $rule['route'], $url, $rule['option']);
}
}
@@ -893,7 +922,7 @@ class Route
if (is_string($str) && $str && 0 !== strpos(str_replace('|', '/', $url), $str)) {
continue;
}
self::setOption($option);
$result = self::checkRoute($request, $rule, $url, $depr, $key, $option);
if (false !== $result) {
return $result;
@@ -908,6 +937,8 @@ class Route
if ($group) {
$rule = $group . ($rule ? '/' . ltrim($rule, '/') : '');
}
self::setOption($option);
if (isset($options['bind_model']) && isset($option['bind_model'])) {
$option['bind_model'] = array_merge($options['bind_model'], $option['bind_model']);
}
@@ -994,6 +1025,9 @@ class Route
case 'class':
// 绑定到类
return self::bindToClass($url, $bind, $depr);
case 'controller':
// 绑定到控制器类
return self::bindToController($url, $bind, $depr);
case 'namespace':
// 绑定到命名空间
return self::bindToNamespace($url, $bind, $depr);
@@ -1038,7 +1072,7 @@ class Route
if (!empty($array[2])) {
self::parseUrlParams($array[2]);
}
return ['type' => 'method', 'method' => [$namespace . '\\' . $class, $method]];
return ['type' => 'method', 'method' => [$namespace . '\\' . Loader::parseName($class, 1), $method]];
}
/**
@@ -1088,12 +1122,16 @@ class Route
*/
private static function checkOption($option, $request)
{
// 请求类型检测
if ((isset($option['method']) && is_string($option['method']) && false === stripos($option['method'], $request->method()))
|| (isset($option['ext']) && false === stripos($option['ext'], $request->ext())) // 伪静态后缀检测
|| (isset($option['ajax']) && $option['ajax'] && !$request->isAjax()) // Ajax检测
|| (isset($option['ajax']) && !$option['ajax'] && $request->isAjax()) // 非Ajax检测
|| (isset($option['pjax']) && $option['pjax'] && !$request->isPjax()) // Pjax检测
|| (isset($option['pjax']) && !$option['pjax'] && $request->isPjax()) // 非Pjax检测
|| (isset($option['ext']) && false === stripos($option['ext'], $request->ext())) // 伪静态后缀检测
|| (isset($option['deny_ext']) && false !== stripos($option['deny_ext'], $request->ext()))
|| (isset($option['domain']) && !in_array($option['domain'], [$_SERVER['HTTP_HOST'], self::$subDomain])) // 域名检测
|| (!empty($option['https']) && !$request->isSsl()) // https检测
|| (isset($option['https']) && $option['https'] && !$request->isSsl()) // https检测
|| (isset($option['https']) && !$option['https'] && $request->isSsl()) // https检测
|| (!empty($option['before_behavior']) && false === Hook::exec($option['before_behavior'])) // 行为检测
|| (!empty($option['callback']) && is_callable($option['callback']) && false === call_user_func($option['callback'])) // 自定义检测
) {
@@ -1161,8 +1199,9 @@ class Route
{
if (isset(self::$bind['module'])) {
$bind = str_replace('/', $depr, self::$bind['module']);
// 如果有模块/控制器绑定
$url = self::$bind['module'] . '/' . ltrim($url, '/');
$url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr);
}
$url = str_replace($depr, '|', $url);
list($path, $var) = self::parseUrlPath($url);
@@ -1175,15 +1214,24 @@ class Route
$dir = APP_PATH . ($module ? $module . DS : '') . Config::get('url_controller_layer');
$suffix = App::$suffix || Config::get('controller_suffix') ? ucfirst(Config::get('url_controller_layer')) : '';
$item = [];
$find = false;
foreach ($path as $val) {
$item[] = array_shift($path);
if (is_file($dir . DS . $val . $suffix . EXT)) {
$item[] = $val;
$file = $dir . DS . str_replace('.', DS, $val) . $suffix . EXT;
$file = pathinfo($file, PATHINFO_DIRNAME) . DS . Loader::parseName(pathinfo($file, PATHINFO_FILENAME), 1) . EXT;
if (is_file($file)) {
$find = true;
break;
} else {
$dir .= DS . $val;
}
}
$controller = implode('.', $item);
if ($find) {
$controller = implode('.', $item);
$path = array_slice($path, count($item));
} else {
$controller = array_shift($path);
}
} else {
// 解析控制器
$controller = !empty($path) ? array_shift($path) : null;
@@ -1194,8 +1242,15 @@ class Route
self::parseUrlParams(empty($path) ? '' : implode('|', $path));
// 封装路由
$route = [$module, $controller, $action];
if (isset(self::$rules['name'][implode($depr, $route)])) {
throw new HttpException(404, 'invalid request:' . $url);
// 检查地址是否被定义过路由
$name = strtolower($module . '/' . Loader::parseName($controller, 1) . '/' . $action);
$name2 = '';
if (empty($module) || isset($bind) && $module == $bind) {
$name2 = strtolower(Loader::parseName($controller, 1) . '/' . $action);
}
if (isset(self::$rules['name'][$name]) || isset(self::$rules['name'][$name2])) {
throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url));
}
}
return ['type' => 'module', 'module' => $route];
@@ -1285,9 +1340,16 @@ class Route
if (!$optional && !isset($m1[$key])) {
return false;
}
if (isset($m1[$key]) && isset($pattern[$name]) && !preg_match('/^' . $pattern[$name] . '$/', $m1[$key])) {
if (isset($m1[$key]) && isset($pattern[$name])) {
// 检查变量规则
return false;
if ($pattern[$name] instanceof \Closure) {
$result = call_user_func_array($pattern[$name], [$m1[$key]]);
if (false === $result) {
return false;
}
} elseif (!preg_match('/^' . $pattern[$name] . '$/', $m1[$key])) {
return false;
}
}
$var[$name] = isset($m1[$key]) ? $m1[$key] : '';
} elseif (!isset($m1[$key]) || 0 !== strcasecmp($val, $m1[$key])) {
@@ -1343,7 +1405,6 @@ class Route
foreach ($matches as $key => $val) {
if (false !== strpos($route, ':' . $key)) {
$route = str_replace(':' . $key, $val, $route);
unset($matches[$key]);
}
}
}
@@ -1430,7 +1491,7 @@ class Route
$result = self::parseModule($route);
}
// 开启请求缓存
if ($request->isGet() && !empty($option['cache'])) {
if ($request->isGet() && isset($option['cache'])) {
$cache = $option['cache'];
if (is_array($cache)) {
list($key, $expire) = $cache;

View File

@@ -196,8 +196,44 @@ class Session
}
/**
* 删除session数据
* session设置 下一次请求有效
* @param string $name session名称
* @param mixed $value session值
* @param string|null $prefix 作用域(前缀)
* @return void
*/
public static function flash($name, $value)
{
self::set($name, $value);
if (!self::has('__flash__.__time__')) {
self::set('__flash__.__time__', $_SERVER['REQUEST_TIME_FLOAT']);
}
self::push('__flash__', $name);
}
/**
* 清空当前请求的session数据
* @return void
*/
public static function flush()
{
if (self::$init) {
$item = self::get('__flash__');
if (!empty($item)) {
$time = $item['__time__'];
if ($_SERVER['REQUEST_TIME_FLOAT'] > $time) {
unset($item['__time__']);
self::delete($item);
self::set('__flash__', []);
}
}
}
}
/**
* 删除session数据
* @param string|array $name session名称
* @param string|null $prefix 作用域(前缀)
* @return void
*/
@@ -205,7 +241,11 @@ class Session
{
empty(self::$init) && self::boot();
$prefix = !is_null($prefix) ? $prefix : self::$prefix;
if (strpos($name, '.')) {
if (is_array($name)) {
foreach ($name as $key) {
self::delete($key, $prefix);
}
} elseif (strpos($name, '.')) {
list($name1, $name2) = explode('.', $name);
if ($prefix) {
unset($_SESSION[$prefix][$name1][$name2]);
@@ -256,6 +296,22 @@ class Session
}
}
/**
* 添加数据到一个session数组
* @param string $key
* @param mixed $value
* @return void
*/
public static function push($key, $value)
{
$array = self::get($key);
if (is_null($array)) {
$array = [];
}
$array[] = $value;
self::set($key, $array);
}
/**
* 启动session
* @return void

View File

@@ -20,6 +20,7 @@ class Url
{
// 生成URL地址的root
protected static $root;
protected static $bindCheck;
/**
* URL生成 支持路由反射
@@ -31,7 +32,7 @@ class Url
*/
public static function build($url = '', $vars = '', $suffix = true, $domain = false)
{
if (false === $domain && Config::get('url_domain_deploy')) {
if (false === $domain && Route::rules('domain')) {
$domain = true;
}
// 解析URL
@@ -40,22 +41,24 @@ class Url
$name = substr($url, 1, $pos - 1);
$url = 'name' . substr($url, $pos + 1);
}
$info = parse_url($url);
$url = !empty($info['path']) ? $info['path'] : '';
if (isset($info['fragment'])) {
// 解析锚点
$anchor = $info['fragment'];
if (false !== strpos($anchor, '?')) {
// 解析参数
list($anchor, $info['query']) = explode('?', $anchor, 2);
}
if (false !== strpos($anchor, '@')) {
if (false === strpos($url, '://') && 0 !== strpos($url, '/')) {
$info = parse_url($url);
$url = !empty($info['path']) ? $info['path'] : '';
if (isset($info['fragment'])) {
// 解析锚点
$anchor = $info['fragment'];
if (false !== strpos($anchor, '?')) {
// 解析参数
list($anchor, $info['query']) = explode('?', $anchor, 2);
}
if (false !== strpos($anchor, '@')) {
// 解析域名
list($anchor, $domain) = explode('@', $anchor, 2);
}
} elseif (strpos($url, '@') && false === strpos($url, '\\')) {
// 解析域名
list($anchor, $domain) = explode('@', $anchor, 2);
list($url, $domain) = explode('@', $url, 2);
}
} elseif (strpos($url, '@')) {
// 解析域名
list($url, $domain) = explode('@', $url, 2);
}
// 解析参数
@@ -78,7 +81,7 @@ class Url
// 匹配路由命名标识
$url = $match[0];
// 替换可选分隔符
$url = preg_replace(['/\((\W)\?\)$/', '/\((\W)\?\)/'], ['', '\1'], $url);
$url = preg_replace(['/(\W)\?$/', '/(\W)\?/'], ['', '\1'], $url);
if (!empty($match[1])) {
$domain = $match[1];
}
@@ -110,11 +113,13 @@ class Url
}
// 检测URL绑定
$type = Route::getBind('type');
if ($type) {
$bind = Route::getBind($type);
if (0 === strpos($url, $bind)) {
$url = substr($url, strlen($bind) + 1);
if (!self::$bindCheck) {
$type = Route::getBind('type');
if ($type) {
$bind = Route::getBind($type);
if (0 === strpos($url, $bind)) {
$url = substr($url, strlen($bind) + 1);
}
}
}
// 还原URL分隔符
@@ -132,9 +137,10 @@ class Url
$vars = urldecode(http_build_query($vars));
$url .= $suffix . '?' . $vars . $anchor;
} else {
$paramType = Config::get('url_param_type');
foreach ($vars as $var => $val) {
if ('' !== trim($val)) {
if (Config::get('url_param_type')) {
if ($paramType) {
$url .= $depr . urlencode($val);
} else {
$url .= $depr . $var . $depr . urlencode($val);
@@ -149,12 +155,13 @@ class Url
// 检测域名
$domain = self::parseDomain($url, $domain);
// URL组装
$url = $domain . (self::$root ?: Request::instance()->root()) . '/' . ltrim($url, '/');
$url = $domain . (self::$root ?: Request::instance()->root()) . '/' . ltrim($url, '/');
self::$bindCheck = false;
return $url;
}
// 直接解析URL地址
protected static function parseUrl($url, $domain)
protected static function parseUrl($url, &$domain)
{
$request = Request::instance();
if (0 === strpos($url, '/')) {
@@ -170,14 +177,36 @@ class Url
// 解析到 模块/控制器/操作
$module = $request->module();
$domains = Route::rules('domain');
if (isset($domains[$domain]['[bind]'][0])) {
$bindModule = $domains[$domain]['[bind]'][0];
if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) {
$module = '';
if (true === $domain && 2 == substr_count($url, '/')) {
$current = $request->host();
$match = [];
$pos = [];
foreach ($domains as $key => $item) {
if (isset($item['[bind]']) && 0 === strpos($url, $item['[bind]'][0])) {
$pos[$key] = strlen($item['[bind]'][0]) + 1;
$match[] = $key;
$module = '';
}
}
if ($match) {
$domain = current($match);
foreach ($match as $item) {
if (0 === strpos($current, $item)) {
$domain = $item;
}
}
self::$bindCheck = true;
$url = substr($url, $pos[$domain]);
}
} elseif ($domain) {
if (isset($domains[$domain]['[bind]'][0])) {
$bindModule = $domains[$domain]['[bind]'][0];
if ($bindModule && !in_array($bindModule[0], ['\\', '@'])) {
$module = '';
}
}
} else {
$module = $module ? $module . '/' : '';
}
$module = $module ? $module . '/' : '';
$controller = Loader::parseName($request->controller());
if ('' == $url) {
@@ -200,15 +229,15 @@ class Url
if (!$domain) {
return '';
}
$request = Request::instance();
$request = Request::instance();
$rootDomain = Config::get('url_domain_root');
if (true === $domain) {
// 自动判断域名
$domain = $request->host();
if (Config::get('url_domain_deploy')) {
// 根域名
$urlDomainRoot = Config::get('url_domain_root');
$domains = Route::rules('domain');
$route_domain = array_keys($domains);
$domains = Route::rules('domain');
if ($domains) {
$route_domain = array_keys($domains);
foreach ($route_domain as $domain_prefix) {
if (0 === strpos($domain_prefix, '*.') && strpos($domain, ltrim($domain_prefix, '*.')) !== false) {
foreach ($domains as $key => $rule) {
@@ -217,13 +246,13 @@ class Url
$url = ltrim($url, $rule);
$domain = $key;
// 生成对应子域名
if (!empty($urlDomainRoot)) {
$domain .= $urlDomainRoot;
if (!empty($rootDomain)) {
$domain .= $rootDomain;
}
break;
} else if (false !== strpos($key, '*')) {
if (!empty($urlDomainRoot)) {
$domain .= $urlDomainRoot;
if (!empty($rootDomain)) {
$domain .= $rootDomain;
}
break;
}
@@ -231,13 +260,15 @@ class Url
}
}
}
} elseif (!strpos($domain, '.')) {
$rootDomain = Config::get('url_domain_root');
} else {
if (empty($rootDomain)) {
$host = $request->host();
$rootDomain = substr_count($host, '.') > 1 ? substr(strstr($host, '.'), 1) : $host;
}
$domain .= '.' . $rootDomain;
if (!strpos($domain, $rootDomain)) {
$domain .= '.' . $rootDomain;
}
}
return ($request->isSsl() ? 'https://' : 'http://') . $domain;
}

View File

@@ -348,16 +348,22 @@ class Validate
$result = call_user_func_array($rule, [$value, $data]);
} else {
// 判断验证类型
if (is_numeric($key) && strpos($rule, ':')) {
list($type, $rule) = explode(':', $rule, 2);
if (isset($this->alias[$type])) {
// 判断别名
$type = $this->alias[$type];
if (is_numeric($key)) {
if (strpos($rule, ':')) {
list($type, $rule) = explode(':', $rule, 2);
if (isset($this->alias[$type])) {
// 判断别名
$type = $this->alias[$type];
}
$info = $type;
} elseif (method_exists($this, $rule)) {
$type = $rule;
$info = $rule;
$rule = '';
} else {
$type = 'is';
$info = $rule;
}
$info = $type;
} elseif (is_numeric($key)) {
$type = 'is';
$info = $rule;
} else {
$info = $type = $key;
}
@@ -377,7 +383,7 @@ class Validate
// 验证失败 返回错误信息
if (isset($msg[$i])) {
$message = $msg[$i];
if (is_string($message) && strpos($message, '{%')) {
if (is_string($message) && strpos($message, '{%') === 0) {
$message = Lang::get(substr($message, 2, -1));
}
} else {
@@ -607,6 +613,9 @@ class Validate
*/
protected function activeUrl($value, $rule)
{
if (!in_array($rule, ['A', 'MX', 'NS', 'SOA', 'PTR', 'CNAME', 'AAAA', 'A6', 'SRV', 'NAPTR', 'TXT', 'ANY'])) {
$rule = 'MX';
}
return checkdnsrr($value, $rule);
}
@@ -715,19 +724,24 @@ class Validate
if (!($file instanceof File)) {
return false;
}
$rule = explode(',', $rule);
list($width, $height, $type) = getimagesize($file->getRealPath());
if (isset($rule[2])) {
$imageType = strtolower($rule[2]);
if ('jpeg' == $imageType) {
$imageType = 'jpg';
}
if (image_type_to_extension($type, false) != $imageType) {
return false;
if ($rule) {
$rule = explode(',', $rule);
list($width, $height, $type) = getimagesize($file->getRealPath());
if (isset($rule[2])) {
$imageType = strtolower($rule[2]);
if ('jpeg' == $imageType) {
$imageType = 'jpg';
}
if (image_type_to_extension($type, false) != $imageType) {
return false;
}
}
list($w, $h) = $rule;
return $w == $width && $h == $height;
} else {
return in_array($this->getImageType($file->getRealPath()), [1, 2, 3, 6]);
}
list($w, $h) = $rule;
return $w == $width && $h == $height;
}
/**

View File

@@ -109,6 +109,27 @@ abstract class Driver
}
}
/**
* 如果不存在则写入缓存
* @access public
* @param string $name 缓存变量名
* @param mixed $value 存储数据
* @param int $expire 有效时间 0为永久
* @return mixed
*/
public function remember($name, $value, $expire = null)
{
if (!$this->has($name)) {
if ($value instanceof \Closure) {
$value = call_user_func($value);
}
$this->set($name, $value, $expire);
} else {
$value = $this->get($name);
}
return $value;
}
/**
* 缓存标签
* @access public

View File

@@ -397,7 +397,15 @@ abstract class Builder
protected function parseDateTime($value, $key, $options = [], $bindName = null, $bindType = null)
{
// 获取时间字段类型
$type = $this->query->getFieldsType($options);
if (strpos($key, '.')) {
list($table, $key) = explode('.', $key);
if (isset($options['alias']) && $pos = array_search($table, $options['alias'])) {
$table = $pos;
}
} else {
$table = $options['table'];
}
$type = $this->query->getTableInfo($table, 'type');
if (isset($type[$key])) {
$info = $type[$key];
}

View File

@@ -421,6 +421,8 @@ abstract class Connection
$type = is_array($val) ? $val[1] : PDO::PARAM_STR;
if (PDO::PARAM_STR == $type) {
$value = $this->quote($value);
} elseif (PDO::PARAM_INT == $type && '' === $value) {
$value = 0;
}
// 判断占位符
$sql = is_numeric($key) ?
@@ -431,7 +433,7 @@ abstract class Connection
$sql . ' ');
}
}
return $sql;
return rtrim($sql);
}
/**
@@ -449,6 +451,9 @@ abstract class Connection
// 占位符
$param = is_numeric($key) ? $key + 1 : ':' . $key;
if (is_array($val)) {
if (PDO::PARAM_INT == $val[1] && '' === $val[0]) {
$val[0] = 0;
}
$result = $this->PDOStatement->bindValue($param, $val[0], $val[1]);
} else {
$result = $this->PDOStatement->bindValue($param, $val);

View File

@@ -373,12 +373,13 @@ class Query
* @access public
* @param string $field 字段名
* @param mixed $default 默认值
* @param bool $force 强制转为数字类型
* @return mixed
*/
public function value($field, $default = null)
public function value($field, $default = null, $force = false)
{
$result = false;
if (!empty($this->options['cache'])) {
if (empty($options['fetch_sql']) && !empty($this->options['cache'])) {
// 判断查询缓存
$cache = $this->options['cache'];
if (empty($this->options['table'])) {
@@ -397,6 +398,9 @@ class Query
return $pdo;
}
$result = $pdo->fetchColumn();
if ($force) {
$result = is_numeric($result) ? $result + 0 : $result;
}
if (isset($cache)) {
// 缓存数据
if (isset($cache['tag'])) {
@@ -422,7 +426,7 @@ class Query
public function column($field, $key = '')
{
$result = false;
if (!empty($this->options['cache'])) {
if (empty($options['fetch_sql']) && !empty($this->options['cache'])) {
// 判断查询缓存
$cache = $this->options['cache'];
if (empty($this->options['table'])) {
@@ -489,7 +493,7 @@ class Query
*/
public function count($field = '*')
{
return (int) $this->value('COUNT(' . $field . ') AS tp_count', 0);
return $this->value('COUNT(' . $field . ') AS tp_count', 0, true);
}
/**
@@ -500,29 +504,29 @@ class Query
*/
public function sum($field = '*')
{
return $this->value('SUM(' . $field . ') AS tp_sum', 0) + 0;
return $this->value('SUM(' . $field . ') AS tp_sum', 0, true);
}
/**
* MIN查询
* @access public
* @param string $field 字段名
* @return float|int
* @return mixed
*/
public function min($field = '*')
{
return $this->value('MIN(' . $field . ') AS tp_min', 0) + 0;
return $this->value('MIN(' . $field . ') AS tp_min', 0, true);
}
/**
* MAX查询
* @access public
* @param string $field 字段名
* @return float|int
* @return mixed
*/
public function max($field = '*')
{
return $this->value('MAX(' . $field . ') AS tp_max', 0) + 0;
return $this->value('MAX(' . $field . ') AS tp_max', 0, true);
}
/**
@@ -533,7 +537,7 @@ class Query
*/
public function avg($field = '*')
{
return $this->value('AVG(' . $field . ') AS tp_avg', 0) + 0;
return $this->value('AVG(' . $field . ') AS tp_avg', 0, true);
}
/**
@@ -578,8 +582,6 @@ class Query
// 清空查询条件
$this->options = [];
return true;
} else {
return $this->setField($field, $step);
}
}
return $this->setField($field, ['exp', $field . '+' . $step]);
@@ -609,8 +611,6 @@ class Query
// 清空查询条件
$this->options = [];
return true;
} else {
return $this->setField($field, $step);
}
}
return $this->setField($field, ['exp', $field . '-' . $step]);
@@ -663,28 +663,53 @@ class Query
}
}
} else {
// 传入的表名为数组
if (is_array($join)) {
if (0 !== $key = key($join)) {
// 设置了键名则键名为表名,键值作为表的别名
$table = [$key => array_shift($join)];
$this->alias($table);
} else {
$table = array_shift($join);
}
} else {
$table = trim($join);
if (strpos($table, ' ') && !strpos($table, ')')) {
list($table, $alias) = explode(' ', $table);
$table = [$table => $alias];
$this->alias($table);
}
}
$table = $this->getJoinTable($join);
$this->options['join'][] = [$table, strtoupper($type), $condition];
}
return $this;
}
/**
* 获取Join表名及别名 支持
* ['prefix_table或者子查询'=>'alias'] 'prefix_table alias' 'table alias'
* @access public
* @param array|string $join
* @return array|string
*/
protected function getJoinTable($join, &$alias = null)
{
// 传入的表名为数组
if (is_array($join)) {
list($table, $alias) = each($join);
} else {
$join = trim($join);
if (false !== strpos($join, '(')) {
// 使用子查询
$table = $join;
} else {
$prefix = $this->prefix;
if (strpos($join, ' ')) {
// 使用别名
list($table, $alias) = explode(' ', $join);
} else {
$table = $join;
if (false === strpos($join, '.') && 0 !== strpos($join, '__')) {
$alias = $join;
}
}
if ($prefix && false === strpos($table, '.') && 0 !== strpos($table, $prefix) && 0 !== strpos($table, '__')) {
$table = $this->getTable($table);
}
}
}
if (isset($alias)) {
$table = [$table => $alias];
$this->alias($table);
}
return $table;
}
/**
* 查询SQL组装 union
* @access public
@@ -767,13 +792,8 @@ class Query
}
} else {
$fields = [];
if (is_array($join)) {
// 支持数据表别名
list($join, $alias, $table) = array_pad($join, 3, '');
} else {
$alias = $join;
}
$table = !empty($table) ? $table : $this->getTable($join);
$table = $this->getJoinTable($join, $alias);
if (true === $field) {
$fields = $alias . '.*';
} else {
@@ -797,9 +817,9 @@ class Query
}
$this->field($fields);
if ($on) {
$this->join($table . ' ' . $alias, $on, $type);
$this->join($table, $on, $type);
} else {
$this->table($table . ' ' . $alias);
$this->table($table);
}
}
return $this;
@@ -897,13 +917,9 @@ class Query
if (is_array($field)) {
// 数组批量查询
$where = $field;
} elseif ($field) {
} elseif ($field && is_string($field)) {
// 字符串查询
if (is_numeric($field)) {
$where[] = ['exp', $field];
} else {
$where[$field] = ['null', ''];
}
$where[$field] = ['null', ''];
}
} elseif (is_array($op)) {
$where[$field] = $param;
@@ -924,6 +940,22 @@ class Query
}
}
/**
* 去除某个查询条件
* @access public
* @param string $field 查询字段
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function removeWhereField($field, $logic = 'AND')
{
$logic = strtoupper($logic);
if (isset($this->options['where'][$logic][$field])) {
unset($this->options['where'][$logic][$field]);
}
return $this;
}
/**
* 指定查询数量
* @access public
@@ -993,8 +1025,11 @@ class Query
if (!isset($total) && !$simple) {
$options = $this->getOptions();
$total = $this->count();
if (isset($options['order'])) {
unset($this->options['order']);
}
$bind = $this->bind;
$total = $this->count();
$results = $this->options($options)->bind($bind)->page($page, $listRows)->select();
} elseif ($simple) {
$results = $this->limit(($page - 1) * $listRows, $listRows + 1)->select();
@@ -1014,7 +1049,10 @@ class Query
public function table($table)
{
if (is_string($table)) {
if (strpos($table, ',')) {
if (strpos($table, ')')) {
// 子查询
$table = $table;
} elseif (strpos($table, ',')) {
$tables = explode(',', $table);
$table = [];
foreach ($tables as $item) {
@@ -1174,6 +1212,9 @@ class Query
} else {
if (isset($this->options['table'])) {
$table = is_array($this->options['table']) ? key($this->options['table']) : $this->options['table'];
if (false !== strpos($table, '__')) {
$table = $this->parseSqlTable($table);
}
} else {
$table = $this->getTable();
}
@@ -1595,6 +1636,8 @@ class Query
$field = $this->options['with_field'];
unset($this->options['with_field']);
}
} elseif (isset($info['option']['field'])) {
$field = $info['option']['field'];
}
$this->field($field, false, $joinTable, $joinAlias, $relation . '__');
$i++;

View File

@@ -0,0 +1,24 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: yunwuxin <448901948@qq.com>
// +----------------------------------------------------------------------
namespace think\exception;
use think\exception\HttpException;
class RouteNotFoundException extends HttpException
{
public function __construct()
{
parent::__construct(404);
}
}

View File

@@ -61,14 +61,14 @@ class Merge extends Model
{
$class = new static();
$master = $class->name;
$fields = self::getModelField($query, $master, '', $class->mapFields);
$fields = self::getModelField($query, $master, '', $class->mapFields, $class->field);
$query->alias($master)->field($fields);
foreach ($class->relationModel as $key => $model) {
$name = is_int($key) ? $model : $key;
$table = is_int($key) ? $query->getTable($name) : $model;
$query->join($table . ' ' . $name, $name . '.' . $class->fk . '=' . $master . '.' . $class->getPk());
$fields = self::getModelField($query, $name, $table, $class->mapFields);
$fields = self::getModelField($query, $name, $table, $class->mapFields, $class->field);
$query->field($fields);
}
return $query;
@@ -81,12 +81,13 @@ class Merge extends Model
* @param string $name 模型名称
* @param string $table 关联表名称
* @param array $map 字段映射
* @param array $fields 查询字段
* @return array
*/
protected static function getModelField($query, $name, $table = '', $map = [])
protected static function getModelField($query, $name, $table = '', $map = [], $fields = [])
{
// 获取模型的字段信息
$fields = $query->getTableInfo($table, 'fields');
$fields = $fields ?: $query->getTableInfo($table, 'fields');
$array = [];
foreach ($fields as $field) {
if ($key = array_search($name . '.' . $field, $map)) {

View File

@@ -47,7 +47,8 @@ class Relation
protected $query;
// 关联查询条件
protected $where;
// 关联查询参数
protected $option;
/**
* 架构函数
* @access public
@@ -74,6 +75,7 @@ class Relation
'localKey' => $this->localKey,
'alias' => $this->alias,
'joinType' => $this->joinType,
'option' => $this->option,
];
return $name ? $info[$name] : $info;
}
@@ -689,8 +691,10 @@ class Relation
}
$result = call_user_func_array([$this->query, $method], $args);
if ($result instanceof \think\db\Query) {
$this->option = $result->getOptions();
return $this;
} else {
$this->option = [];
return $result;
}
} else {

View File

@@ -27,17 +27,25 @@ class Json extends Response
* @access protected
* @param mixed $data 要处理的数据
* @return mixed
* @throws \Exception
*/
protected function output($data)
{
// 返回JSON数据格式到客户端 包含状态信息
$data = json_encode($data, $this->options['json_encode_param']);
try {
// 返回JSON数据格式到客户端 包含状态信息
$data = json_encode($data, $this->options['json_encode_param']);
if ($data === false) {
throw new \InvalidArgumentException(json_last_error_msg());
if ($data === false) {
throw new \InvalidArgumentException(json_last_error_msg());
}
return $data;
} catch (\Exception $e) {
if ($e->getPrevious()) {
throw $e->getPrevious();
}
throw $e;
}
return $data;
}
}

View File

@@ -30,21 +30,29 @@ class Jsonp extends Response
* @access protected
* @param mixed $data 要处理的数据
* @return mixed
* @throws \Exception
*/
protected function output($data)
{
// 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的故使用Request来获取<xiaobo.sun@qq.com>]
$var_jsonp_handler = Request::instance()->param($this->options['var_jsonp_handler'], "");
$handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler'];
try {
// 返回JSON数据格式到客户端 包含状态信息 [当url_common_param为false时是无法获取到$_GET的数据的故使用Request来获取<xiaobo.sun@qq.com>]
$var_jsonp_handler = Request::instance()->param($this->options['var_jsonp_handler'], "");
$handler = !empty($var_jsonp_handler) ? $var_jsonp_handler : $this->options['default_jsonp_handler'];
$data = json_encode($data, $this->options['json_encode_param']);
$data = json_encode($data, $this->options['json_encode_param']);
if ($data === false) {
throw new \InvalidArgumentException(json_last_error_msg());
if ($data === false) {
throw new \InvalidArgumentException(json_last_error_msg());
}
$data = $handler . '(' . $data . ');';
return $data;
} catch (\Exception $e) {
if ($e->getPrevious()) {
throw $e->getPrevious();
}
throw $e;
}
$data = $handler . '(' . $data . ');';
return $data;
}
}

View File

@@ -53,10 +53,10 @@ class Redirect extends Response
{
if (is_array($name)) {
foreach ($name as $key => $val) {
Session::set($key, $val);
Session::flash($key, $val);
}
} else {
Session::set($name, $value);
Session::flash($name, $value);
}
return $this;
}

View File

@@ -24,6 +24,8 @@ class Think
private $template;
// 模板引擎参数
protected $config = [
// 视图基础目录(集中式)
'view_base' => '',
// 模板起始路径
'view_path' => '',
// 模板文件后缀
@@ -103,18 +105,21 @@ class Think
*/
private function parseTemplate($template)
{
// 分析模板文件规则
$request = Request::instance();
// 获取视图根目录
if (strpos($template, '@')) {
// 跨模块调用
list($module, $template) = explode('@', $template);
$path = APP_PATH . $module . DS . 'view' . DS;
}
if ($this->config['view_base']) {
// 基础视图目录
$module = isset($module) ? $module : $request->module();
$path = $this->config['view_base'] . ($module ? $module . DS : '');
} else {
// 当前视图目录
$path = $this->config['view_path'];
$path = isset($module) ? APP_PATH . $module . DS . 'view' . DS : $this->config['view_path'];
}
// 分析模板文件规则
$request = Request::instance();
$controller = Loader::parseName($request->controller());
if ($controller && 0 !== strpos($template, '/')) {
$depr = $this->config['view_depr'];