modified 升级ThinkPHP核心到5.0.19版本

This commit is contained in:
zhaoxiang 2018-05-08 00:59:51 +08:00
parent f5cc7aa3ee
commit 5175d614de
26 changed files with 452 additions and 216 deletions

View File

@ -9,7 +9,7 @@
// | Author: liu21st <liu21st@gmail.com> // | Author: liu21st <liu21st@gmail.com>
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
define('THINK_VERSION', '5.0.16'); define('THINK_VERSION', '5.0.19');
define('THINK_START_TIME', microtime(true)); define('THINK_START_TIME', microtime(true));
define('THINK_START_MEM', memory_get_usage()); define('THINK_START_MEM', memory_get_usage());
define('EXT', '.php'); define('EXT', '.php');

View File

@ -116,6 +116,8 @@ return [
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
'template' => [ 'template' => [
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
// 模板引擎类型 支持 php think 支持扩展 // 模板引擎类型 支持 php think 支持扩展
'type' => 'Think', 'type' => 'Think',
// 视图基础目录,配置目录为所有模块的视图起始目录 // 视图基础目录,配置目录为所有模块的视图起始目录

View File

@ -443,12 +443,6 @@ if (!function_exists('view')) {
*/ */
function view($template = '', $vars = [], $replace = [], $code = 200) function view($template = '', $vars = [], $replace = [], $code = 200)
{ {
if ('' === $template) {
$trace = debug_backtrace(false, 2);
$suffix = Config::get('action_suffix');
$action = $suffix ? substr($trace[1]['function'], 0, -strlen($suffix)) : $trace[1]['function'];
$template = Loader::parseName($action);
}
return Response::create($template, 'view', $code)->replace($replace)->assign($vars); return Response::create($template, 'view', $code)->replace($replace)->assign($vars);
} }
} }

View File

@ -66,6 +66,7 @@ return [
'relation data not exists' => '关联数据不存在', 'relation data not exists' => '关联数据不存在',
'relation not support' => '关联不支持', 'relation not support' => '关联不支持',
'chunk not support order' => 'Chunk不支持调用order方法', 'chunk not support order' => 'Chunk不支持调用order方法',
'closure not support cache(true)' => '使用闭包查询不支持cache(true)请指定缓存Key',
// 上传错误信息 // 上传错误信息
'unknown upload error' => '未知上传错误!', 'unknown upload error' => '未知上传错误!',

View File

@ -555,7 +555,11 @@ class App
// 获取操作名 // 获取操作名
$actionName = strip_tags($result[2] ?: $config['default_action']); $actionName = strip_tags($result[2] ?: $config['default_action']);
$actionName = $convert ? strtolower($actionName) : $actionName; if (!empty($config['action_convert'])) {
$actionName = Loader::parseName($actionName, 1);
} else {
$actionName = $convert ? strtolower($actionName) : $actionName;
}
// 设置当前请求的控制器、操作 // 设置当前请求的控制器、操作
$request->controller(Loader::parseName($controller, 1))->action($actionName); $request->controller(Loader::parseName($controller, 1))->action($actionName);
@ -581,6 +585,13 @@ class App
if (is_callable([$instance, $action])) { if (is_callable([$instance, $action])) {
// 执行操作方法 // 执行操作方法
$call = [$instance, $action]; $call = [$instance, $action];
// 严格获取当前操作方法名
$reflect = new \ReflectionMethod($instance, $action);
$methodName = $reflect->getName();
$suffix = $config['action_suffix'];
$actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
$request->action($actionName);
} elseif (is_callable([$instance, '_empty'])) { } elseif (is_callable([$instance, '_empty'])) {
// 空操作 // 空操作
$call = [$instance, '_empty']; $call = [$instance, '_empty'];

View File

@ -117,13 +117,6 @@ class Controller
*/ */
protected function fetch($template = '', $vars = [], $replace = [], $config = []) protected function fetch($template = '', $vars = [], $replace = [], $config = [])
{ {
if ('' === $template) {
$trace = debug_backtrace(false, 2);
$suffix = Config::get('action_suffix');
$action = $suffix ? substr($trace[1]['function'], 0, -strlen($suffix)) : $trace[1]['function'];
$template = Loader::parseName($action);
}
return $this->view->fetch($template, $vars, $replace, $config); return $this->view->fetch($template, $vars, $replace, $config);
} }

View File

@ -23,7 +23,7 @@ class Loader
/** /**
* @var array 类名映射 * @var array 类名映射
*/ */
protected static $map = []; protected static $classMap = [];
/** /**
* @var array 命名空间别名 * @var array 命名空间别名
@ -56,9 +56,9 @@ class Loader
private static $fallbackDirsPsr0 = []; private static $fallbackDirsPsr0 = [];
/** /**
* @var array 自动加载的文件 * @var array 需要加载的文件
*/ */
private static $autoloadFiles = []; private static $files = [];
/** /**
* 自动加载 * 自动加载
@ -99,8 +99,8 @@ class Loader
private static function findFile($class) private static function findFile($class)
{ {
// 类库映射 // 类库映射
if (!empty(self::$map[$class])) { if (!empty(self::$classMap[$class])) {
return self::$map[$class]; return self::$classMap[$class];
} }
// 查找 PSR-4 // 查找 PSR-4
@ -156,7 +156,7 @@ class Loader
} }
// 找不到则设置映射为 false 并返回 // 找不到则设置映射为 false 并返回
return self::$map[$class] = false; return self::$classMap[$class] = false;
} }
/** /**
@ -169,9 +169,9 @@ class Loader
public static function addClassMap($class, $map = '') public static function addClassMap($class, $map = '')
{ {
if (is_array($class)) { if (is_array($class)) {
self::$map = array_merge(self::$map, $class); self::$classMap = array_merge(self::$classMap, $class);
} else { } else {
self::$map[$class] = $map; self::$classMap[$class] = $map;
} }
} }
@ -292,12 +292,11 @@ class Loader
$declaredClass = get_declared_classes(); $declaredClass = get_declared_classes();
$composerClass = array_pop($declaredClass); $composerClass = array_pop($declaredClass);
self::$prefixLengthsPsr4 = $composerClass::$prefixLengthsPsr4; foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
if (property_exists($composerClass, $attr)) {
self::$prefixDirsPsr4 = property_exists($composerClass, 'prefixDirsPsr4') ? $composerClass::$prefixDirsPsr4 : []; self::${$attr} = $composerClass::${$attr};
}
self::$prefixesPsr0 = property_exists($composerClass, 'prefixesPsr0') ? $composerClass::$prefixesPsr0 : []; }
self::$map = property_exists($composerClass, 'classMap') ? $composerClass::$classMap : [];
} else { } else {
self::registerComposerLoader(); self::registerComposerLoader();
} }
@ -348,18 +347,20 @@ class Loader
self::addClassMap($classMap); self::addClassMap($classMap);
} }
} }
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) {
self::$files = require VENDOR_PATH . 'composer/autoload_files.php';
}
} }
// 加载composer autofile文件 // 加载composer autofile文件
public static function loadComposerAutoloadFiles() public static function loadComposerAutoloadFiles()
{ {
if (is_file(VENDOR_PATH . 'composer/autoload_files.php')) { foreach (self::$files as $fileIdentifier => $file) {
$includeFiles = require VENDOR_PATH . 'composer/autoload_files.php'; if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
foreach ($includeFiles as $fileIdentifier => $file) { __require_file($file);
if (empty(self::$autoloadFiles[$fileIdentifier])) {
__require_file($file); $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
self::$autoloadFiles[$fileIdentifier] = true;
}
} }
} }
} }

View File

@ -116,6 +116,12 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
*/ */
protected static $initialized = []; protected static $initialized = [];
/**
* 是否从主库读取(主从分布式有效)
* @var array
*/
protected static $readMaster;
/** /**
* 构造方法 * 构造方法
* @access public * @access public
@ -171,6 +177,20 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$this->initialize(); $this->initialize();
} }
/**
* 是否从主库读取数据(主从分布有效)
* @access public
* @param bool $all 是否所有模型生效
* @return $this
*/
public function readMaster($all = false)
{
$model = $all ? '*' : $this->class;
static::$readMaster[$model] = true;
return $this;
}
/** /**
* 创建模型的查询对象 * 创建模型的查询对象
* @access protected * @access protected
@ -194,6 +214,10 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$queryClass = $this->query ?: $con->getConfig('query'); $queryClass = $this->query ?: $con->getConfig('query');
$query = new $queryClass($con, $this); $query = new $queryClass($con, $this);
if (isset(static::$readMaster['*']) || isset(static::$readMaster[$this->class])) {
$query->master(true);
}
// 设置当前数据表和模型名 // 设置当前数据表和模型名
if (!empty($this->table)) { if (!empty($this->table)) {
$query->setTable($this->table); $query->setTable($this->table);

View File

@ -395,7 +395,15 @@ abstract class Paginator implements ArrayAccess, Countable, IteratorAggregate, J
public function __call($name, $arguments) public function __call($name, $arguments)
{ {
return call_user_func_array([$this->getCollection(), $name], $arguments); $collection = $this->getCollection();
$result = call_user_func_array([$collection, $name], $arguments);
if ($result === $collection) {
return $this;
}
return $result;
} }
} }

View File

@ -232,7 +232,7 @@ class Request
parse_str(html_entity_decode($info['query']), $query); parse_str(html_entity_decode($info['query']), $query);
if (!empty($params)) { if (!empty($params)) {
$params = array_replace($query, $params); $params = array_replace($query, $params);
$queryString = http_build_query($query, '', '&'); $queryString = http_build_query($params, '', '&');
} else { } else {
$params = $query; $params = $query;
$queryString = $info['query']; $queryString = $info['query'];
@ -1093,7 +1093,7 @@ class Request
public function filterExp(&$value) public function filterExp(&$value)
{ {
// 过滤查询特殊字符 // 过滤查询特殊字符
if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) { if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT LIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOT EXISTS|NOTEXISTS|EXISTS|NOT NULL|NOTNULL|NULL|BETWEEN TIME|NOT BETWEEN TIME|NOTBETWEEN TIME|NOTIN|NOT IN|IN)$/i', $value)) {
$value .= ' '; $value .= ' ';
} }
// TODO 其他安全过滤 // TODO 其他安全过滤
@ -1273,7 +1273,11 @@ class Request
return $ip[$type]; return $ip[$type];
} }
if ($adv) { $httpAgentIp = Config::get('http_agent_ip');
if ($httpAgentIp && isset($_SERVER[$httpAgentIp])) {
$ip = $_SERVER[$httpAgentIp];
} elseif ($adv) {
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $arr = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$pos = array_search('unknown', $arr); $pos = array_search('unknown', $arr);
@ -1338,14 +1342,18 @@ class Request
/** /**
* 当前请求的host * 当前请求的host
* @access public * @access public
* @param bool $strict true 仅仅获取HOST
* @return string * @return string
*/ */
public function host() public function host($strict = false)
{ {
if (isset($_SERVER['HTTP_X_REAL_HOST'])) { if (isset($_SERVER['HTTP_X_REAL_HOST'])) {
return $_SERVER['HTTP_X_REAL_HOST']; $host = $_SERVER['HTTP_X_REAL_HOST'];
} else {
$host = $this->server('HTTP_HOST');
} }
return $this->server('HTTP_HOST');
return true === $strict && strpos($host, ':') ? strstr($host, ':', true) : $host;
} }
/** /**
@ -1466,11 +1474,12 @@ class Request
*/ */
public function action($action = null) public function action($action = null)
{ {
if (!is_null($action)) { if (!is_null($action) && !is_bool($action)) {
$this->action = $action; $this->action = $action;
return $this; return $this;
} else { } else {
return $this->action ?: ''; $name = $this->action ?: '';
return true === $action ? $name : strtolower($name);
} }
} }

View File

@ -69,9 +69,7 @@ class Response
*/ */
public static function create($data = '', $type = '', $code = 200, array $header = [], $options = []) public static function create($data = '', $type = '', $code = 200, array $header = [], $options = [])
{ {
$type = empty($type) ? 'null' : strtolower($type); $class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst(strtolower($type));
$class = false !== strpos($type, '\\') ? $type : '\\think\\response\\' . ucfirst($type);
if (class_exists($class)) { if (class_exists($class)) {
$response = new $class($data, $code, $header, $options); $response = new $class($data, $code, $header, $options);
} else { } else {

View File

@ -737,7 +737,7 @@ class Route
$rules = self::$rules['domain']; $rules = self::$rules['domain'];
// 开启子域名部署 支持二级和三级域名 // 开启子域名部署 支持二级和三级域名
if (!empty($rules)) { if (!empty($rules)) {
$host = $request->host(); $host = $request->host(true);
if (isset($rules[$host])) { if (isset($rules[$host])) {
// 完整域名或者IP配置 // 完整域名或者IP配置
$item = $rules[$host]; $item = $rules[$host];
@ -1506,7 +1506,7 @@ class Route
App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : ''); App::$modulePath = APP_PATH . (Config::get('app_multi_module') ? $request->module() . DS : '');
} else { } else {
// 路由到模块/控制器/操作 // 路由到模块/控制器/操作
$result = self::parseModule($route); $result = self::parseModule($route, isset($option['convert']) ? $option['convert'] : false);
} }
// 开启请求缓存 // 开启请求缓存
if ($request->isGet() && isset($option['cache'])) { if ($request->isGet() && isset($option['cache'])) {
@ -1527,9 +1527,10 @@ class Route
* 解析URL地址为 模块/控制器/操作 * 解析URL地址为 模块/控制器/操作
* @access private * @access private
* @param string $url URL地址 * @param string $url URL地址
* @param bool $convert 是否自动转换URL地址
* @return array * @return array
*/ */
private static function parseModule($url) private static function parseModule($url, $convert = false)
{ {
list($path, $var) = self::parseUrlPath($url); list($path, $var) = self::parseUrlPath($url);
$action = array_pop($path); $action = array_pop($path);
@ -1543,7 +1544,7 @@ class Route
// 设置当前请求的路由变量 // 设置当前请求的路由变量
Request::instance()->route($var); Request::instance()->route($var);
// 路由到模块/控制器/操作 // 路由到模块/控制器/操作
return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => false]; return ['type' => 'module', 'module' => [$module, $controller, $action], 'convert' => $convert];
} }
/** /**

View File

@ -239,7 +239,7 @@ class Url
$rootDomain = Config::get('url_domain_root'); $rootDomain = Config::get('url_domain_root');
if (true === $domain) { if (true === $domain) {
// 自动判断域名 // 自动判断域名
$domain = Config::get('app_host') ?: $request->host(); $domain = Config::get('app_host') ?: $request->host(true);
$domains = Route::rules('domain'); $domains = Route::rules('domain');
if ($domains) { if ($domains) {

View File

@ -11,7 +11,6 @@
namespace think\db; namespace think\db;
use BadMethodCallException;
use PDO; use PDO;
use think\Exception; use think\Exception;
@ -99,8 +98,11 @@ abstract class Builder
$result = []; $result = [];
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
$item = $this->parseKey($key, $options); $item = $this->parseKey($key, $options, true);
if (is_object($val) && method_exists($val, '__toString')) { if ($val instanceof Expression) {
$result[$item] = $val->getValue();
continue;
} elseif (is_object($val) && method_exists($val, '__toString')) {
// 对象数据写入 // 对象数据写入
$val = $val->__toString(); $val = $val->__toString();
} }
@ -112,18 +114,11 @@ abstract class Builder
$result[$item] = 'NULL'; $result[$item] = 'NULL';
} elseif (is_array($val) && !empty($val)) { } elseif (is_array($val) && !empty($val)) {
switch ($val[0]) { switch ($val[0]) {
case 'exp':
$result[$item] = $val[1];
break;
case 'inc': case 'inc':
if ($key == $val[1]) { $result[$item] = $item . '+' . floatval($val[1]);
$result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
}
break; break;
case 'dec': case 'dec':
if ($key == $val[1]) { $result[$item] = $item . '-' . floatval($val[1]);
$result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
}
break; break;
} }
} elseif (is_scalar($val)) { } elseif (is_scalar($val)) {
@ -147,7 +142,7 @@ abstract class Builder
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
return $key; return $key;
} }
@ -188,8 +183,10 @@ abstract class Builder
// 支持 'field1'=>'field2' 这样的字段别名定义 // 支持 'field1'=>'field2' 这样的字段别名定义
$array = []; $array = [];
foreach ($fields as $key => $field) { foreach ($fields as $key => $field) {
if (!is_numeric($key)) { if ($field instanceof Expression) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); $array[] = $field->getValue();
} elseif (!is_numeric($key)) {
$array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true);
} else { } else {
$array[] = $this->parseKey($field, $options); $array[] = $this->parseKey($field, $options);
} }
@ -268,6 +265,10 @@ abstract class Builder
foreach ($where as $key => $val) { foreach ($where as $key => $val) {
$str = []; $str = [];
foreach ($val as $field => $value) { foreach ($val as $field => $value) {
if ($value instanceof Expression) {
$str[] = ' ' . $key . ' ( ' . $value->getValue() . ' )';
continue;
}
if ($value instanceof \Closure) { if ($value instanceof \Closure) {
// 使用闭包查询 // 使用闭包查询
$query = new Query($this->connection); $query = new Query($this->connection);
@ -309,7 +310,7 @@ abstract class Builder
protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null) protected function parseWhereItem($field, $val, $rule = '', $options = [], $binds = [], $bindName = null)
{ {
// 字段分析 // 字段分析
$key = $field ? $this->parseKey($field, $options) : ''; $key = $field ? $this->parseKey($field, $options, true) : '';
// 查询规则和条件 // 查询规则和条件
if (!is_array($val)) { if (!is_array($val)) {
@ -348,7 +349,9 @@ abstract class Builder
$bindName = md5($bindName); $bindName = md5($bindName);
} }
if (is_object($value) && method_exists($value, '__toString')) { if ($value instanceof Expression) {
} elseif (is_object($value) && method_exists($value, '__toString')) {
// 对象数据写入 // 对象数据写入
$value = $value->__toString(); $value = $value->__toString();
} }
@ -385,7 +388,11 @@ abstract class Builder
} }
} elseif ('EXP' == $exp) { } elseif ('EXP' == $exp) {
// 表达式查询 // 表达式查询
$whereStr .= '( ' . $key . ' ' . $value . ' )'; if ($value instanceof Expression) {
$whereStr .= '( ' . $key . ' ' . $value->getValue() . ' )';
} else {
throw new Exception('where express error:' . $exp);
}
} elseif (in_array($exp, ['NOT NULL', 'NULL'])) { } elseif (in_array($exp, ['NOT NULL', 'NULL'])) {
// NULL 查询 // NULL 查询
$whereStr .= $key . ' IS ' . $exp; $whereStr .= $key . ' IS ' . $exp;
@ -503,6 +510,11 @@ abstract class Builder
} }
} }
$bindName = $bindName ?: $key; $bindName = $bindName ?: $key;
if ($this->query->isBind($bindName)) {
$bindName .= '_' . str_replace('.', '_', uniqid('', true));
}
$this->query->bind($bindName, $value, $bindType); $this->query->bind($bindName, $value, $bindType);
return ':' . $bindName; return ':' . $bindName;
} }
@ -533,7 +545,9 @@ abstract class Builder
list($table, $type, $on) = $item; list($table, $type, $on) = $item;
$condition = []; $condition = [];
foreach ((array) $on as $val) { foreach ((array) $on as $val) {
if (strpos($val, '=')) { if ($val instanceof Expression) {
$condition[] = $val->getValue();
} elseif (strpos($val, '=')) {
list($val1, $val2) = explode('=', $val, 2); list($val1, $val2) = explode('=', $val, 2);
$condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options); $condition[] = $this->parseKey($val1, $options) . '=' . $this->parseKey($val2, $options);
} else { } else {
@ -557,28 +571,29 @@ abstract class Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
$array = []; return '';
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if ('[rand]' == $val) {
if (method_exists($this, 'parseRand')) {
$array[] = $this->parseRand();
} else {
throw new BadMethodCallException('method not exists:' . get_class($this) . '-> parseRand');
}
} elseif (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
} }
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
if (is_numeric($key)) {
list($key, $sort) = explode(' ', strpos($val, ' ') ? $val : $val . ' ');
} else {
$sort = $val;
}
$sort = strtoupper($sort);
$sort = in_array($sort, ['ASC', 'DESC'], true) ? ' ' . $sort : '';
$array[] = $this->parseKey($key, $options, true) . $sort;
}
}
$order = implode(',', $array);
return !empty($order) ? ' ORDER BY ' . $order : ''; return !empty($order) ? ' ORDER BY ' . $order : '';
} }
@ -612,6 +627,9 @@ abstract class Builder
*/ */
protected function parseComment($comment) protected function parseComment($comment)
{ {
if (false !== strpos($comment, '*/')) {
$comment = strstr($coment, '*/', true);
}
return !empty($comment) ? ' /* ' . $comment . ' */' : ''; return !empty($comment) ? ' /* ' . $comment . ' */' : '';
} }
@ -661,11 +679,7 @@ abstract class Builder
return ''; return '';
} }
if (is_array($index)) { return sprintf(" FORCE INDEX ( %s ) ", is_array($index) ? implode(',', $index) : $index);
$index = join(",", $index);
}
return sprintf(" FORCE INDEX ( %s ) ", $index);
} }
/** /**
@ -783,10 +797,14 @@ abstract class Builder
$values[] = 'SELECT ' . implode(',', $value); $values[] = 'SELECT ' . implode(',', $value);
if (!isset($insertFields)) { if (!isset($insertFields)) {
$insertFields = array_map([$this, 'parseKey'], array_keys($data)); $insertFields = array_keys($data);
} }
} }
foreach ($insertFields as $field) {
$fields[] = $this->parseKey($query, $field, true);
}
return str_replace( return str_replace(
['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'], ['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],
[ [

View File

@ -90,6 +90,8 @@ abstract class Connection
'master_num' => 1, 'master_num' => 1,
// 指定从服务器序号 // 指定从服务器序号
'slave_no' => '', 'slave_no' => '',
// 模型写入后自动读取主服务器
'read_master' => false,
// 是否严格检查字段是否存在 // 是否严格检查字段是否存在
'fields_strict' => true, 'fields_strict' => true,
// 数据返回类型 // 数据返回类型
@ -378,7 +380,7 @@ abstract class Connection
// 执行查询 // 执行查询
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false, '', $master);
// 返回结果集 // 返回结果集
return $this->getResult($pdo, $procedure); return $this->getResult($pdo, $procedure);
} catch (\PDOException $e) { } catch (\PDOException $e) {
@ -402,13 +404,14 @@ abstract class Connection
/** /**
* 执行语句 * 执行语句
* @access public * @access public
* @param string $sql sql指令 * @param string $sql sql指令
* @param array $bind 参数绑定 * @param array $bind 参数绑定
* @param Query $query 查询对象
* @return int * @return int
* @throws PDOException * @throws PDOException
* @throws \Exception * @throws \Exception
*/ */
public function execute($sql, $bind = []) public function execute($sql, $bind = [], Query $query = null)
{ {
$this->initConnect(true); $this->initConnect(true);
if (!$this->linkID) { if (!$this->linkID) {
@ -445,23 +448,27 @@ abstract class Connection
// 执行语句 // 执行语句
$this->PDOStatement->execute(); $this->PDOStatement->execute();
// 调试结束 // 调试结束
$this->debug(false); $this->debug(false, '', true);
if ($query && !empty($this->config['deploy']) && !empty($this->config['read_master'])) {
$query->readMaster();
}
$this->numRows = $this->PDOStatement->rowCount(); $this->numRows = $this->PDOStatement->rowCount();
return $this->numRows; return $this->numRows;
} catch (\PDOException $e) { } catch (\PDOException $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw new PDOException($e, $this->config, $this->getLastsql()); throw new PDOException($e, $this->config, $this->getLastsql());
} catch (\Throwable $e) { } catch (\Throwable $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw $e; throw $e;
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
return $this->close()->execute($sql, $bind); return $this->close()->execute($sql, $bind, $query);
} }
throw $e; throw $e;
} }
@ -744,7 +751,7 @@ abstract class Connection
* @param array $sqlArray SQL批处理指令 * @param array $sqlArray SQL批处理指令
* @return boolean * @return boolean
*/ */
public function batchQuery($sqlArray = [], $bind = []) public function batchQuery($sqlArray = [], $bind = [], Query $query = null)
{ {
if (!is_array($sqlArray)) { if (!is_array($sqlArray)) {
return false; return false;
@ -753,7 +760,7 @@ abstract class Connection
$this->startTrans(); $this->startTrans();
try { try {
foreach ($sqlArray as $sql) { foreach ($sqlArray as $sql) {
$this->execute($sql, $bind); $this->execute($sql, $bind, $query);
} }
// 提交事务 // 提交事务
$this->commit(); $this->commit();
@ -904,9 +911,10 @@ abstract class Connection
* @access protected * @access protected
* @param boolean $start 调试开始标记 true 开始 false 结束 * @param boolean $start 调试开始标记 true 开始 false 结束
* @param string $sql 执行的SQL语句 留空自动获取 * @param string $sql 执行的SQL语句 留空自动获取
* @param boolean $master 主从标记
* @return void * @return void
*/ */
protected function debug($start, $sql = '') protected function debug($start, $sql = '', $master = false)
{ {
if (!empty($this->config['debug'])) { if (!empty($this->config['debug'])) {
// 开启数据库调试模式 // 开启数据库调试模式
@ -923,7 +931,7 @@ abstract class Connection
$result = $this->getExplain($sql); $result = $this->getExplain($sql);
} }
// SQL监听 // SQL监听
$this->trigger($sql, $runtime, $result); $this->trigger($sql, $runtime, $result, $master);
} }
} }
} }
@ -945,19 +953,27 @@ abstract class Connection
* @param string $sql SQL语句 * @param string $sql SQL语句
* @param float $runtime SQL运行时间 * @param float $runtime SQL运行时间
* @param mixed $explain SQL分析 * @param mixed $explain SQL分析
* @return bool * @param bool $master 主从标记
* @return void
*/ */
protected function trigger($sql, $runtime, $explain = []) protected function trigger($sql, $runtime, $explain = [], $master = false)
{ {
if (!empty(self::$event)) { if (!empty(self::$event)) {
foreach (self::$event as $callback) { foreach (self::$event as $callback) {
if (is_callable($callback)) { if (is_callable($callback)) {
call_user_func_array($callback, [$sql, $runtime, $explain]); call_user_func_array($callback, [$sql, $runtime, $explain, $master]);
} }
} }
} else { } else {
// 未注册监听则记录到日志中 // 未注册监听则记录到日志中
Log::record('[ SQL ] ' . $sql . ' [ RunTime:' . $runtime . 's ]', 'sql'); if ($this->config['deploy']) {
// 分布式记录当前操作的主从
$master = $master ? 'master|' : 'slave|';
} else {
$master = '';
}
Log::record('[ SQL ] ' . $sql . ' [ ' . $master . 'RunTime:' . $runtime . 's ]', 'sql');
if (!empty($explain)) { if (!empty($explain)) {
Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql'); Log::record('[ EXPLAIN : ' . var_export($explain, true) . ' ]', 'sql');
} }

View File

@ -53,6 +53,8 @@ class Query
protected static $info = []; protected static $info = [];
// 回调事件 // 回调事件
private static $event = []; private static $event = [];
// 读取主库
private static $readMaster = [];
/** /**
* 构造函数 * 构造函数
@ -140,6 +142,25 @@ class Query
return $this->model; return $this->model;
} }
/**
* 设置后续从主库读取数据
* @access public
* @param bool $allTable
* @return void
*/
public function readMaster($allTable = false)
{
if ($allTable) {
$table = '*';
} else {
$table = isset($this->options['table']) ? $this->options['table'] : $this->getTable();
}
static::$readMaster[$table] = true;
return $this;
}
/** /**
* 获取当前的builder实例对象 * 获取当前的builder实例对象
* @access public * @access public
@ -238,7 +259,7 @@ class Query
*/ */
public function execute($sql, $bind = []) public function execute($sql, $bind = [])
{ {
return $this->connection->execute($sql, $bind); return $this->connection->execute($sql, $bind, $this);
} }
/** /**
@ -609,7 +630,7 @@ class Query
return true; return true;
} }
} }
return $this->setField($field, ['inc', $field, $step]); return $this->setField($field, ['inc', $step]);
} }
/** /**
@ -637,9 +658,9 @@ class Query
$this->options = []; $this->options = [];
return true; return true;
} }
return $this->setField($field, ['inc', $field, $step]); return $this->setField($field, ['inc', $step]);
} }
return $this->setField($field, ['dec', $field, $step]); return $this->setField($field, ['dec', $step]);
} }
/** /**
@ -769,8 +790,15 @@ class Query
{ {
if (empty($field)) { if (empty($field)) {
return $this; return $this;
} elseif ($field instanceof Expression) {
$this->options['field'][] = $field;
return $this;
} }
if (is_string($field)) { if (is_string($field)) {
if (preg_match('/[\<\'\"\(]/', $field)) {
return $this->fieldRaw($field);
}
$field = array_map('trim', explode(',', $field)); $field = array_map('trim', explode(',', $field));
} }
if (true === $field) { if (true === $field) {
@ -800,6 +828,24 @@ class Query
return $this; return $this;
} }
/**
* 表达式方式指定查询字段
* @access public
* @param string $field 字段名
* @param array $bind 参数绑定
* @return $this
*/
public function fieldRaw($field, array $bind = [])
{
$this->options['field'][] = $this->raw($field);
if ($bind) {
$this->bind($bind);
}
return $this;
}
/** /**
* 设置数据 * 设置数据
* @access public * @access public
@ -828,7 +874,7 @@ class Query
{ {
$fields = is_string($field) ? explode(',', $field) : $field; $fields = is_string($field) ? explode(',', $field) : $field;
foreach ($fields as $field) { foreach ($fields as $field) {
$this->data($field, ['inc', $field, $step]); $this->data($field, ['inc', $step]);
} }
return $this; return $this;
} }
@ -844,7 +890,7 @@ class Query
{ {
$fields = is_string($field) ? explode(',', $field) : $field; $fields = is_string($field) ? explode(',', $field) : $field;
foreach ($fields as $field) { foreach ($fields as $field) {
$this->data($field, ['dec', $field, $step]); $this->data($field, ['dec', $step]);
} }
return $this; return $this;
} }
@ -858,16 +904,27 @@ class Query
*/ */
public function exp($field, $value) public function exp($field, $value)
{ {
$this->data($field, ['exp', $value]); $this->data($field, $this->raw($value));
return $this; return $this;
} }
/**
* 使用表达式设置数据
* @access public
* @param mixed $value 表达式
* @return Expression
*/
public function raw($value)
{
return new Expression($value);
}
/** /**
* 指定JOIN查询字段 * 指定JOIN查询字段
* @access public * @access public
* @param string|array $table 数据表 * @param string|array $table 数据表
* @param string|array $field 查询字段 * @param string|array $field 查询字段
* @param string|array $on JOIN条件 * @param mixed $on JOIN条件
* @param string $type JOIN类型 * @param string $type JOIN类型
* @return $this * @return $this
*/ */
@ -975,6 +1032,37 @@ class Query
return $this; return $this;
} }
/**
* 指定表达式查询条件
* @access public
* @param string $where 查询条件
* @param array $bind 参数绑定
* @param string $logic 查询逻辑 and or xor
* @return $this
*/
public function whereRaw($where, $bind = [], $logic = 'AND')
{
$this->options['where'][$logic][] = $this->raw($where);
if ($bind) {
$this->bind($bind);
}
return $this;
}
/**
* 指定表达式查询条件 OR
* @access public
* @param string $where 查询条件
* @param array $bind 参数绑定
* @return $this
*/
public function whereOrRaw($where, $bind = [])
{
return $this->whereRaw($where, $bind, 'OR');
}
/** /**
* 指定Null查询条件 * 指定Null查询条件
* @access public * @access public
@ -1121,7 +1209,7 @@ class Query
*/ */
public function whereExp($field, $condition, $logic = 'AND') public function whereExp($field, $condition, $logic = 'AND')
{ {
$this->parseWhereExp($logic, $field, 'exp', $condition, [], true); $this->parseWhereExp($logic, $field, 'exp', $this->raw($condition), [], true);
return $this; return $this;
} }
@ -1163,14 +1251,16 @@ class Query
$field = $this->options['via'] . '.' . $field; $field = $this->options['via'] . '.' . $field;
} }
if ($strict) { if ($field instanceof Expression) {
return $this->whereRaw($field, is_array($op) ? $op : []);
} elseif ($strict) {
// 使用严格模式查询 // 使用严格模式查询
$where[$field] = [$op, $condition]; $where[$field] = [$op, $condition];
// 记录一个字段多次查询条件 // 记录一个字段多次查询条件
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) { } elseif (is_string($field) && preg_match('/[,=\>\<\'\"\(\s]/', $field)) {
$where[] = ['exp', $field]; $where[] = ['exp', $this->raw($field)];
if (is_array($op)) { if (is_array($op)) {
// 参数绑定 // 参数绑定
$this->bind($op); $this->bind($op);
@ -1191,21 +1281,28 @@ class Query
$where[$field] = $param; $where[$field] = $param;
} elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) { } elseif (in_array(strtolower($op), ['null', 'notnull', 'not null'])) {
// null查询 // null查询
$where[$field] = [$op, '']; $where[$field] = [$op, ''];
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} elseif (is_null($condition)) { } elseif (is_null($condition)) {
// 字段相等查询 // 字段相等查询
$where[$field] = ['eq', $op]; $where[$field] = ['eq', $op];
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} else { } else {
$where[$field] = [$op, $condition, isset($param[2]) ? $param[2] : null]; if ('exp' == strtolower($op)) {
if ('exp' == strtolower($op) && isset($param[2]) && is_array($param[2])) { $where[$field] = ['exp', $this->raw($condition)];
// 参数绑定 // 参数绑定
$this->bind($param[2]); if (isset($param[2]) && is_array($param[2])) {
$this->bind($param[2]);
}
} else {
$where[$field] = [$op, $condition];
} }
// 记录一个字段多次查询条件 // 记录一个字段多次查询条件
$this->options['multi'][$logic][$field][] = $where[$field]; $this->options['multi'][$logic][$field][] = $where[$field];
} }
if (!empty($where)) { if (!empty($where)) {
if (!isset($this->options['where'][$logic])) { if (!isset($this->options['where'][$logic])) {
$this->options['where'][$logic] = []; $this->options['where'][$logic] = [];
@ -1423,31 +1520,59 @@ class Query
*/ */
public function order($field, $order = null) public function order($field, $order = null)
{ {
if (!empty($field)) { if (empty($field)) {
if (is_string($field)) { return $this;
if (!empty($this->options['via'])) { } elseif ($field instanceof Expression) {
$field = $this->options['via'] . '.' . $field; $this->options['order'][] = $field;
} return $this;
$field = empty($order) ? $field : [$field => $order]; }
} elseif (!empty($this->options['via'])) {
foreach ($field as $key => $val) { if (is_string($field)) {
if (is_numeric($key)) { if (!empty($this->options['via'])) {
$field[$key] = $this->options['via'] . '.' . $val; $field = $this->options['via'] . '.' . $field;
} else {
$field[$this->options['via'] . '.' . $key] = $val;
unset($field[$key]);
}
}
} }
if (!isset($this->options['order'])) { if (strpos($field, ',')) {
$this->options['order'] = []; $field = array_map('trim', explode(',', $field));
}
if (is_array($field)) {
$this->options['order'] = array_merge($this->options['order'], $field);
} else { } else {
$this->options['order'][] = $field; $field = empty($order) ? $field : [$field => $order];
}
} elseif (!empty($this->options['via'])) {
foreach ($field as $key => $val) {
if (is_numeric($key)) {
$field[$key] = $this->options['via'] . '.' . $val;
} else {
$field[$this->options['via'] . '.' . $key] = $val;
unset($field[$key]);
}
} }
} }
if (!isset($this->options['order'])) {
$this->options['order'] = [];
}
if (is_array($field)) {
$this->options['order'] = array_merge($this->options['order'], $field);
} else {
$this->options['order'][] = $field;
}
return $this;
}
/**
* 表达式方式指定Field排序
* @access public
* @param string $field 排序字段
* @param array $bind 参数绑定
* @return $this
*/
public function orderRaw($field, array $bind = [])
{
$this->options['order'][] = $this->raw($field);
if ($bind) {
$this->bind($bind);
}
return $this; return $this;
} }
@ -2100,7 +2225,7 @@ class Query
} }
// 执行操作 // 执行操作
$result = 0 === $sql ? 0 : $this->execute($sql, $bind); $result = 0 === $sql ? 0 : $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
$sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null); $sequence = $sequence ?: (isset($options['sequence']) ? $options['sequence'] : null);
$lastInsId = $this->getLastInsID($sequence); $lastInsId = $this->getLastInsID($sequence);
@ -2166,10 +2291,10 @@ class Query
return $this->connection->getRealSql($sql, $bind); return $this->connection->getRealSql($sql, $bind);
} elseif (is_array($sql)) { } elseif (is_array($sql)) {
// 执行操作 // 执行操作
return $this->batchQuery($sql, $bind); return $this->batchQuery($sql, $bind, $this);
} else { } else {
// 执行操作 // 执行操作
return $this->execute($sql, $bind); return $this->execute($sql, $bind, $this);
} }
} }
@ -2195,7 +2320,7 @@ class Query
return $this->connection->getRealSql($sql, $bind); return $this->connection->getRealSql($sql, $bind);
} else { } else {
// 执行操作 // 执行操作
return $this->execute($sql, $bind); return $this->execute($sql, $bind, $this);
} }
} }
@ -2262,7 +2387,7 @@ class Query
Cache::clear($options['cache']['tag']); Cache::clear($options['cache']['tag']);
} }
// 执行操作 // 执行操作
$result = '' == $sql ? 0 : $this->execute($sql, $bind); $result = '' == $sql ? 0 : $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
if (is_string($pk) && isset($where[$pk])) { if (is_string($pk) && isset($where[$pk])) {
$data[$pk] = $where[$pk]; $data[$pk] = $where[$pk];
@ -2436,8 +2561,12 @@ class Query
if (isset($data)) { if (isset($data)) {
return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data; return 'think:' . $prefix . (is_array($options['table']) ? key($options['table']) : $options['table']) . '|' . $data;
} else { }
try {
return md5($prefix . serialize($options) . serialize($bind)); return md5($prefix . serialize($options) . serialize($bind));
} catch (\Exception $e) {
throw new Exception('closure not support cache(true)');
} }
} }
@ -2730,7 +2859,7 @@ class Query
Cache::clear($options['cache']['tag']); Cache::clear($options['cache']['tag']);
} }
// 执行操作 // 执行操作
$result = $this->execute($sql, $bind); $result = $this->execute($sql, $bind, $this);
if ($result) { if ($result) {
if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) { if (!is_array($data) && is_string($pk) && isset($key) && strpos($key, '|')) {
list($a, $val) = explode('|', $key); list($a, $val) = explode('|', $key);
@ -2815,6 +2944,10 @@ class Query
} }
} }
if (isset(static::$readMaster['*']) || (is_string($options['table']) && isset(static::$readMaster[$options['table']]))) {
$options['master'] = true;
}
foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) { foreach (['join', 'union', 'group', 'having', 'limit', 'order', 'force', 'comment'] as $name) {
if (!isset($options[$name])) { if (!isset($options[$name])) {
$options[$name] = ''; $options[$name] = '';

View File

@ -82,17 +82,23 @@ class Mysql extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) { if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持 // JSON字段支持
list($field, $name) = explode('$.', $key); list($field, $name) = explode('$.', $key);
$key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; return 'json_extract(' . $field . ', \'$.' . $name . '\')';
} elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);
if ('__TABLE__' == $table) { if ('__TABLE__' == $table) {
@ -102,7 +108,8 @@ class Mysql extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
$key = '`' . $key . '`'; $key = '`' . $key . '`';
} }
if (isset($table)) { if (isset($table)) {

View File

@ -44,12 +44,18 @@ class Pgsql extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '$.') && false === strpos($key, '(')) { if (strpos($key, '$.') && false === strpos($key, '(')) {
// JSON字段支持 // JSON字段支持

View File

@ -52,12 +52,18 @@ class Sqlite extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '.')) { if (strpos($key, '.')) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);

View File

@ -12,6 +12,7 @@
namespace think\db\builder; namespace think\db\builder;
use think\db\Builder; use think\db\Builder;
use think\db\Expression;
/** /**
* Sqlsrv数据库驱动 * Sqlsrv数据库驱动
@ -34,25 +35,29 @@ class Sqlsrv extends Builder
*/ */
protected function parseOrder($order, $options = []) protected function parseOrder($order, $options = [])
{ {
if (is_array($order)) { if (empty($order)) {
$array = []; return ' ORDER BY rand()';
foreach ($order as $key => $val) {
if (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc']) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options) . ' ' . $sort;
}
}
$order = implode(',', $array);
} }
return !empty($order) ? ' ORDER BY ' . $order : ' ORDER BY rand()';
$array = [];
foreach ($order as $key => $val) {
if ($val instanceof Expression) {
$array[] = $val->getValue();
} elseif (is_numeric($key)) {
if (false === strpos($val, '(')) {
$array[] = $this->parseKey($val, $options);
} elseif ('[rand]' == $val) {
$array[] = $this->parseRand();
} else {
$array[] = $val;
}
} else {
$sort = in_array(strtolower(trim($val)), ['asc', 'desc'], true) ? ' ' . $val : '';
$array[] = $this->parseKey($key, $options, true) . ' ' . $sort;
}
}
return ' ORDER BY ' . implode(',', $array);
} }
/** /**
@ -68,12 +73,17 @@ class Sqlsrv extends Builder
/** /**
* 字段和表名处理 * 字段和表名处理
* @access protected * @access protected
* @param string $key * @param mixed $key
* @param array $options * @param array $options
* @return string * @return string
*/ */
protected function parseKey($key, $options = []) protected function parseKey($key, $options = [], $strict = false)
{ {
if (is_numeric($key)) {
return $key;
} elseif ($key instanceof Expression) {
return $key->getValue();
}
$key = trim($key); $key = trim($key);
if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) {
list($table, $key) = explode('.', $key, 2); list($table, $key) = explode('.', $key, 2);
@ -84,7 +94,7 @@ class Sqlsrv extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
$key = '[' . $key . ']'; $key = '[' . $key . ']';
} }
if (isset($table)) { if (isset($table)) {

View File

@ -12,6 +12,7 @@
namespace think\model\relation; namespace think\model\relation;
use think\Collection; use think\Collection;
use think\Db;
use think\db\Query; use think\db\Query;
use think\Exception; use think\Exception;
use think\Loader; use think\Loader;
@ -338,7 +339,7 @@ class BelongsToMany extends Relation
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
'pivot.' . $this->localKey => [ 'pivot.' . $this->localKey => [
'exp', 'exp',
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(), Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
], ],
])->fetchSql()->count(); ])->fetchSql()->count();
} }

