!20 tp升级到5.0.23

Merge pull request !20 from 聆听/develop
This commit is contained in:
聆听 2018-12-28 15:21:25 +08:00
commit ed0dc89623
23 changed files with 590 additions and 237 deletions

View File

@ -9,7 +9,7 @@
// | Author: liu21st <liu21st@gmail.com> // | Author: liu21st <liu21st@gmail.com>
// +---------------------------------------------------------------------- // +----------------------------------------------------------------------
define('THINK_VERSION', '5.0.21'); define('THINK_VERSION', '5.0.23');
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

@ -551,12 +551,13 @@ class App
// 获取控制器名 // 获取控制器名
$controller = strip_tags($result[1] ?: $config['default_controller']); $controller = strip_tags($result[1] ?: $config['default_controller']);
$controller = $convert ? strtolower($controller) : $controller;
if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) { if (!preg_match('/^[A-Za-z](\w|\.)*$/', $controller)) {
throw new HttpException(404, 'controller not exists:' . $controller); throw new HttpException(404, 'controller not exists:' . $controller);
} }
$controller = $convert ? strtolower($controller) : $controller;
// 获取操作名 // 获取操作名
$actionName = strip_tags($result[2] ?: $config['default_action']); $actionName = strip_tags($result[2] ?: $config['default_action']);
if (!empty($config['action_convert'])) { if (!empty($config['action_convert'])) {

View File

@ -176,7 +176,7 @@ class Log
} }
} }
if ($result = self::$driver->save($log)) { if ($result = self::$driver->save($log, true)) {
self::$log = []; self::$log = [];
} }
@ -211,7 +211,7 @@ class Log
is_null(self::$driver) && self::init(Config::get('log')); is_null(self::$driver) && self::init(Config::get('log'));
// 写入日志 // 写入日志
if ($result = self::$driver->save($log)) { if ($result = self::$driver->save($log, false)) {
self::$log = []; self::$log = [];
} }

View File

