From 8ae0fdef465e6c6977380bc79607bd20254c8eaa Mon Sep 17 00:00:00 2001 From: thinkphp Date: Thu, 12 Apr 2018 09:25:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Expression=E7=B1=BB=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- library/think/Request.php | 2 +- library/think/db/Builder.php | 31 +++++++---- library/think/db/Expression.php | 48 +++++++++++++++++ library/think/db/Query.php | 82 ++++++++++++++++++++++++++++- library/think/db/builder/Mysql.php | 9 ++-- library/think/db/builder/Pgsql.php | 2 +- library/think/db/builder/Sqlite.php | 2 +- library/think/db/builder/Sqlsrv.php | 12 +++-- 8 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 library/think/db/Expression.php diff --git a/library/think/Request.php b/library/think/Request.php index ea18fdc8..5710c5cb 100644 --- a/library/think/Request.php +++ b/library/think/Request.php @@ -1093,7 +1093,7 @@ class Request 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 .= ' '; } // TODO 其他安全过滤 diff --git a/library/think/db/Builder.php b/library/think/db/Builder.php index d2297a4c..fcbb69bc 100644 --- a/library/think/db/Builder.php +++ b/library/think/db/Builder.php @@ -99,8 +99,11 @@ abstract class Builder $result = []; foreach ($data as $key => $val) { - $item = $this->parseKey($key, $options); - if (is_object($val) && method_exists($val, '__toString')) { + $item = $this->parseKey($key, $options, true); + if ($val instanceof Expression) { + $result[$item] = $val->getValue(); + continue; + } elseif (is_object($val) && method_exists($val, '__toString')) { // 对象数据写入 $val = $val->__toString(); } @@ -143,7 +146,7 @@ abstract class Builder * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { return $key; } @@ -184,10 +187,12 @@ abstract class Builder // 支持 'field1'=>'field2' 这样的字段别名定义 $array = []; foreach ($fields as $key => $field) { - if (!is_numeric($key)) { - $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options); + if ($field instanceof Expression) { + $array[] = $field->getValue(); + } elseif (!is_numeric($key)) { + $array[] = $this->parseKey($key, $options) . ' AS ' . $this->parseKey($field, $options, true); } else { - $array[] = $this->parseKey($field, $options); + $array[] = $this->parseKey($field, $options, true); } } $fieldsStr = implode(',', $array); @@ -264,6 +269,10 @@ abstract class Builder foreach ($where as $key => $val) { $str = []; foreach ($val as $field => $value) { + if ($value instanceof Expression) { + $str[] = ' ' . $logic . ' ( ' . $value->getValue() . ' )'; + continue; + } if ($value instanceof \Closure) { // 使用闭包查询 $query = new Query($this->connection); @@ -305,7 +314,7 @@ abstract class Builder 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)) { @@ -344,7 +353,9 @@ abstract class Builder $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(); } @@ -556,7 +567,9 @@ abstract class Builder if (is_array($order)) { $array = []; foreach ($order as $key => $val) { - if (is_numeric($key)) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif (is_numeric($key)) { if ('[rand]' == $val) { if (method_exists($this, 'parseRand')) { $array[] = $this->parseRand(); diff --git a/library/think/db/Expression.php b/library/think/db/Expression.php new file mode 100644 index 00000000..f1b92abd --- /dev/null +++ b/library/think/db/Expression.php @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- + +namespace think\db; + +class Expression +{ + /** + * 查询表达式 + * + * @var string + */ + protected $value; + + /** + * 创建一个查询表达式 + * + * @param string $value + * @return void + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * 获取表达式 + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return (string) $this->value; + } +} diff --git a/library/think/db/Query.php b/library/think/db/Query.php index 478e1f3c..018430fa 100644 --- a/library/think/db/Query.php +++ b/library/think/db/Query.php @@ -800,6 +800,24 @@ class Query 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 @@ -862,6 +880,17 @@ class Query return $this; } + /** + * 使用表达式设置数据 + * @access public + * @param mixed $value 表达式 + * @return Expression + */ + public function raw($value) + { + return new Expression($value); + } + /** * 指定JOIN查询字段 * @access public @@ -975,6 +1004,37 @@ class Query 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查询条件 * @access public @@ -1163,7 +1223,9 @@ class Query $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]; @@ -1451,6 +1513,24 @@ class Query 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; + } + /** * 查询缓存 * @access public diff --git a/library/think/db/builder/Mysql.php b/library/think/db/builder/Mysql.php index 30fa506c..465e9485 100644 --- a/library/think/db/builder/Mysql.php +++ b/library/think/db/builder/Mysql.php @@ -86,13 +86,16 @@ class Mysql extends Builder * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { + if (is_int($key)) { + return $key; + } $key = trim($key); if (strpos($key, '$.') && false === strpos($key, '(')) { // JSON字段支持 list($field, $name) = explode('$.', $key); - $key = 'json_extract(' . $field . ', \'$.' . $name . '\')'; + return 'json_extract(' . $field . ', \'$.' . $name . '\')'; } elseif (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) { list($table, $key) = explode('.', $key, 2); if ('__TABLE__' == $table) { @@ -102,7 +105,7 @@ class Mysql extends Builder $table = $options['alias'][$table]; } } - if (!preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { + if ($strict || !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) { $key = '`' . $key . '`'; } if (isset($table)) { diff --git a/library/think/db/builder/Pgsql.php b/library/think/db/builder/Pgsql.php index 21de525c..5885e721 100644 --- a/library/think/db/builder/Pgsql.php +++ b/library/think/db/builder/Pgsql.php @@ -48,7 +48,7 @@ class Pgsql extends Builder * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { $key = trim($key); if (strpos($key, '$.') && false === strpos($key, '(')) { diff --git a/library/think/db/builder/Sqlite.php b/library/think/db/builder/Sqlite.php index 960533b7..fbd3bbaf 100644 --- a/library/think/db/builder/Sqlite.php +++ b/library/think/db/builder/Sqlite.php @@ -56,7 +56,7 @@ class Sqlite extends Builder * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { $key = trim($key); if (strpos($key, '.')) { diff --git a/library/think/db/builder/Sqlsrv.php b/library/think/db/builder/Sqlsrv.php index 59ea021f..2690900a 100644 --- a/library/think/db/builder/Sqlsrv.php +++ b/library/think/db/builder/Sqlsrv.php @@ -12,6 +12,7 @@ namespace think\db\builder; use think\db\Builder; +use think\db\Expression; /** * Sqlsrv数据库驱动 @@ -37,7 +38,9 @@ class Sqlsrv extends Builder if (is_array($order)) { $array = []; foreach ($order as $key => $val) { - if (is_numeric($key)) { + if ($val instanceof Expression) { + $array[] = $val->getValue(); + } elseif (is_numeric($key)) { if (false === strpos($val, '(')) { $array[] = $this->parseKey($val, $options); } elseif ('[rand]' == $val) { @@ -72,8 +75,11 @@ class Sqlsrv extends Builder * @param array $options * @return string */ - protected function parseKey($key, $options = []) + protected function parseKey($key, $options = [], $strict = false) { + if (is_int($key)) { + return $key; + } $key = trim($key); if (strpos($key, '.') && !preg_match('/[,\'\"\(\)\[\s]/', $key)) { list($table, $key) = explode('.', $key, 2); @@ -84,7 +90,7 @@ class Sqlsrv extends Builder $table = $options['alias'][$table]; } } - if (!is_numeric($key) && !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { + if ($strict || !preg_match('/[,\'\"\*\(\)\[.\s]/', $key)) { $key = '[' . $key . ']'; } if (isset($table)) {