db->fields("*")->join(array("leftJoin", array('table' => 'ad_site', 'alias' => 'b', 'on' => 'a.uid = b.uid')))->where(array('a.create_at:greater' => 1))->limit(10)->groupBy(array('a.uid', 'a.create_at'))->orderBy(array('a.uid' => 'desc'))->selectAll('user a'); * $this->db->where(array('uid' => 1))->set(array('create_at:plus' => time(), 'status' => 1))->update('user'); * $this->db->updateObject('user', array('create_at:plus' => time(), 'status' => 1), array('uid' => 1)); * $this->db->where(array('uid:greater' => 1))->delete('user'); * $this->db->deleteObject('user', array('uid' => 1)); * $this->db->where(array('uid:greater' => 1))->like("email like '%test@test.com%'")->selectAll('user'); * $this->db->where(array('uid:greater' => 1))->where("or")->like(array('email' => 'test@testcom'))->selectAll('user'); * $this->db->where(array('uid:greater' => 1, 'or', 'email:like' => 'test@test.com'))->selectAll('user'); * $this->db->where(array('uid:greater' => 1))->where("OR")->like(array('email' => 'test@test.com'))->selectAll('user'); * $this->db->where(array('name' => 'antsnet'))->exclude(array('email' => 'test@test.com', 'status' => 1))->selectAll('user'); * $this->db->where(array('name' => 'antsnet'))->orTerms(array('email' => 'test@test.com', 'status' => 1))->selectAll('user'); * $this->db->orTerms(array('email' => 'test@test.com', 'status' => 1))->selectAll('user'); * $this->db->join(array("leftJoin", array("table" => 'table', 'alias' => 'a', 'on' => 'a.id=b.id'))); * $this->db->join(" LEFT JOIN table c on c.id=a.id")->selectAll('use a'); * $this->db->where(array('email:unequal' => 'test@test.com'))->in(array('uid:in' => array('1,2,3'), 'status' => array(1)))->selectAll('user'); * $this->>db->where(array('uid:in' => '1,2,3', 'email' => 'test@tset.com', 'status' => 1))->selectAll('user'); * $this->db->where(array(array('email:unequal' => 'test@test.com'), array('uid:in' => array('1,2,3'), 'status:in' => array(1))))->selectAll('user'); * } * } */ class Base { const VERSION = '1.3'; use TraitDatabase; use TraitCache; //query 方法 protected $_query = array( "INSERT" => "INSERT INTO %s(%s) VALUES('%s')", "REPLACE" => "REPLACE %s (%s) VALUES('%s')", "SELECT" => "SELECT %s FROM %s %s", "UPDATE" => "UPDATE %s SET ", "DELETE" => "DELETE FROM %s %s", "WHERE" => " WHERE %s", "OR" => " `%s` = '%s' ", "ORDER" => " ORDER BY %s %s", "GROUP" => " GROUP BY %s", "LIMIT" => " LIMIT %d, %d" ); //load类 public $load; //cache类 public $cache; //语言包 public $language; //响应内容 public $response; //执行的sql语句 public $modelSQL = ""; /** * @var string sql 引用 */ public $executeSQL = ''; public $operateVal = array('or', 'and', 'like');//连接条件操作; public $shortExpression = array( 'equal' => "%s`%s` = '%s'", 'in' => "%s`%s` in (%s)", 'unequal' => "%s`%s` != '%s'", 'greater' => "%s`%s` > '%s'", 'gt' => "%s`%s` > '%s'", 'greaterEqual' => "%s`%s` >= '%s'", 'gte' => "%s`%s` >= '%s'", 'less' => "%s`%s` < '%s'", 'lessEqual' => "%s`%s` <= '%s'", 'lte' => "%s`%s` <= '%s'", 'like' => "%s`%s` like '%%%s%%'" ); public $operateTable = array('leftJoin', 'rightJoin', 'innerJoin');//链接表的操作 public $faultTolerant = true; public $whereCondition = array(); public $joinCondition = array(); public $fields = "*"; public $sets = array(); protected $groupBy; protected $limit; protected $orderBy; protected $_modelAlias = array('updateRows' => 'update', 'selectRows' => 'selectAll', 'select' => 'selectRow', 'getOne' => 'selectOne', 'getRow' => 'selectRow', 'getAll' => 'selectAll', 'remove' => 'delete', 'deleteRows' => 'delete'); public function __construct() { $this->language = Psr4::getInstance()->loadClass('Qii\Language\Loader'); $this->load = Psr4::getInstance()->loadClass('\Qii\Autoloader\Loader'); $this->response = new Response(); } /** * 获取所有数据 * * @param $table * @param null $where * @return mixed * @throws \Exception */ public function selectAll($table, $where = null) { if(is_array($where) && count($where) > 0) { $this->where($where); } $sql = $this->createSelectSQL($table); return $this->getAll($sql); } /** * 返回resource资源 * @param string $table 数据表名称 */ final function rs($table) { $sql = $this->createSelectSQL($table); return $this->setQuery($sql); } /** * 查询一行 * @param $table * @param null $where * @return array | bool * @throws \Exception */ public function selectRow($table, $where = null) { if(is_array($where) && count($where) > 0) { $this->where($where); } $sql = $this->createSelectSQL($table); return $this->getRow($sql); } /** * get row 子类去覆写 * * @param $sql * @return false */ public function getRow($sql) { return false; } /** * * 查询一列 * @param string $table 表名 * @return false | array * @throws TableException */ final function selectOne($table) { //验证table是否合法 if (!$this->verifyTable($table)) { if (gettype($table) == 'string') { throw new TableException("表名不能包含怪字符且不能以数字开头,获取到的是". $table); } throw new TableException("表名必须是字符串加下划线,目标字符为". gettype($table)); } $sql = $this->createSelectSQL($table); return $this->getOne($sql); } /** * get one 方法, 子类去覆写 * @param string $sql * @return false | array */ public function getOne($sql) { return false; } /** * 格式化插入值,新增null值插入 * * @param $arr * @return array|mixed */ protected function formatInsertObject($arr) { if(is_array($arr)) { $values = array(); foreach ($arr as $val) { if(strtoupper(gettype($val)) == 'NULL') { $values[] = 'null'; continue; } $values[] = "'". $val . "'"; } return $values; } return $arr; } /** * 插入数据 * @param $table * @param $dataArray * @return int * @throws InvalidFormat */ final function insertObject($table, $dataArray) { if (empty($table)) { throw new InvalidParams(_i('%s is invalid', 'table'), __LINE__); } $replaceObj = $this->createInsertReplaceObj($dataArray); if(empty($replaceObj['fields']) || empty($replaceObj['values'])) { throw new Variable(_i('Invalid %s format', 'data'), __LINE__); } // 针对 null 值单独处理,并将值的两端加上单引号 ' $values = $this->formatInsertObject($replaceObj['values']); $this->executeSQL = $this->modelSQL = $sql = "INSERT INTO " . $this->getTable($table) . "(`" . join("`, `", $replaceObj['fields']) . "`) VALUES(". join(',', $values) . ")"; $this->setQuery($sql); $this->setError(); return $this->lastInsertId(); } /** * * Replace Object * @param String $table * @param Array|Object $dataArray */ final function replaceObject($table, $dataArray) { if (empty($table)) { throw new InvalidParams(_i('%s is invalid', 'table'), __LINE__); } $replaceObj = $this->createInsertReplaceObj($dataArray); if(empty($replaceObj['fields']) || empty($replaceObj['values'])) { throw new Variable(_i('Invalid %s format', 'data'), __LINE__); } // 针对 null 值单独处理,并将值的两端加上单引号 ' $values = $this->formatInsertObject($replaceObj['values']); $this->executeSQL = $this->modelSQL = $sql = "REPLACE INTO " . $this->getTable($table) . "(`" . join("`, `", $replaceObj['fields']) . "`) VALUES(". join(',', $values) . ")"; $rs = $this->setQuery($sql); $this->setError(); return $this->AffectedRows($rs); } /** * 创建插入或替换的内容 * * @param $dataArray * @return array * @throws InvalidFormat */ protected function createInsertReplaceObj($dataArray) { if(gettype($dataArray) == 'object') { $dataArray = get_object_vars($dataArray); } if (sizeof($dataArray) > 0) { $keys = array(); $values = array(); foreach ($dataArray AS $key => $value) { $keys[] = $key; if (is_array($value)) { throw new InvalidFormat(_i('Invalid %s format', $key), __LINE__); } if(strtoupper(gettype($value)) == 'NULL') { $values[] = null; }else{ $values[] = $this->setQuote($value); } } return array('fields' => $keys, 'values' => $values); } return array('fields' => array(), 'values' => array()); } /** * 更新表中指定数据 结合set、where等方法是用 * * @param string $table * @return string * @throws \Exception */ public function update($table) { if(!$table) { throw new InvalidParams(_i('%s is invalid', '表名'), __LINE__); } $set = join(",", $this->sets); $this->sets = array(); if(count($this->whereCondition) > 0 && $this->isOperator($this->whereCondition[0])) { array_shift($this->whereCondition); } $where = count($this->whereCondition) > 0 ? " WHERE ". join(" ", $this->whereCondition) : ""; $this->whereCondition = array(); $alias = $this->getTableAlias($table); $this->executeSQL = $this->modelSQL = $sql = sprintf($this->_query['UPDATE'], $alias['name'] . $alias['alias']) . $set. $where; return $this->exec($sql); } /** * 更新表的数据,同时可以使用update方法达到同样的效果 * * @param $table * @param $dataArray * @param array $where [key => $val] * @return string * @throws \Exception */ final function updateObject($table, $dataArray, $where = array()){ if(!$table) { throw new InvalidParams(_i('%s is invalid', '表名'), __LINE__); } return $this->set($dataArray)->where($where)->update($table); } /** * 删除表中的数据,结合where方法使用 * * @param string $table 表名 * @return string * @throws \Exception */ final function delete($table) { if(!$table) { throw new \Exception('未指定更新的表名', __LINE__); } $where = count($this->whereCondition) > 0 ? " WHERE ". join(" ", $this->whereCondition) : ""; $this->whereCondition = array(); $alias = $this->getTableAlias($table); $this->executeSQL = $this->modelSQL = $sql = sprintf($this->_query['DELETE'], $alias['name'], $where); return $this->exec($sql); } /** * 删除表中数据 * * @param string $table 表名 * @param null $where * @return string * @throws \Exception */ final function deleteObject($table, $where = null) { if(!$table) { throw new InvalidParams(_i('%s is invalid', '表名'), __LINE__); } return $this->where($where)->delete($table); } /** * like语句 * @param mix $arr * @return $this */ final function like($arr) { if(!is_array($arr)) { if(!empty($arr)) { $this->where($arr); } return $this; } foreach($arr as $key => $val) { $arr[$key .":like"] = $val; unset($arr[$key]); } $this->where($arr); return $this; } /** * * Limit函数,如果省略第二个参数则第一个为0,第二个参数值取第一个 * @param int $limit * @param int $offset */ final function limit($limit, $offset = null) { $this->limit = null; if($limit === '' || $limit === null) { throw new InvalidParams(_i('%s is invalid', 'Limit'), __LINE__); } if ($limit !== '') { if ($offset === null) { $this->limit = sprintf($this->_query["LIMIT"], 0, $limit); } else { $this->limit = sprintf($this->_query["LIMIT"], $limit, $offset); } } return $this; } /** * sql中的set条件 * * @param array $set * @param null $res * @param string $defaultOperator * @return $this */ final public function set($set, &$res = null, $defaultOperator = 'equal') { if(!is_array($set)) { if(!empty($set)) { $this->sets[] = $set; } return $this; } if(count($set) == 0) { return $this; } $operator = array("equal" => "%s`%s` = '%s'", "unequal" => "%s`%s` != '%s'", "plus" => "%s`%s` = %s`%s`+%s", "minus" => "%s`%s` = %s`%s`-%s", "multiply" => "%s`%s` = %s`%s`*%s", "divide" => "%s`%s` = %s`%s`/%s"); foreach($set as $key => $val) { if(strtoupper(gettype($val)) != 'NULL') { $val = $this->setQuote($val); } $alias = $this->getFieldAlias($key); $operate = $this->getFieldOperator($alias['name']); if($operate['operator'] == '') { $operate['operator'] = $defaultOperator; } $opt = $operator[$operate['operator']]; if(strtoupper(gettype($val)) == 'NULL') { $val = 'NULL'; $opt = str_replace("'", '', $opt); } if($operate['operator'] == 'equal') { $this->sets[] = sprintf($opt, $alias['alias'], $operate['field'], $val); }else{ $this->sets[] = sprintf($opt, $alias['alias'], $operate['field'], $alias['alias'], $operate['field'], $val); } } return $this; } /** * set 字段加 * @param $set * @return $this */ public function upsetCounter($set) { if(!is_array($set)) { if(!empty($set)) { $this->sets[] = $set; } return $this; } if(count($set) == 0) { return $this; } foreach ($set as $key => $val) { if(substr($key, -5) == ':plus') { continue; } $set[$key . ":plus"] = $val; unset($set[$key]); } return $this->set($set); } /** * set 字段减 * @param $set * @return $this */ public function downsetCounter($set) { if(!is_array($set)) { if(!empty($set)) { $this->sets[] = $set; } return $this; } if(count($set) == 0) { return $this; } foreach ($set as $key => $val) { if(substr($key, -6) == ':minus') { continue; } $set[$key . ":minus"] = $val; unset($set[$key]); } return $this->set($set); } /** * 处理where条件 * * @param $where * @return $this|array * @throws \Exception */ public function handleWhereFields($where) { if(!is_array($where)) { return $this; } $fields = []; foreach ($where as $key => $val) { $arr = explode(':', $key); $len = count($arr); $maxLen = 1; $opt = ''; if(!$this->isExpression($arr[$len - 1])) { $maxLen = 0; }else{ $opt = ':' .$arr[$len - 1]; } if($len > $maxLen) { $arr1 = array_slice($arr, 0, count($arr) - $maxLen); foreach ($arr1 as $key) { $fields[$key . $opt] = $val; } } } return $fields; } /** * where条件 * @param array $where where语句 * @param null $res * @return $this */ public function where($where, &$res = null) { if(!is_array($where)) { if(!empty($where)) { if(!empty($this->whereCondition) && !$this->isOperator($where)) { $this->whereCondition[] = "and"; } if($this->isOperator($where)) { $this->whereCondition[] = $where; }else{ $this->whereCondition[] = "(". $where . ")"; } } return $this; } if(count($where) == 0) { return $this; } $where = $this->handleWhereFields($where); $slices = $this->groupContents($where); $this->handleCondition($slices, $res); return $this; } /** * OR 条件 * 用法: * 1: * $this->orTerms(['field1:like|equal....' => val1])->orTerms(....) * $this->orTerms(['field1:field2...:like|equal....' => val1]) * == (field1 like|equal.... val1) or (field2 like|equal.... val1) .... * 2: * $this->orTerm(['field1:like|equal...' => val1, 'field2:like|equal...' => val1]) * == (field1 like|equal.... val1 AND field2 like|equal.... val1 ....) * @param $where * @param string $defaultOperater * @param null $res * @return $this * @throws \Exception */ public function orTerms($where, $defaultOperater = 'or', &$res = null) { if(!is_array($where)) { if($where != '') { if(!empty($this->whereCondition) && !$this->isOperator($where)) { $this->whereCondition[] = $defaultOperater; } $this->whereCondition[] = "(". $where . ")"; } return $this; } if(count($where) == 0) { return $this; } $extra = []; foreach ($where as $key => $val) { $fields = explode(':', $key); $len = count($fields); $min = 2; $opt = ''; if($this->isExpression($fields[$len - 1])) { $min = 3; $opt = ':'. $fields[$len - 1]; } if($len >= $min) { $arr = array_slice($fields, 0, ($opt != '' ? $len - 1 : $len)); foreach ($arr as $field) { $extra[$field . $opt] = $val; } unset($where[$key]); } } if(count($where) > 0) { $this->handleCondition(array(array($defaultOperater, $where)), $res); } if(count($extra) > 0) { foreach ($extra as $key => $val) { $this->handleCondition(array(array($defaultOperater, [$key => $val])), $res); } } return $this; } /** * in 条件 * @param $where * @param $defaultOperater * @param $res * @return $this * @throws \Exception */ public function in($where, $defaultOperater = 'or', &$res = null) { if(!is_array($where)) { if($where != '') { if(!empty($this->whereCondition) && !$this->isOperator($where)) { $this->whereCondition[] = $defaultOperater; } $this->whereCondition[] = "(". $where . ")"; } return $this; } if(count($where) == 0) { return $this; } foreach ($where as $key => $val) { if(substr($key, - 3) == ':in') { continue; } $where[$key . ":in"] = $val; unset($where[$key]); } $where = $this->handleWhereFields($where); $this->handleCondition(array(array('and', $where)), $res); return $this; } /** * 不包含 * * @param mix $where 条件 * @return $this */ public function exclude($where) { if(!is_array($where)) { return $this->where($where); } if(count($where) == 0) { return $this; } foreach($where as $key => $value) { if($this->getFieldOperator($key)['operator'] == '') { $where[$key . ":unequal"] = $value; unset($where[$key]); } } return $this->where($where); } /** * join语句 * * @param mix $val * @param reference $res 用于返回值 * @return $this * @throws \Exception */ public function join($val, &$res = null, $joinType = 'leftJoin') { if(!is_array($val)) { if(!empty($val)) { $this->joinCondition[] = $val; } return $this; } if(count($val) == 0) { return $this; } $slices = $this->groupContents($val); $this->handleTableJoin($slices, $joinType, $res); return $this; } /** * left join * @param $table * @param $on * @return $this * @throws \Exception */ final function leftJoinObject($table, $on) { $alias = $this->getTableAlias($table); return $this->join(" LEFT JOIN ". $this->getTable($alias['name']) . " " . $alias['alias'] ." ON ". $on); } /** * GROUP BY * @param mix $group * @return $this */ public function groupBy($group) { if(!is_array($group)) { if(!empty($group)) { $this->groupBy = stristr($group, 'GROUP BY') === false? "GROUP BY ". $group: $group; } return $this; } if(count($group) == 0) { return $this; } foreach($group as $index => $val) { $alias = $this->getFieldAlias($val); $group[$index] = $alias['alias'] . "`". $alias['name'] ."`"; } $this->groupBy = " GROUP BY ". join(",", $group); return $this; } /** * Order by * * @param array|string $order * @return $this * @throws \Exception */ public function orderBy($order) { if(!is_array($order)) { if(!empty($order)) { $this->orderBy = stristr($order, 'ORDER BY') === false ? "ORDER BY ". $order: $order; } return $this; } if(count($order) == 0) { return $this; } $allowSortOff = array('asc', 'desc'); $orderBy = array(); $i = 0; $countOrder = count($order); foreach($order as $key => $sort) { if(!in_array(strtolower($sort), $allowSortOff) && $countOrder == $i) { throw new InvalidParams(_i('%s is invalid', 'sort'), __LINE__); } $alias = $this->getFieldAlias($key); $orderBy[] = $alias['alias'] . "`". $alias['name'] ."` ". $sort; $i++; } $this->orderBy = " ORDER BY ". join(",", $orderBy); return $this; } /** * Order by sql * * @param string $orderBy * @return $this * @throws \Exception */ public function orderByStr($orderBy) { return $this->orderBy($orderBy); } /** * 过滤值 * * @param string|array $word * @return array|string */ final function setQuote($word) { if (ini_get("magic_quotes_gpc")) { return $word; } if(in_array(gettype($word), array("object", "resource","resource (closed)"))) { throw new \Exception('期待参数为数组或字符串,获取到的是:'. gettype($word)."(". json_encode($word) .")"); } return is_array($word) ? array_map('addslashes', $word) : addslashes($word); } /** * * 查询的字段 * @param string|array $fields * @return $this */ final function fields($fields = "*", $append = false) { if (empty($fields) && !$append) $fields = "*"; if (is_array($fields)) { if(count($fields) == 0) { $fields = $append ? '' : '*'; }else{ foreach($fields as $key => $val) { $alias = explode('.', $val); if(count($alias) > 1) { if(strpos("*", $alias[1]) !== false) { $fields[$key] = $val; continue; } $fields[$key] = $alias[0] . ".`".join(".", array_slice($alias, 1))."`"; } } $fields = join(',', $fields); } } if($append) { if($fields != "") $this->fields .= ','. $fields; }else{ $this->fields = $fields; } return $this; } /** * 清空Data数据 */ final public function cleanCondition() { $this->fields = '*'; $this->whereCondition = array(); $this->joinCondition = array(); $this->groupBy = ""; $this->orderBy = ""; $this->limit = null; $this->sets = array(); } /** * 获取错误码 */ final public function getCode() { return $this->response->getCode(); } /** * 获取错误信息 */ final public function getMessage() { if ($this->response->isError()) { return $this->response->getMessage(); } } /** * 返回response对象 * * @return Response */ public function getResponse() { return $this->response; } /** * 转换字符创 * * @param $str * @return array|string */ final public function iconv($str) { if (is_array($str)) { return array_map(function ($n) { return toUtf8($n); }, $str); } return toUtf8($str); } /** * 执行Model过程中保存的相关信息 * * @param string $option * @return mixed */ final function querySQL($option = '') { $allow = array('_queryTimes', '_querySeconds', '_errorInfo', '_exeSQL'); if (in_array($option, $allow)) { return $this->{$option}; } return 0; } /** * 获取执行的sql * * @param string $sql * @return $this */ public function fetchSql(&$sql) { $this->executeSQL = &$sql; return $this; } /** * 通过where\set\limit等方法自动生成SQL语句 * * @param string $table 必填 * @return string * @throws \Exception */ public function createSelectSQL($table) { if(!$table) { throw new InvalidParams(_i('%s is invalid', '表名'), __LINE__); } $aliases = $this->getTableAlias($table); $fields = (trim($this->fields) != '') ? $this->fields : "*"; //$this->fields = '*'; if(count($this->whereCondition) > 0 && $this->isOperator($this->whereCondition[0])) { array_shift($this->whereCondition); } $where = count($this->whereCondition) > 0 ? " WHERE ". join(" ", $this->whereCondition) : ""; //$this->whereCondition = array(); $join = count($this->joinCondition) > 0 ? join("\n", $this->joinCondition) : ""; //$this->joinCondition = array(); $groupBy = " ". $this->groupBy; //$this->groupBy = ""; $orderBy = " ". $this->orderBy; //$this->orderBy = ""; $limit = $this->limit; //$this->limit = null; $sql = sprintf($this->_query['SELECT'], $fields, $aliases['name'], $aliases['alias']) . $join . $where . $groupBy . $orderBy . $limit; $this->cleanCondition(); $this->executeSQL = $this->modelSQL = $sql; return $sql; } /** * 获取操作符 * * @param string $field 字段 * @return array */ protected function getFieldOperator($field) { $operator = explode(":", $field); if(count($operator) == 1) { return array('operator' => '', 'field' => $field); } $operate = array_pop($operator); $field = join(":", $operator); return array('operator' => $operate, 'field' => $field); } /** * 获取字段别名 * * @param string $name * @param string $connector * @return array */ protected function getFieldAlias($name, $connector = ".") { $aliases = explode('.', $name, 2); if(count($aliases) == 1) { return array('alias' => '', 'name' => $name); } return array('alias' => $aliases[0] . $connector, 'name' => join(".", array_slice($aliases, 1))); } /** * 获取表的别名 * * @param string $name 名字 * @return array */ protected function getTableAlias($name) { if (!$this->verifyTable($name)) { if (gettype($name) == 'string') { throw new TableException("表名不能包含怪字符且不能以数字开头,获取到的是". $name); } throw new TableException("表名必须是字符串加下划线,目标字符为". gettype($name)); } //去掉table前后的``符号 $name = str_replace('`', '', $name); $aliases = explode(' ', $name); //检查表名中时候含数据库名,有数据表名的时候需要单独处理 $hasDatabaseName = false; if(stristr($name, '.')) { $hasDatabaseName = true; } if(count($aliases) == 1) { if($hasDatabaseName) { $names = explode(".", $name); $name = $names[0] . ".`". $names[1] . "`"; }else{ $name = "`". $name ."`"; } return array('alias' => '', 'name' => $name); } $res = array(); $res['alias'] = array_pop($aliases); $res['name'] = join(" ", array_slice($aliases, -1)); if($hasDatabaseName) { $names = explode(".", $res['name']); $res['name'] = $names[0] . ".`". $names[1] . "`"; }else{ $res['name'] = "`". $res['name'] ."`"; } return $res; } /** * 把条件根据『链接字符串,『字段:值』』等内容分组 * * * $whereGroup = array( * ['id' => 1, 'name' => '名字'], * 'or', * ['status' => 1, 'create_at:greater' => '22222'],// * 'or',//链接字段与字段之间的操作符 * 'like',//使用在字段与值之间的操作符,可以省略 * 'and',//字段1与字段2之间的操作符 * 'or',//字段2与字段3之间的操作符 * '...',//字段n与字段n+1之间的操作符 * //字段n与字段n+1之间的操作符可以省略 * //子数组之间的操作符优先于上边的操作符 * ['email' => 'antsnet@163.com', "and", 'nickname' => 'antsnet'], * ['uid' => 1], * 'and', * ['name' => 'ss'], * 'and', * 'and', * 'update_at=1', * ); * @param $group * @return array [['or/and', 'like/equal/...', ['字段1' => '值1', '字段2' => '值2']],[...]] * @throws \Exception */ protected function groupContents($group) { $slices = array(); //如果是一维数组就直接操作值,不检查操作符 if(count($group) == count($group, 1)) { $slices[] = array($group); return $slices; } $count = count($group); $tmpWhere = array(); //最后一次是否是赋值 $lastIsValue = null; foreach($group as $index => $val) { //in比较特殊,它可以是字符串,可以是数组,结果是数组就单独处理不走操作符的处理逻辑 if(substr($index, -3) == ':in' && is_array($val)) { $tmpWhere[$index] = $val; continue; } if(is_array($val)) { // group array 的情况,每组条件之间加 and $slices[] = $this->groupContents($val)[0]; if($index < $count - 1) { $slices[] = 'and'; } continue; } $isOperator = $this->isOperator($val);//如果是操作符,上一个不是操作符就清空tmpWhere,并放入slices $isValue = !$isOperator; if($lastIsValue && $isOperator) { $slices[] = array_values($tmpWhere); $tmpWhere = array(); $tmpWhere[$index] = $val; } else if($count - 1 == $index) { $tmpWhere[$index] = $val; $slices[] = array_values($tmpWhere); $tmpWhere = array(); }else { $tmpWhere[$index] = $val; } $lastIsValue = $isValue; } // @todo 待测试 if(count($tmpWhere) > 0) { //作为独立 slices 的条件 if(count($slices) > 0) { $slices[] = "and"; } $slices[][] = $tmpWhere; } return $slices; } /** * 判断是否是操作符号 * * @param mixed $val 值 * @return bool */ protected function isOperator($val) { if(is_array($val)) { return false; } if(!is_array($val)) { if(in_array(gettype($val), array("array", "object", "resource","resource (closed)"))) { throw new \Exception("期待参数为字符串,获取到的为: " . gettype($val) ."(". json_encode($val) . ")"); } $val = strtolower($val); } return in_array($val, $this->operateVal) || in_array($val, $this->operateTable); } /** * 判断是否是表达式 equal unequal ... * * @param string $val * @return bool * @throws \Exception */ protected function isExpression($val) { if(is_array($val)) { return false; } if(in_array(gettype($val), array("array", "object", "resource","resource (closed)"))) { throw new \Exception("期待参数为字符串,获取到的为: " . gettype($val) ."(". json_encode($val) . ")"); } return isset($this->shortExpression[$val]); } /** * 是否是操作表 * * @param string $val 值 * @return bool */ protected function isOperateTable($val) { if(is_array($val)) { return false; } return in_array($val, $this->operateTable); } /** * where条件子句组合 * * @param array $values ['字段1' => '值1', '字段2' => '值2', ...] * @param string $defaultOperate 默认字句的链接方式 * ..... 额外的参数,用于字段与字段之间的连接 * @return string * @throws \Exception */ protected function handleSubCondition($values, $defaultOperate = 'equal') { $extraParams = array(); if(func_num_args() > 2) { $extraParams = array_slice(func_get_args(), 2); } $where = array(); $operator = $this->shortExpression; $lastIsOperator = false; $lastIsValue = null; $i = 0; foreach($values as $key => $val) { $isOperator = preg_match("/^[0-9].*/", $key) && $this->isOperator($val);//如果是操作符,上一个不是操作符就清空tmpWhere,并放入slices $isValue = !$isOperator; if($lastIsValue && $isOperator)//如果当前是操作符,上一个是值,就将操作符加入到条件中 { $where[] = $val; $lastIsValue = $isValue; $lastIsOperator = $isOperator; continue; } //如果上一次是操作符,这次又是操作符,就是用当前的操作符,去掉上一个操作符 if($lastIsOperator && $isOperator) { if(!$this->faultTolerant) throw new InvalidParams(_i('Unsupported operator'), __LINE__); array_pop($where); $where[] = $val; continue; } if($lastIsValue && $isValue)//需要添加操作符 { $where[] = count($extraParams) > 0 && isset($extraParams[$i-1]) ? $extraParams[$i-1] : "and"; } //$aliases = explode(".", $key); $aliases = $this->getFieldAlias($key); $alias = $aliases['alias']; $operate = $this->getFieldOperator($aliases['name']); $opt = $operate['operator'] ? $operate['operator'] : $defaultOperate; $name = $operate['field']; if($opt == 'in') { if(is_array($val)) { $where[] = sprintf($operator[$opt], $alias, $name, "'". join("','", $val) . "'"); }else{ $where[] = sprintf($operator[$opt], $alias, $name, $val); } }else{ if(!isset($operator[$opt])) { throw new \Exception("Unknow operator " . $opt, __LINE__); } $where[] = sprintf($operator[$opt], $alias, $name, $this->setQuote($val)); } $lastIsValue = $isValue; $lastIsOperator = $isOperator; $i++; } return join(" ", $where); } /** * 处理table join * @param array $slices [['leftJoin', ['table' => '表名', 'alias' => 'a', 'on' => 'a.id = b.id']]] * @param string $defaultJoin * @param null $res * @return $this * @throws \Exception */ protected function handleTableJoin($slices, $defaultJoin = 'leftJoin', &$res = null) { foreach ($slices as $v) { if(count($v) == 0) { throw new \Exception('连接表操作失败,格式:"leftJoin/rightJoin/innerJoin", ["table" => "表名", "alias" => "b", "on" => "a.id=b.id"]', __LINE__); } if(count($v) == 1 && !$this->isOperateTable($v[0])) { array_unshift($v, $defaultJoin); } $joinOperator = array( "leftJoin" => " LEFT JOIN %s ON %s", "rightJoin" => " RIGHT JOIN %s ON %s", "innerJoin" => " INNER JOIN %s ON %s" ); if(!$this->isOperateTable($v[0])) { throw new \Exception('连接表操作失败,格式:"leftJoin/rightJoin/innerJoin", ["table" => "表名", "alias" => "b", "on" => "a.id=b.id"]', __LINE__); } $joinCondition = sprintf($joinOperator[$v[0]], $v[1]['table'] . ' '. $v[1]['alias'], $v[1]['on']); if(!empty($this->join) && is_array($this->join)) { $this->joinCondition = array_merge($this->join, array($joinCondition)); } else { $this->joinCondition[] = $joinCondition; } $res[] = $joinCondition; } unset($joinCondition); return $this; } /** * 处理 where 条件 * @param array $condition * @param null $res * @return $this * @throws \Exception */ protected function handleCondition($condition, &$res = null) { if(!is_array($condition)) { return $this; } $count = count($condition); $whereCondition = array(); for($i = 0; $i < $count; $i++) { $v = $condition[$i]; if(!is_array($v) && $this->isOperator($v)) { $whereCondition[] = ' and '; continue; } //如果有两个及上的操作符 //默认第一个为连接其他条件的操作符,第二个位字段和值之间的操作符, 从第三个开始即为字段与字段之间的连接符 //最里层的字段与值之间的操作符优先级最高 //例如 1: 'or/and','like','or', 'and', ['uid' => 1, 'name' => '名字', 'status' => 激活]; //结果 1:or/and (uid like '%1%' or name like '%名字%' and status like '%激活%') //例如 2: 'or/and','like','or', 'and', ['uid' => 1, 'name' => '名字', 'status:equal' => 激活]; //结果 2:or/and (uid like '%1%' or name like '%名字%' and status = '激活') $operateCondition = array(); $values = array(); $countCondition = count($v); $handleTable = false; for($j = 0; $j < $countCondition; $j++) { $handleTable = false; if($this->isOperateTable($v[$j])) { /* $operateCondition[] = $v[$j]; $joinTable[$v[$j]] = $this->handleTableJoin($v); */ //如果是操作数据表就直接退出循环单独处理 $handleTable = true; break; } if($this->isOperator($v[$j])) { $operateCondition[] = $v[$j]; continue; } if(!$this->isOperator($v[$j])) { if(!is_array($v[$j])) { $whereCondition[] = $v[0]; $whereCondition[] = "(". $v[1] . ")"; break; } $values = is_array($v[$j]) ? array_merge($values, $v[$j]) : array_merge($values, array($v[$j])); } } if($handleTable) { continue; } if(count($operateCondition) > 0 && count($values) > 0) { $whereCondition[] = array_shift($operateCondition); } if(count($values) > 0) { if(count($operateCondition) > 0) { array_unshift($operateCondition, $values); $whereCondition[] = "(". call_user_func_array(array($this, 'handleSubCondition'), $operateCondition) .")"; }else{ $whereCondition[] = "(". $this->handleSubCondition($values) .")"; } } } if(!empty($this->whereCondition)) { $lastCondition = $this->whereCondition[count($this->whereCondition) - 1]; if(count($whereCondition) > 0 && !$this->isOperator($whereCondition[0]) && !$this->isOperator($lastCondition)) { $this->whereCondition = array_merge($this->whereCondition, array('and')); } $this->whereCondition = array_merge($this->whereCondition, $whereCondition); } else { $this->whereCondition = $whereCondition; } $res = $whereCondition; unset($whereCondition); return $this; } /** * 验证数据表名是否符合规范 * 表名不能以数字开头, * * @param string $name * @return bool */ final public function verifyTable($name) { if (!is_string($name) || ( // table !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}$/", $name) && // `table` !preg_match("/^`[a-zA-Z_]+[a-zA-Z0-9_]{0,}`$/", $name)) && // table alias !preg_match("/^[a-zA-Z_]+[a-zA-Z0-9_]{0,}\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) && // `table` alias !preg_match("/^`[a-zA-Z_]+[a-zA-Z0-9_]{0,}`\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) && // database.table !preg_match("/^[a-zA-Z]+\.[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) && !preg_match("/^[a-zA-Z]+\.[a-zA-Z]+[a-zA-Z0-9_]{0,}\s+[a-zA-Z]+[a-zA-Z0-9_]{0,}$/", $name) ) { return false; } return true; } /** * 如果不存在指定的方法则调用提示错误 * * @param string $method * @param mixd $args * @return mixed */ public function __call($method, $args) { if (isset($this->_modelAlias[$method])) { if (method_exists($this, $this->_modelAlias[$method])) { return call_user_func_array(array($this, $this->_modelAlias[$method]), $args); } return \Qii::setError(false, __LINE__, 1506, 'Alias ' . get_called_class() . '->' . $method . '() not found'); } return \Qii::setError(false, __LINE__, 1506, get_called_class() . '->' . $method . '() not found'); } }