@ -94,6 +94,8 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
protected $type = []; protected $type = [];
// 是否为更新数据 // 是否为更新数据
protected $isUpdate = false; protected $isUpdate = false;
// 是否使用Replace
protected $replace = false;
// 是否强制更新所有数据 // 是否强制更新所有数据
protected $force = false; protected $force = false;
// 更新条件 // 更新条件
@ -1013,6 +1015,18 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
return false; return false;
} }
/**
* 新增数据是否使用Replace
* @access public
* @param bool $replace
* @return $this
*/
public function replace($replace = true)
{
$this->replace = $replace;
return $this;
}
/** /**
* 保存当前数据对象 * 保存当前数据对象
* @access public * @access public
@ -1028,20 +1042,20 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
$data = []; $data = [];
} }
if (!empty($data)) {
// 数据自动验证 // 数据自动验证
if (!$this->validateData($data)) { if (!$this->validateData($data)) {
return false; return false;
} }
// 数据对象赋值 // 数据对象赋值
foreach ($data as $key => $value) { foreach ($data as $key => $value) {
$this->setAttr($key, $value, $data); $this->setAttr($key, $value, $data);
} }
if (!empty($where)) { if (!empty($where)) {
$this->isUpdate = true; $this->isUpdate = true;
$this->updateWhere = $where; $this->updateWhere = $where;
} }
}
// 自动关联写入 // 自动关联写入
if (!empty($this->relationWrite)) { if (!empty($this->relationWrite)) {
@ -1163,9 +1177,9 @@ abstract class Model implements \JsonSerializable, \ArrayAccess
// 检测字段 // 检测字段
$allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert)); $allowFields = $this->checkAllowField(array_merge($this->auto, $this->insert));
if (!empty($allowFields)) { if (!empty($allowFields)) {
$result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, false, false, $sequence); $result = $this->getQuery()->strict(false)->field($allowFields)->insert($this->data, $this->replace, false, $sequence);
} else { } else {
$result = $this->getQuery()->insert($this->data, false, false, $sequence); $result = $this->getQuery()->insert($this->data, $this->replace, false, $sequence);
} }
// 获取自动增长主键 // 获取自动增长主键

View File

@ -673,6 +673,7 @@ class Request
{ {
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->route = array_merge($this->route, $name); return $this->route = array_merge($this->route, $name);
} }
return $this->input($this->route, $name, $default, $filter); return $this->input($this->route, $name, $default, $filter);
@ -693,6 +694,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->get = array_merge($this->get, $name); return $this->get = array_merge($this->get, $name);
} }
return $this->input($this->get, $name, $default, $filter); return $this->input($this->get, $name, $default, $filter);
@ -718,6 +720,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->post = array_merge($this->post, $name); return $this->post = array_merge($this->post, $name);
} }
return $this->input($this->post, $name, $default, $filter); return $this->input($this->post, $name, $default, $filter);
@ -743,6 +746,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name); return $this->put = is_null($this->put) ? $name : array_merge($this->put, $name);
} }
@ -789,6 +793,7 @@ class Request
} }
if (is_array($name)) { if (is_array($name)) {
$this->param = []; $this->param = [];
$this->mergeParam = false;
return $this->request = array_merge($this->request, $name); return $this->request = array_merge($this->request, $name);
} }
return $this->input($this->request, $name, $default, $filter); return $this->input($this->request, $name, $default, $filter);

View File

@ -67,6 +67,8 @@ class Validate
'min' => 'min size of :attribute must be :rule', 'min' => 'min size of :attribute must be :rule',
'after' => ':attribute cannot be less than :rule', 'after' => ':attribute cannot be less than :rule',
'before' => ':attribute cannot exceed :rule', 'before' => ':attribute cannot exceed :rule',
'afterWith' => ':attribute cannot be less than :rule',
'beforeWith' => ':attribute cannot exceed :rule',
'expire' => ':attribute not within :rule', 'expire' => ':attribute not within :rule',
'allowIp' => 'access IP is not allowed', 'allowIp' => 'access IP is not allowed',
'denyIp' => 'access IP denied', 'denyIp' => 'access IP denied',
@ -1113,9 +1115,10 @@ class Validate
* @access protected * @access protected
* @param mixed $value 字段值 * @param mixed $value 字段值
* @param mixed $rule 验证规则 * @param mixed $rule 验证规则
* @param array $data 数据
* @return bool * @return bool
*/ */
protected function after($value, $rule) protected function after($value, $rule, $data)
{ {
return strtotime($value) >= strtotime($rule); return strtotime($value) >= strtotime($rule);
} }
@ -1125,13 +1128,42 @@ class Validate
* @access protected * @access protected
* @param mixed $value 字段值 * @param mixed $value 字段值
* @param mixed $rule 验证规则 * @param mixed $rule 验证规则
* @param array $data 数据
* @return bool * @return bool
*/ */
protected function before($value, $rule) protected function before($value, $rule, $data)
{ {
return strtotime($value) <= strtotime($rule); return strtotime($value) <= strtotime($rule);
} }
/**
* 验证日期字段
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function afterWith($value, $rule, $data)
{
$rule = $this->getDataValue($data, $rule);
return !is_null($rule) && strtotime($value) >= strtotime($rule);
}
/**
* 验证日期字段
* @access protected
* @param mixed $value 字段值
* @param mixed $rule 验证规则
* @param array $data 数据
* @return bool
*/
protected function beforeWith($value, $rule, $data)
{
$rule = $this->getDataValue($data, $rule);
return !is_null($rule) && strtotime($value) <= strtotime($rule);
}
/** /**
* 验证有效期 * 验证有效期
* @access protected * @access protected

View File

@ -98,6 +98,10 @@ abstract class Builder
$result = []; $result = [];
foreach ($data as $key => $val) { foreach ($data as $key => $val) {
if ('*' != $options['field'] && !in_array($key, $fields, true)) {
continue;
}
$item = $this->parseKey($key, $options, true); $item = $this->parseKey($key, $options, true);
if ($val instanceof Expression) { if ($val instanceof Expression) {
$result[$item] = $val->getValue(); $result[$item] = $val->getValue();
@ -115,18 +119,10 @@ abstract class Builder
} elseif (is_array($val) && !empty($val)) { } elseif (is_array($val) && !empty($val)) {
switch (strtolower($val[0])) { switch (strtolower($val[0])) {
case 'inc': case 'inc':
// $result[$item] = $item . '+' . floatval($val[1]); $result[$item] = $item . '+' . floatval($val[1]);
if ($key == $val[1]) {
$result[$item] = $this->parseKey($val[1]) . '+' . floatval($val[2]);
}
break; break;
case 'dec': case 'dec':
// $result[$item] = $item . '-' . floatval($val[1]); $result[$item] = $item . '-' . floatval($val[1]);
if ($key == $val[1]) {
$result[$item] = $this->parseKey($val[1]) . '-' . floatval($val[2]);
}
break; break;
case 'exp': case 'exp':
throw new Exception('not support data:[' . $val[0] . ']'); throw new Exception('not support data:[' . $val[0] . ']');

View File

@ -361,14 +361,9 @@ abstract class Connection
// 调试开始 // 调试开始
$this->debug(true); $this->debug(true);
// 释放前次的查询结果
if (!empty($this->PDOStatement)) {
$this->free();
}
// 预处理 // 预处理
if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
}
// 是否为存储过程调用 // 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定 // 参数绑定
@ -429,14 +424,9 @@ abstract class Connection
// 调试开始 // 调试开始
$this->debug(true); $this->debug(true);
//释放前次的查询结果
if (!empty($this->PDOStatement) && $this->PDOStatement->queryString != $sql) {
$this->free();
}
// 预处理 // 预处理
if (empty($this->PDOStatement)) {
$this->PDOStatement = $this->linkID->prepare($sql); $this->PDOStatement = $this->linkID->prepare($sql);
}
// 是否为存储过程调用 // 是否为存储过程调用
$procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']); $procedure = in_array(strtolower(substr(trim($sql), 0, 4)), ['call', 'exec']);
// 参数绑定 // 参数绑定
@ -659,18 +649,15 @@ abstract class Connection
); );
} }
} catch (\PDOException $e) {
if ($this->isBreak($e)) {
return $this->close()->startTrans();
}
throw $e;
} catch (\Exception $e) { } catch (\Exception $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans(); return $this->close()->startTrans();
} }
throw $e; throw $e;
} catch (\Error $e) { } catch (\Error $e) {
if ($this->isBreak($e)) { if ($this->isBreak($e)) {
--$this->transTimes;
return $this->close()->startTrans(); return $this->close()->startTrans();
} }
throw $e; throw $e;
@ -804,6 +791,8 @@ abstract class Connection
$this->linkWrite = null; $this->linkWrite = null;
$this->linkRead = null; $this->linkRead = null;
$this->links = []; $this->links = [];
// 释放查询
$this->free();
return $this; return $this;
} }

View File

@ -92,6 +92,13 @@ class Query
$name = Loader::parseName(substr($method, 10)); $name = Loader::parseName(substr($method, 10));
$where[$name] = $args[0]; $where[$name] = $args[0];
return $this->where($where)->value($args[1]); return $this->where($where)->value($args[1]);
} elseif ($this->model && method_exists($this->model, 'scope' . $method)) {
// 动态调用命名范围
$method = 'scope' . $method;
array_unshift($args, $this);
call_user_func_array([$this->model, $method], $args);
return $this;
} else { } else {
throw new Exception('method not exist:' . __CLASS__ . '->' . $method); throw new Exception('method not exist:' . __CLASS__ . '->' . $method);
} }
@ -436,9 +443,10 @@ class Query
// 返回SQL语句 // 返回SQL语句
return $pdo; return $pdo;
} }
$result = $pdo->fetchColumn(); $result = $pdo->fetchColumn();
if ($force) { if ($force) {
$result += 0; $result = (float) $result;
} }
if (isset($cache) && false !== $result) { if (isset($cache) && false !== $result) {
@ -531,13 +539,43 @@ class Query
public function count($field = '*') public function count($field = '*')
{ {
if (isset($this->options['group'])) { if (isset($this->options['group'])) {
if (!preg_match('/^[\w\.\*]+$/', $field)) {
throw new Exception('not support data:' . $field);
}
// 支持GROUP // 支持GROUP
$options = $this->getOptions(); $options = $this->getOptions();
$subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql(); $subSql = $this->options($options)->field('count(' . $field . ')')->bind($this->bind)->buildSql();
return $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true);
$count = $this->table([$subSql => '_group_count_'])->value('COUNT(*) AS tp_count', 0, true);
} else {
$count = $this->aggregate('COUNT', $field, true);
} }
return $this->value('COUNT(' . $field . ') AS tp_count', 0, true); return is_string($count) ? $count : (int) $count;
}
/**
* 聚合查询
* @access public
* @param string $aggregate 聚合方法
* @param string $field 字段名
* @param bool $force 强制转为数字类型
* @return mixed
*/
public function aggregate($aggregate, $field, $force = false)
{
if (0 === stripos($field, 'DISTINCT ')) {
list($distinct, $field) = explode(' ', $field);
}
if (!preg_match('/^[\w\.\+\-\*]+$/', $field)) {
throw new Exception('not support data:' . $field);
}
$result = $this->value($aggregate . '(' . (!empty($distinct) ? 'DISTINCT ' : '') . $field . ') AS tp_' . strtolower($aggregate), 0, $force);
return $result;
} }
/** /**
@ -548,7 +586,7 @@ class Query
*/ */
public function sum($field) public function sum($field)
{ {
return $this->value('SUM(' . $field . ') AS tp_sum', 0, true); return $this->aggregate('SUM', $field, true);
} }
/** /**
@ -560,7 +598,7 @@ class Query
*/ */
public function min($field, $force = true) public function min($field, $force = true)
{ {
return $this->value('MIN(' . $field . ') AS tp_min', 0, $force); return $this->aggregate('MIN', $field, $force);
} }
/** /**
@ -572,7 +610,7 @@ class Query
*/ */
public function max($field, $force = true) public function max($field, $force = true)
{ {
return $this->value('MAX(' . $field . ') AS tp_max', 0, $force); return $this->aggregate('MAX', $field, $force);
} }
/** /**
@ -583,7 +621,7 @@ class Query
*/ */
public function avg($field) public function avg($field)
{ {
return $this->value('AVG(' . $field . ') AS tp_avg', 0, true); return $this->aggregate('AVG', $field, true);
} }
/** /**
@ -2092,14 +2130,23 @@ class Query
$this->field('*'); $this->field('*');
} }
foreach ($relations as $key => $relation) { foreach ($relations as $key => $relation) {
$closure = false; $closure = $name = null;
if ($relation instanceof \Closure) { if ($relation instanceof \Closure) {
$closure = $relation; $closure = $relation;
$relation = $key; $relation = $key;
} elseif (!is_int($key)) {
$name = $relation;
$relation = $key;
} }
$relation = Loader::parseName($relation, 1, false); $relation = Loader::parseName($relation, 1, false);
$count = '(' . $this->model->$relation()->getRelationCountQuery($closure) . ')';
$this->field([$count => Loader::parseName($relation) . '_count']); $count = '(' . $this->model->$relation()->getRelationCountQuery($closure, $name) . ')';
if (empty($name)) {
$name = Loader::parseName($relation) . '_count';
}
$this->field([$count => $name]);
} }
} }
return $this; return $this;

View File

@ -109,6 +109,9 @@ class Mysql extends Builder
} }
} }
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
throw new Exception('not support data:' . $key);
}
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) { if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key))) {
$key = '`' . $key . '`'; $key = '`' . $key . '`';
} }

View File

@ -94,6 +94,10 @@ class Sqlsrv extends Builder
$table = $options['alias'][$table]; $table = $options['alias'][$table];
} }
} }
if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) {
throw new Exception('not support data:' . $key);
}
if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) { if ('*' != $key && ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key))) {
$key = '[' . $key . ']'; $key = '[' . $key . ']';
} }

View File

@ -50,6 +50,9 @@ class Sqlsrv extends Connection
public function getFields($tableName) public function getFields($tableName)
{ {
list($tableName) = explode(' ', $tableName); list($tableName) = explode(' ', $tableName);
$tableNames = explode('.', $tableName);
$tableName = isset($tableNames[1]) ? $tableNames[1] : $tableNames[0];
$sql = "SELECT column_name, data_type, column_default, is_nullable $sql = "SELECT column_name, data_type, column_default, is_nullable
FROM information_schema.tables AS t FROM information_schema.tables AS t
JOIN information_schema.columns AS c JOIN information_schema.columns AS c

View File

@ -26,10 +26,9 @@ class File
'path' => LOG_PATH, 'path' => LOG_PATH,
'apart_level' => [], 'apart_level' => [],
'max_files' => 0, 'max_files' => 0,
'json' => false,
]; ];
protected $writed = [];
// 实例化并传入参数 // 实例化并传入参数
public function __construct($config = []) public function __construct($config = [])
{ {
@ -42,105 +41,230 @@ class File
* 日志写入接口 * 日志写入接口
* @access public * @access public
* @param array $log 日志信息 * @param array $log 日志信息
* @param bool $append 是否追加请求信息
* @return bool * @return bool
*/ */
public function save(array $log = []) public function save(array $log = [], $append = false)
{
$destination = $this->getMasterLogFile();
$path = dirname($destination);
!is_dir($path) && mkdir($path, 0755, true);
$info = [];
foreach ($log as $type => $val) {
foreach ($val as $msg) {
if (!is_string($msg)) {
$msg = var_export($msg, true);
}
$info[$type][] = $this->config['json'] ? $msg : '[ ' . $type . ' ] ' . $msg;
}
if (!$this->config['json'] && (true === $this->config['apart_level'] || in_array($type, $this->config['apart_level']))) {
// 独立记录的日志级别
$filename = $this->getApartLevelFile($path, $type);
$this->write($info[$type], $filename, true, $append);
unset($info[$type]);
}
}
if ($info) {
return $this->write($info, $destination, false, $append);
}
return true;
}
/**
* 获取主日志文件名
* @access public
* @return string
*/
protected function getMasterLogFile()
{ {
if ($this->config['single']) { if ($this->config['single']) {
$destination = $this->config['path'] . 'single.log'; $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
$destination = $this->config['path'] . $name . '.log';
} else { } else {
$cli = IS_CLI ? '_cli' : ''; $cli = PHP_SAPI == 'cli' ? '_cli' : '';
if ($this->config['max_files']) { if ($this->config['max_files']) {
$filename = date('Ymd') . $cli . '.log'; $filename = date('Ymd') . $cli . '.log';
$files = glob($this->config['path'] . '*.log'); $files = glob($this->config['path'] . '*.log');
try {
if (count($files) > $this->config['max_files']) { if (count($files) > $this->config['max_files']) {
unlink($files[0]); unlink($files[0]);
} }
} catch (\Exception $e) {
}
} else { } else {
$filename = date('Ym') . '/' . date('d') . $cli . '.log'; $filename = date('Ym') . DIRECTORY_SEPARATOR . date('d') . $cli . '.log';
} }
$destination = $this->config['path'] . $filename; $destination = $this->config['path'] . $filename;
} }
$path = dirname($destination); return $destination;
!is_dir($path) && mkdir($path, 0755, true); }
/**
* 获取独立日志文件名
* @access public
* @param string $path 日志目录
* @param string $type 日志类型
* @return string
*/
protected function getApartLevelFile($path, $type)
{
$cli = PHP_SAPI == 'cli' ? '_cli' : '';
$info = '';
foreach ($log as $type => $val) {
$level = '';
foreach ($val as $msg) {
if (!is_string($msg)) {
$msg = var_export($msg, true);
}
$level .= '[ ' . $type . ' ] ' . $msg . "\r\n";
}
if (in_array($type, $this->config['apart_level'])) {
// 独立记录的日志级别
if ($this->config['single']) { if ($this->config['single']) {
$filename = $path . DS . $type . '.log'; $name = is_string($this->config['single']) ? $this->config['single'] : 'single';
$name .= '_' . $type;
} elseif ($this->config['max_files']) { } elseif ($this->config['max_files']) {
$filename = $path . DS . date('Ymd') . '_' . $type . $cli . '.log'; $name = date('Ymd') . '_' . $type . $cli;
} else { } else {
$filename = $path . DS . date('d') . '_' . $type . $cli . '.log'; $name = date('d') . '_' . $type . $cli;
}
$this->write($level, $filename, true);
} else {
$info .= $level;
}
}
if ($info) {
return $this->write($info, $destination);
}
return true;
} }
protected function write($message, $destination, $apart = false) return $path . DIRECTORY_SEPARATOR . $name . '.log';
}
/**
* 日志写入
* @access protected
* @param array $message 日志信息
* @param string $destination 日志文件
* @param bool $apart 是否独立文件写入
* @param bool $append 是否追加请求信息
* @return bool
*/
protected function write($message, $destination, $apart = false, $append = false)
{ {
// 检测日志文件大小,超过配置大小则备份日志文件重新生成 // 检测日志文件大小,超过配置大小则备份日志文件重新生成
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { $this->checkLogSize($destination);
try {
rename($destination, dirname($destination) . DS . time() . '-' . basename($destination)); // 日志信息封装
} catch (\Exception $e) { $info['timestamp'] = date($this->config['time_format']);
}
$this->writed[$destination] = false; foreach ($message as $type => $msg) {
$info[$type] = is_array($msg) ? implode("\r\n", $msg) : $msg;
} }
if (empty($this->writed[$destination]) && !IS_CLI) { if (PHP_SAPI == 'cli') {
if (App::$debug && !$apart) { $message = $this->parseCliLog($info);
// 获取基本信息
if (isset($_SERVER['HTTP_HOST'])) {
$current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
} else { } else {
$current_uri = "cmd:" . implode(' ', $_SERVER['argv']); // 添加调试日志
} $this->getDebugLog($info, $append, $apart);
$runtime = round(microtime(true) - THINK_START_TIME, 10); $message = $this->parseLog($info);
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
$message = '[ info ] ' . $current_uri . $time_str . $memory_str . $file_load . "\r\n" . $message;
}
$now = date($this->config['time_format']);
$ip = Request::instance()->ip();
$method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$message = "---------------------------------------------------------------\r\n[{$now}] {$ip} {$method} {$uri}\r\n" . $message;
$this->writed[$destination] = true;
}
if (IS_CLI) {
$now = date($this->config['time_format']);
$message = "[{$now}]" . $message;
} }
return error_log($message, 3, $destination); return error_log($message, 3, $destination);
} }
/**
* 检查日志文件大小并自动生成备份文件
* @access protected
* @param string $destination 日志文件
* @return void
*/
protected function checkLogSize($destination)
{
if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) {
try {
rename($destination, dirname($destination) . DIRECTORY_SEPARATOR . time() . '-' . basename($destination));
} catch (\Exception $e) {
}
}
}
/**
* CLI日志解析
* @access protected
* @param array $info 日志信息
* @return string
*/
protected function parseCliLog($info)
{
if ($this->config['json']) {
$message = json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
} else {
$now = $info['timestamp'];
unset($info['timestamp']);
$message = implode("\r\n", $info);
$message = "[{$now}]" . $message . "\r\n";
}
return $message;
}
/**
* 解析日志
* @access protected
* @param array $info 日志信息
* @return string
*/
protected function parseLog($info)
{
$request = Request::instance();
$requestInfo = [
'ip' => $request->ip(),
'method' => $request->method(),
'host' => $request->host(),
'uri' => $request->url(),
];
if ($this->config['json']) {
$info = $requestInfo + $info;
return json_encode($info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\r\n";
}
array_unshift($info, "---------------------------------------------------------------\r\n[{$info['timestamp']}] {$requestInfo['ip']} {$requestInfo['method']} {$requestInfo['host']}{$requestInfo['uri']}");
unset($info['timestamp']);
return implode("\r\n", $info) . "\r\n";
}
protected function getDebugLog(&$info, $append, $apart)
{
if (App::$debug && $append) {
if ($this->config['json']) {
// 获取基本信息
$runtime = round(microtime(true) - THINK_START_TIME, 10);
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
$info = [
'runtime' => number_format($runtime, 6) . 's',
'reqs' => $reqs . 'req/s',
'memory' => $memory_use . 'kb',
'file' => count(get_included_files()),
] + $info;
} elseif (!$apart) {
// 增加额外的调试信息
$runtime = round(microtime(true) - THINK_START_TIME, 10);
$reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
$memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
$time_str = '[运行时间:' . number_format($runtime, 6) . 's] [吞吐率:' . $reqs . 'req/s]';
$memory_str = ' [内存消耗:' . $memory_use . 'kb]';
$file_load = ' [文件加载:' . count(get_included_files()) . ']';
array_unshift($info, $time_str . $memory_str . $file_load);
}
}
}
} }