View File

@ -143,7 +143,7 @@ class HasMany extends Relation
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $this->query]); call_user_func_array($closure, [ & $this->query]);
} }
$count = $this->query->where([$this->foreignKey => $result->$localKey])->count(); $count = $this->query->where($this->foreignKey, $result->$localKey)->count();
} }
return $count; return $count;
} }
@ -160,12 +160,7 @@ class HasMany extends Relation
call_user_func_array($closure, [ & $this->query]); call_user_func_array($closure, [ & $this->query]);
} }
$localKey = $this->localKey ?: $this->parent->getPk(); $localKey = $this->localKey ?: $this->parent->getPk();
return $this->query->where([ return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
$this->foreignKey => [
'exp',
'=' . $this->parent->getTable() . '.' . $localKey,
],
])->fetchSql()->count();
} }
/** /**

View File

@ -11,6 +11,7 @@
namespace think\model\relation; namespace think\model\relation;
use think\Db;
use think\db\Query; use think\db\Query;
use think\Exception; use think\Exception;
use think\Loader; use think\Loader;
@ -201,7 +202,7 @@ class MorphMany extends Relation
return $this->query->where([ return $this->query->where([
$this->morphKey => [ $this->morphKey => [
'exp', 'exp',
'=' . $this->parent->getTable() . '.' . $this->parent->getPk(), Db::raw('=' . $this->parent->getTable() . '.' . $this->parent->getPk()),
], ],
$this->morphType => $this->type, $this->morphType => $this->type,
])->fetchSql()->count(); ])->fetchSql()->count();

View File

@ -15,6 +15,8 @@ use think\Exception;
class File class File
{ {
protected $cacheFile;
/** /**
* 写入编译缓存 * 写入编译缓存
* @param string $cacheFile 缓存的文件名 * @param string $cacheFile 缓存的文件名
@ -42,12 +44,13 @@ class File
*/ */
public function read($cacheFile, $vars = []) public function read($cacheFile, $vars = [])
{ {
$this->cacheFile = $cacheFile;
if (!empty($vars) && is_array($vars)) { if (!empty($vars) && is_array($vars)) {
// 模板阵列变量分解成为独立变量 // 模板阵列变量分解成为独立变量
extract($vars, EXTR_OVERWRITE); extract($vars, EXTR_OVERWRITE);
} }
//载入模版缓存文件 //载入模版缓存文件
include $cacheFile; include $this->cacheFile;
} }
/** /**

View File

@ -29,7 +29,11 @@ class Php
'view_suffix' => 'php', 'view_suffix' => 'php',
// 模板文件名分隔符 // 模板文件名分隔符
'view_depr' => DS, 'view_depr' => DS,
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
]; ];
protected $template;
protected $content;
public function __construct($config = []) public function __construct($config = [])
{ {
@ -68,16 +72,12 @@ class Php
if (!is_file($template)) { if (!is_file($template)) {
throw new TemplateNotFoundException('template not exists:' . $template, $template); throw new TemplateNotFoundException('template not exists:' . $template, $template);
} }
$this->template = $template;
// 记录视图信息 // 记录视图信息
App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info'); App::$debug && Log::record('[ VIEW ] ' . $template . ' [ ' . var_export(array_keys($data), true) . ' ]', 'info');
if (isset($data['template'])) {
$__template__ = $template; extract($data, EXTR_OVERWRITE);
extract($data, EXTR_OVERWRITE); include $this->template;
include $__template__;
} else {
extract($data, EXTR_OVERWRITE);
include $template;
}
} }
/** /**
@ -89,14 +89,10 @@ class Php
*/ */
public function display($content, $data = []) public function display($content, $data = [])
{ {
if (isset($data['content'])) { $this->content = $content;
$__content__ = $content;
extract($data, EXTR_OVERWRITE); extract($data, EXTR_OVERWRITE);
eval('?>' . $__content__); eval('?>' . $this->content);
} else {
extract($data, EXTR_OVERWRITE);
eval('?>' . $content);
}
} }
/** /**
@ -132,7 +128,7 @@ class Php
if ($controller) { if ($controller) {
if ('' == $template) { if ('' == $template) {
// 如果模板文件名为空 按照默认规则定位 // 如果模板文件名为空 按照默认规则定位
$template = str_replace('.', DS, $controller) . $depr . $request->action(); $template = str_replace('.', DS, $controller) . $depr . (1 == $this->config['auto_rule'] ? Loader::parseName($request->action(true)) : $request->action());
} elseif (false === strpos($template, $depr)) { } elseif (false === strpos($template, $depr)) {
$template = str_replace('.', DS, $controller) . $depr . $template; $template = str_replace('.', DS, $controller) . $depr . $template;
} }

View File

@ -34,6 +34,8 @@ class Think
'view_depr' => DS, 'view_depr' => DS,
// 是否开启模板编译缓存,设为false则每次都会重新编译 // 是否开启模板编译缓存,设为false则每次都会重新编译
'tpl_cache' => true, 'tpl_cache' => true,
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写
'auto_rule' => 1,
]; ];
public function __construct($config = []) public function __construct($config = [])
@ -127,7 +129,7 @@ class Think
if ($controller) { if ($controller) {
if ('' == $template) { if ('' == $template) {
// 如果模板文件名为空 按照默认规则定位 // 如果模板文件名为空 按照默认规则定位
$template = str_replace('.', DS, $controller) . $depr . $request->action(); $template = str_replace('.', DS, $controller) . $depr . (1 == $this->config['auto_rule'] ? Loader::parseName($request->action(true)) : $request->action());
} elseif (false === strpos($template, $depr)) { } elseif (false === strpos($template, $depr)) {
$template = str_replace('.', DS, $controller) . $depr . $template; $template = str_replace('.', DS, $controller) . $depr . $template;
} }