View File

@ -60,7 +60,7 @@ class Socket
* @param array $log 日志信息 * @param array $log 日志信息
* @return bool * @return bool
*/ */
public function save(array $log = []) public function save(array $log = [], $append = false)
{ {
if (!$this->check()) { if (!$this->check()) {
return false; return false;

View File

@ -29,6 +29,8 @@ class BelongsToMany extends Relation
protected $pivotName; protected $pivotName;
// 中间表模型对象 // 中间表模型对象
protected $pivot; protected $pivot;
// 中间表数据名称
protected $pivotDataName = 'pivot';
/** /**
* 构造函数 * 构造函数
@ -70,6 +72,18 @@ class BelongsToMany extends Relation
return $this; return $this;
} }
/**
* 设置中间表数据名称
* @access public
* @param string $name
* @return $this
*/
public function pivotDataName($name)
{
$this->pivotDataName = $name;
return $this;
}
/** /**
* 获取中间表更新条件 * 获取中间表更新条件
* @param $data * @param $data
@ -118,7 +132,7 @@ class BelongsToMany extends Relation
} }
} }
} }
$model->setRelation('pivot', $this->newPivot($pivot, true)); $model->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
} }
} }
@ -346,10 +360,18 @@ class BelongsToMany extends Relation
* 获取关联统计子查询 * 获取关联统计子查询
* @access public * @access public
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string * @return string
*/ */
public function getRelationCountQuery($closure) public function getRelationCountQuery($closure, &$name = null)
{ {
if ($closure) {
$return = call_user_func_array($closure, [ & $this->query]);
if ($return && is_string($return)) {
$name = $return;
}
}
return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [ return $this->belongsToManyQuery($this->foreignKey, $this->localKey, [
'pivot.' . $this->localKey => [ 'pivot.' . $this->localKey => [
'exp', 'exp',
@ -384,7 +406,7 @@ class BelongsToMany extends Relation
} }
} }
} }
$set->setRelation('pivot', $this->newPivot($pivot, true)); $set->setRelation($this->pivotDataName, $this->newPivot($pivot, true));
$data[$pivot[$this->localKey]][] = $set; $data[$pivot[$this->localKey]][] = $set;
} }
return $data; return $data;
@ -499,6 +521,29 @@ class BelongsToMany extends Relation
} }
} }
/**
* 判断是否存在关联数据
* @access public
* @param mixed $data 数据 可以使用关联模型对象 或者 关联对象的主键
* @return Pivot
* @throws Exception
*/
public function attached($data)
{
if ($data instanceof Model) {
$relationFk = $data->getPk();
$id = $data->$relationFk;
} else {
$id = $data;
}
$pk = $this->parent->getPk();
$pivot = $this->pivot->where($this->localKey, $this->parent->$pk)->where($this->foreignKey, $id)->find();
return $pivot ?: false;
}
/** /**
* 解除关联的一个中间表数据 * 解除关联的一个中间表数据
* @access public * @access public

View File

@ -152,12 +152,16 @@ class HasMany extends Relation
* 创建关联统计子查询 * 创建关联统计子查询
* @access public * @access public
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string * @return string
*/ */
public function getRelationCountQuery($closure) public function getRelationCountQuery($closure, &$name = null)
{ {
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $this->query]); $return = call_user_func_array($closure, [ & $this->query]);
if ($return && is_string($return)) {
$name = $return;
}
} }
$localKey = $this->localKey ?: $this->parent->getPk(); $localKey = $this->localKey ?: $this->parent->getPk();
return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count(); return $this->query->whereExp($this->foreignKey, '=' . $this->parent->getTable() . '.' . $localKey)->fetchSql()->count();
@ -197,14 +201,26 @@ class HasMany extends Relation
* @return Model|false * @return Model|false
*/ */
public function save($data) public function save($data)
{
$model = $this->make($data);
return $model->save($data) ? $model : false;
}
/**
* 创建关联对象实例
* @param array $data
* @return Model
*/
public function make($data = [])
{ {
if ($data instanceof Model) { if ($data instanceof Model) {
$data = $data->getData(); $data = $data->getData();
} }
// 保存关联表数据 // 保存关联表数据
$model = new $this->model;
$data[$this->foreignKey] = $this->parent->{$this->localKey}; $data[$this->foreignKey] = $this->parent->{$this->localKey};
return $model->save($data) ? $model : false;
return new $this->model($data);
} }
/** /**

View File

@ -120,6 +120,18 @@ class HasManyThrough extends Relation
public function relationCount($result, $closure) public function relationCount($result, $closure)
{} {}
/**
* 创建关联统计子查询
* @access public
* @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/
public function getRelationCountQuery($closure, &$name = null)
{
throw new Exception('relation not support: withCount');
}
/** /**
* 执行基础查询(进执行一次) * 执行基础查询(进执行一次)
* @access protected * @access protected

View File

@ -188,15 +188,19 @@ class MorphMany extends Relation
} }
/** /**
* 获取关联统计子查询 * 创建关联统计子查询
* @access public * @access public
* @param \Closure $closure 闭包 * @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string * @return string
*/ */
public function getRelationCountQuery($closure) public function getRelationCountQuery($closure, &$name = null)
{ {
if ($closure) { if ($closure) {
call_user_func_array($closure, [ & $this->query]); $return = call_user_func_array($closure, [ & $this->query]);
if ($return && is_string($return)) {
$name = $return;
}
} }
return $this->query->where([ return $this->query->where([
@ -240,17 +244,30 @@ class MorphMany extends Relation
* @return Model|false * @return Model|false
*/ */
public function save($data) public function save($data)
{
$model = $this->make($data);
return $model->save($data) ? $model : false;
}
/**
* 创建关联对象实例
* @param array $data
* @return Model
*/
public function make($data = [])
{ {
if ($data instanceof Model) { if ($data instanceof Model) {
$data = $data->getData(); $data = $data->getData();
} }
// 保存关联表数据 // 保存关联表数据
$pk = $this->parent->getPk(); $pk = $this->parent->getPk();
$model = new $this->model;
$data[$this->morphKey] = $this->parent->$pk; $data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type; $data[$this->morphType] = $this->type;
return $model->save($data) ? $model : false;
return new $this->model($data);
} }
/** /**

View File

@ -198,6 +198,17 @@ class MorphOne extends Relation
* @return Model|false * @return Model|false
*/ */
public function save($data) public function save($data)
{
$model = $this->make($data);
return $model->save() ? $model : false;
}
/**
* 创建关联对象实例
* @param array $data
* @return Model
*/
public function make($data = [])
{ {
if ($data instanceof Model) { if ($data instanceof Model) {
$data = $data->getData(); $data = $data->getData();
@ -205,10 +216,10 @@ class MorphOne extends Relation
// 保存关联表数据 // 保存关联表数据
$pk = $this->parent->getPk(); $pk = $this->parent->getPk();
$model = new $this->model;
$data[$this->morphKey] = $this->parent->$pk; $data[$this->morphKey] = $this->parent->$pk;
$data[$this->morphType] = $this->type; $data[$this->morphType] = $this->type;
return $model->save($data) ? $model : false;
return new $this->model($data);
} }
/** /**
@ -227,4 +238,15 @@ class MorphOne extends Relation
} }
} }
/**
* 创建关联统计子查询
* @access public
* @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/
public function getRelationCountQuery($closure, &$name = null)
{
throw new Exception('relation not support: withCount');
}
} }

View File

@ -285,4 +285,15 @@ class MorphTo extends Relation
return $this->parent->setRelation($this->relation, null); return $this->parent->setRelation($this->relation, null);
} }
/**
* 创建关联统计子查询
* @access public
* @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/
public function getRelationCountQuery($closure, &$name = null)
{
throw new Exception('relation not support: withCount');
}
} }

View File

@ -323,4 +323,15 @@ abstract class OneToOne extends Relation
return $data; return $data;
} }
/**
* 创建关联统计子查询
* @access public
* @param \Closure $closure 闭包
* @param string $name 统计数据别名
* @return string
*/
public function getRelationCountQuery($closure, &$name = null)
{
throw new Exception('relation not support: withCount');
}
} }

View File

@ -2,6 +2,7 @@
namespace traits\model; namespace traits\model;
use think\Collection;
use think\db\Query; use think\db\Query;
use think\Model; use think\Model;
@ -111,7 +112,7 @@ trait SoftDelete
} }
// 包含软删除数据 // 包含软删除数据
$query = self::withTrashed(); $query = (new static())->db(false);
if (is_array($data) && key($data) !== 0) { if (is_array($data) && key($data) !== 0) {
$query->where($data); $query->where($data);
$data = null; $data = null;

View File

@ -894,17 +894,17 @@
}, },
{ {
"name": "topthink/framework", "name": "topthink/framework",
"version": "v5.0.21", "version": "v5.0.23",
"version_normalized": "5.0.21.0", "version_normalized": "5.0.23.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/top-think/framework.git", "url": "https://github.com/top-think/framework.git",
"reference": "ab826da071a7a47116a7f1d01f72228d6bcf212a" "reference": "4cbc0b5e93314446243ebc7d5f005f9c32864737"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/top-think/framework/zipball/ab826da071a7a47116a7f1d01f72228d6bcf212a", "url": "https://api.github.com/repos/top-think/framework/zipball/4cbc0b5e93314446243ebc7d5f005f9c32864737",
"reference": "ab826da071a7a47116a7f1d01f72228d6bcf212a", "reference": "4cbc0b5e93314446243ebc7d5f005f9c32864737",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -919,7 +919,7 @@
"phpunit/phpunit": "4.8.*", "phpunit/phpunit": "4.8.*",
"sebastian/phpcpd": "2.*" "sebastian/phpcpd": "2.*"
}, },
"time": "2018-09-04T09:18:48+00:00", "time": "2018-12-09T12:40:40+00:00",
"type": "think-framework", "type": "think-framework",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {