此代码身经百战, 使用简便。操作类启用了强类型检查, 代码在下方, 注意需要配合另外4个类文件来执行:
1 Mysql连接操作类 http://www.wkwkk.com/article/a361f53d66945efef0bc33141dbdb165.html
2 用于配套缓存和错误日志的Redis连接操作类(阻塞) http://www.wkwkk.com/article/7f9260987a3d040b4068dcc386500e61.html
3 Sql语句状态缓存类 http://www.wkwkk.com/article/7f9260987a3d040b4068dcc386500e61.html 下方可见
4 基本连接配置和模型实例分配类案例 http://www.wkwkk.com/article/98a742396c78db8ed15c32649c70e9c4.html
代码如下
<?php declare(strict_types=1); namespace Lightpdo; use Lightredis\Worker as RedisWorker; /** * fpm版连贯操作类 * @package orm * @subpackage mysql * @author qinchao <castor@happycoworker.com> * @since 20160811 * @version 20230602 */ if (WORKMODE != 'fpm') { return; } class Worker { private $_mysql_handler; private $_prefix; // redis 连接配置 private $_redis_config = []; // log开关 支持参数: 0=关闭, 1=redis private $_log = 0; private $_state; // 状态容器 private static $_instance = []; /** * 单例 * @param array $config * @return mixed */ public static function instance(array $config) { $hash = substr(md5($config['host'] . $config['port'] . $config['dbname'] . $config['user']), 8, 16); if (empty(static::$_instance[$hash]) || !array_key_exists($hash, static::$_instance)) { static::$_instance[$hash] = new static($config); } static::$_instance[$hash]->_state = new State(); return static::$_instance[$hash]; } public function __construct(array $config) { if (isset($config['transaction']) && $config['transaction']) { $this->_mysql_handler = new ConnectionHandler($config); } else { $this->_mysql_handler = ConnectionHandler::instance($config); } $this->_state = new State(); $this->_prefix = isset($config['prefix']) ? $config['prefix'] : ''; $this->_log = isset($config['log']) ? $config['log'] : 0; $this->_initRedisConfig($config); } private function _initRedisConfig(array $config) { $this->_redis_config = [ 'ip' => isset($config['redis_ip']) ? $config['redis_ip'] : '127.0.0.1', 'port' => isset($config['redis_port']) ? (int)$config['redis_port'] : 6379, 'auth' => isset($config['redis_auth']) ? $config['redis_auth'] : '', 'db' => isset($config['redis_db']) ? (int)$config['redis_db'] : 8, 'log' => isset($config['redis_log']) ? (int)$config['redis_log'] : 4, ]; } /** * @desc 初始化sql状态存储对象 * @access private * @return void */ private function _init_sqlstatement() { if ($this->_state === null) { $this->_state = new State(); } } /** * @desc 断线重连 * @access private * @return void */ private function _reconnect() { $this->_mysql_handler->pdo_instance = $this->_mysql_handler->reconnect(); } /** * @desc 启动事务 * @access public * @return self */ public function transaction(): self { $this->_mysql_handler->pdo_instance->beginTransaction(); return $this; } /** * 提交事务 * @access public * @return void */ public function commit() { $this->_mysql_handler->pdo_instance->commit(); $this->close(); } /** * 撤销事务,回滚 * @access public * @return void */ public function rollback() { $this->_mysql_handler->pdo_instance->rollback(); $this->close(); } /** * @desc 选择字段 * @access public * @return self */ public function select(): self { $this->_init_sqlstatement(); $this->_state->query_type = 1; $numOfArgs = func_num_args(); if ($numOfArgs == 0 || trim((string)func_get_arg(0)) == '') { $this->_state->field = '*'; } else { $selectField = &$this->_state->field; if (is_array(func_get_arg(0))) { foreach (func_get_arg(0) as $v) { $selectField .= $v . ','; } } else { for ($i = 0; $i < $numOfArgs; $i++) { $selectField .= ',' . func_get_arg($i); } } $selectField = ltrim($selectField, ','); } return $this; } /** * @desc 选择数据表 * @param string|array $table 表名/表名数组[表1名=>别名1,表2名=>别名2,...] * @param string $alias 表别名 */ public function from($table, string $alias = ''): self { $this->_init_sqlstatement(); if (is_array($table)) { $clause = ''; foreach ($table as $k => $v) { $clause .= ', ' . trim($this->_prefix . trim($k) . ' ' . $v); } $this->_state->table = ltrim($clause, ', '); } else { $this->_state->table = trim($this->_prefix . trim((string)$table) . ' ' . $alias); } return $this; } /** * @desc 左联表 * @access public * @param string $table 表名 * @param string $alias 表别名 * @param string $case 关联条件 * @return self */ public function leftJoin(string $table, string $alias, string $case): self { $this->_state->join .= ' LEFT JOIN `' . $this->_prefix . trim($table) . '` AS ' . $alias . ' ON ' . $case; return $this; } /** * @desc 内联表 * @access public * @param string $table 表名 * @param string $alias 表别名 * @param string $case 关联条件 * @return self */ public function join(string $table, string $alias, string $case): self { $this->_state->join .= ' INNER JOIN `' . $this->_prefix . trim($table) . '` AS ' . $alias . ' ON ' . $case; return $this; } /** * @desc 右联表 * @access public * @return self */ public function rightJoin(string $table, string $alias, string $case): self { $this->_state->join .= ' RIGHT JOIN `' . $this->_prefix . trim($table) . '` AS ' . $alias . ' ON ' . $case; return $this; } /** * @desc 内联表 * @access public * @param string $table 表名 * @param string $alias 表别名 * @param string $case 关联条件 * @return self */ public function innerJoin(string $table, string $alias, string $case): self { $this->_state->join .= ' INNER JOIN `' . $this->_prefix . trim($table) . '` AS ' . $alias . ' ON ' . $case; return $this; } /** * @desc insert语句中的存在即更新, 注意! 数据表中必须存在主键或唯一索引 * @access public * @return self */ public function dupkeyUpdate($condition): self { if ($this->_state->query_type == 2) { if (is_array($condition)) { $clause = &$this->_state->on_duplicate_key_update; $values = &$this->_state->bind_params; foreach ($condition as $k => $v) { $clause .= ',' . $k . '=?'; $values[] = $v; } $clause = ltrim($clause, ', '); } else { $this->_state->on_duplicate_key_update = $condition; } return $this; } else { $this->_trigger_exception('DUPLICATE KEY UPDATE: requires to start with INSERT at ' . __LINE__); } } /** * @desc where语句 * @access public * @return self */ public function where(): self { $this->_init_sqlstatement(); $numOfArgs = func_num_args(); if ($numOfArgs && is_array(func_get_arg(0))) { $clause = ''; foreach (func_get_arg(0) as $k => $v) { $clause .= ' AND ' . $k . '=?'; $this->_state->bind_params[] = $v; } if (empty($this->_state->where)) { $this->_state->where = ltrim($clause, ' AND '); } else { $this->_state->where .= ' AND ' . ltrim($clause, ' AND '); } } else { switch ($numOfArgs) { // 字符串条件 case 1: $clause = trim(func_get_arg(0)); if (empty($this->_state->where)) { $this->_state->where = $clause; } else { $this->_state->where .= ' AND ' . $clause; } break; // 单等号条件 case 2: $clause = func_get_arg(0) . ' = ?'; if (empty($this->_state->where)) { $this->_state->where = $clause; } else { $this->_state->where .= ' AND ' . $clause; } $this->_state->bind_params[] = func_get_arg(1); break; // 其他符号条件 case 3: $arg1 = strtolower(func_get_arg(1)); if (array_key_exists($arg1, [ 'in' => 0, 'not in' => 0, ])) { if (trim(func_get_arg(2)) == '') { $this->_trigger_exception('IN(): missing argument'); } else { $clause = func_get_arg(0) . ' ' . $arg1 . ' (' . func_get_arg(2) . ')'; $this->_build_where_clause($clause); } } elseif (array_key_exists($arg1, [ 'like' => 0, 'not like' => 0 ])) { if (trim(func_get_arg(2)) == '') { $this->_trigger_exception('LIKE(): missing argument at ' . __LINE__); } else { $clause = func_get_arg(0) . ' ' . $arg1 . " ('" . func_get_arg(2) . "')"; $this->_build_where_clause($clause); } } elseif (array_key_exists($arg1, [ '!=' => 0, '>' => 0, '>=' => 0, '<=' => 0, '<' => 0, '<>' => 0, '=' => 0, ])) { $clause = func_get_arg(0) . ' ' . $arg1 . ' ' . '?'; if (empty($this->_state->where)) { $this->_state->where = $clause; } else { $this->_state->where .= ' AND ' . $clause; } $this->_state->bind_params[] = func_get_arg(2); } else { $this->_trigger_exception('where(): bad operator at ' . __LINE__); } break; // 过多的参数 default: $this->_trigger_exception('where(): too many arguments'); } } return $this; } /** * @desc where 语句中的IN * @param string $field * @param array|string|int $values * @param string $param_type int或者string * @return self */ public function whereIn($field, $values, $param_type = '', $method = 'IN') { if (is_string($values) || is_int($values)) { // 字符串 $this->_build_where_clause(sprintf('%s %s (%s)', $field, $method, $values)); } if (is_array($values)) { // 数组 if (!count($values)) { $this->_trigger_exception($method . ': missing arguments'); } $str = ltrim(array_reduce($values, function ($r, $vv) use ($param_type) { switch (gettype($vv)) { case 'integer': return sprintf('%s,%d', $r, $vv); case 'string': if ($param_type == 'int') { return sprintf('%s,%d', $r, (int)$vv); } else { return sprintf("%s,'%s'", $r, addslashes($vv)); } default: $this->_trigger_exception('IN/NOT IN only supports int & string'); } }), ','); $this->_build_where_clause(sprintf('%s %s (%s)', $field, $method, $str)); } return $this; } /** * @desc where 语句中的NOT IN处理 * @param string $field * @param array|string $values * @return self */ public function whereNotIn($field, $values, $parameter_type = '') { return $this->whereIn($field, $values, $parameter_type, 'NOT IN'); } /** * @desc 在where语句的基础上增加或计算符的条件语句 * @access public * @return self */ public function orWhere() { switch (func_num_args()) { case 0: self::_trigger_runtime_exception('orWhere(): missing argument at ' . __LINE__); case 1: if (is_string(func_get_arg(0))) { if (empty($this->_state->where)) { self::_trigger_runtime_exception('orWhere() must be called after other where() statements at ' . __LINE__); } // 字符串条件 $this->_state->where .= ' OR ' . trim(func_get_arg(0)); } else { self::_trigger_runtime_exception('orWhere(): type error at ' . __LINE__); } break; default: self::_trigger_runtime_exception('orWhere(): wrong behavior at ' . __LINE__); } return $this; } /** * @desc 组装where子句当中存在like或in方法时所用的代码复用方法 * @access private * @param string $clause */ private function _build_where_clause(string $clause) { if (empty($this->_state->where)) { $this->_state->where = $clause; } else { $this->_state->where .= ' AND ' . $clause; } } /** * @desc 连贯操作之排序 * @access public * @param string $order * @return self */ public function orderBy(string $order): self { $this->_state->order = ' ORDER BY ' . $order; return $this; } /** * @desc 连贯操作之分组 * @access public * @param string $group * @return self */ public function groupBy(string $group): self { $this->_state->group = ' GROUP BY ' . $group; return $this; } /** * @desc 连贯操作之having * @access public * @param string $having * @return self */ public function having(string $having): self { $this->_state->having = ' HAVING ' . $having; return $this; } /** * @desc 批量更新方法 * @access public * @param array $params 以主键为首个键值的数组 * @return self */ public function batchUpdate($params): self { $this->_state->query_type = 31; if (is_array($params) && isset($params[0])) { $primaryKey = $primaryValue = ''; foreach ($params as $v) { foreach ($v as $k2 => $v2) { $primaryKey = $k2; $primaryValue = $v2; break; } break; } $arr2 = []; foreach ($params as $v) { $arr2[$v[$primaryKey]] = $v; array_splice($arr2[$v[$primaryKey]], $primaryKey, 1); } $sql = ''; $sets = &$this->_state->bind_params; foreach ($arr2[$primaryValue] as $k => $v) { $sql .= sprintf('%s = CASE %s ', $k, $primaryKey); foreach ($arr2 as $k2 => $v2) { $sql .= "WHEN ? THEN ? "; $sets[] = $k2; $sets[] = $v2[$k]; } $sql .= 'END, '; } $sql = substr($sql, 0, strrpos($sql, ',')); $this->_state->update_field = $sql; $this->_state->where = sprintf('%s IN (%s)', $primaryKey, implode(',', array_keys($arr2))); } else { $this->_trigger_exception('batchUpdate(): bad parameter'); } return $this; } /** * @desc 批量插入方法 * @access public * @param array $params 二维数组 首个子数组内含键必须包含字段名称信息 * @return self */ public function batchInsert($params): self { $this->_state->query_type = 21; if (is_array($params) && count($params) >= 1) { $insertField = $insertValues = ''; foreach ($params[0] as $k => $v) { $insertField .= sprintf(',`%s`', $k); } $this->_state->insert_field = ltrim($this->_state->insert_field, ','); foreach ($params as $v) { $insertValues .= '( '; foreach ($v as $vv) { $insertValues .= ',?'; $this->_state->bind_params[] = $vv; } $insertValues = sprintf('%s),', ltrim($insertValues, ',')); } $this->_state->insert_values = ltrim($insertValues, ','); } else { $this->_trigger_exception('batchInsert(): bad parameter'); } return $this; } /** * @desc 连贯操作之限制条数 * @access public * @return self */ public function limit(): self { switch (func_num_args()) { case 1: $this->_state->limit = sprintf('LIMIT %u', func_get_arg(0)); break; case 2: $this->_state->limit = sprintf('LIMIT %u,%u', func_get_arg(0), func_get_arg(1)); break; default: $this->_trigger_exception('limit(): bad parameter'); } return $this; } /** * @desc 连贯操作之插入指定数据表 * @access public * @param string $table * @return self */ public function insert(string $table): self { $this->_init_sqlstatement(); $this->_state->query_type = 2; $this->_state->table = $this->_prefix . trim($table); return $this; } /** * @desc 连贯操作之插入字段和对应值 * @access public * @return self */ public function values(): self { $numOfArgs = func_num_args(); if ($numOfArgs) { $valuesArr = &$this->_state->bind_params; if (is_array(func_get_arg(0))) { $numOfValues = count(func_get_arg(0)); $insertField = &$this->_state->insert_field; foreach (func_get_arg(0) as $k => $v) { $insertField .= ',`' . $k . '`'; $valuesArr[] = $v; } $this->_state->insert_field = ltrim($this->_state->insert_field, ','); } else { $numOfValues = $numOfArgs; $this->_state->insert_field = ''; for ($i = 0; $i < $numOfArgs; $i++) { $valuesArr[] = func_get_arg($i); } } $insertValues = &$this->_state->insert_values; $insertValues .= str_repeat(',?', $numOfValues); $insertValues = '( ' . ltrim($insertValues, ',') . ' )'; } else { $this->_trigger_exception('values() expects at least 1 parameter'); } return $this; } /** * @desc 连贯操作之获取插入的Id * @access public * @return self */ public function getId(): self { $this->_state->get_last_inserted_id = 1; return $this; } // 获取指定字段的记录数量 public function count($field = '*') { return $this->_callFuncOnFieldSelector('count', $field); } // 获取指定字段的最大值 public function max($field = '') { return $this->_callFuncOnFieldSelector('max', $field); } // 获取指定字段的最小值 public function min($field = '') { return $this->_callFuncOnFieldSelector('min', $field); } // 获取指定字段的平均值 public function avg($field = '') { return $this->_callFuncOnFieldSelector('avg', $field); } // 获取指定字段的和 public function sum($field = '') { return $this->_callFuncOnFieldSelector('sum', $field); } private function _callFuncOnFieldSelector($funcName, $field) { $this->_state->query_type = 1; if (empty($field)) { $this->_trigger_exception('sql needs a field as parameter'); } else { $this->_state->field = $funcName . '(' . $field . ')'; } return $this->sql($this->_build_sql4query())->bind($this->_state->bind_params)->_grab(); } /** * @desc 自增操作 * @access public * @return self */ public function increase(): self { switch (func_num_args()) { case 1: $this->_state->update_field = '`' . func_get_arg(0) . '` = `' . func_get_arg(0) . '`+1'; break; case 2: $this->_state->update_field = '`' . func_get_arg(0) . '` = `' . func_get_arg(0) . '`+?'; $this->_state->bind_params[] = func_get_arg(1); break; default: $this->_trigger_exception('invalid parameter while using increment()'); } return $this; } /** * @desc 自减操作 * @access public * @return self */ public function decrease(): self { switch (func_num_args()) { case 1: $this->_state->update_field = '`' . func_get_arg(0) . '` = `' . func_get_arg(0) . '`-1'; break; case 2: $this->_state->update_field = '`' . func_get_arg(0) . '` = `' . func_get_arg(0) . '`-?'; $this->_state->bind_params[] = func_get_arg(1); break; default: $this->_trigger_exception('invalid paramter while using decrement()'); } return $this; } /** * @desc 连贯操作之开始插入数据 * @access public * @return array|false|mixed|void */ public function go() { if ($this->_state->is_sql_native) { // 原生语句执行 if ($this->_state->query_type == 1) { $this->_trigger_exception('sql query type error at ' . __LINE__); } } switch ($this->_state->query_type) { case 2: // 插入 case 21: return $this->_run($this->_build_sql4insert(), $this->_state->bind_params, 4); case 3: // 更新 case 31: return $this->_run($this->_build_sql4update(), $this->_state->bind_params, 5); case 4: // 删除 return $this->_run($this->_build_sql4delete(), $this->_state->bind_params, 6); default: $this->_trigger_exception('can not determine sql type.'); } } /** * @desc 连贯操作之更新的目标数据表 * @access public * @param string $table * @return self */ public function update(string $table): self { $this->_init_sqlstatement(); $this->_state->query_type = 3; $this->_state->table = $this->_prefix . trim($table); return $this; } /** * @desc 连贯操作之指定更新的字段和值 * @access public * @param array $params * @return self */ public function set(array $params): self { $updateField = &$this->_state->update_field; $setsArr = &$this->_state->bind_params; foreach ($params as $k => $v) { $updateField .= ',`' . $k . '`=?'; $setsArr[] = $v; } $updateField = ltrim($updateField, ','); return $this; } /** * @desc 连贯操作之删除的目标数据库 * @access public * @param string $table * @return self */ public function delete(string $table): self { $this->_init_sqlstatement(); $this->_state->query_type = 4; $this->_state->table = $this->_prefix . trim($table); return $this; } /** * 查询多行 * @access public */ public function all() { if ($this->_state->is_sql_native) { // 原生语句 return $this->_all(); } else { // 连贯操作 if (func_num_args() && func_get_arg(0) > 0) { $this->_state->limit = ' LIMIT ' . abs((int)func_get_arg(0)); } return $this->sql($this->_build_sql4query())->bind($this->_state->bind_params)->_all(); } } /** * 方法别名: 查询多行 * @access public */ public function rows() { return $this->all(); } /** * 查询单行 * @access public * @return array */ public function fetch() { if ($this->_state->is_sql_native) { // 原生语句 return $this->_fetch(); } else { // 连贯操作 $this->_state->limit = ' LIMIT 1'; return $this->sql($this->_build_sql4query())->bind($this->_state->bind_params)->_fetch(); } } public function row() { return $this->fetch(); } /** * 函数别名: 查询一行, 兼容TP框架orm * @access public */ public function find() { return $this->row(); } /** * 查询一个 */ public function grab() { if ($this->_state->is_sql_native) { // 原生语句 return $this->_grab(); } else { // 连贯操作 $this->_state->limit = ' LIMIT 1'; if (func_num_args() && !empty(func_get_arg(0))) { $this->_state->field = trim(func_get_arg(0)); } return $this->sql($this->_build_sql4query())->bind($this->_state->bind_params)->_grab(); } } /** * 打印出Sql语句 * @access public * @return bool|string|void */ public function getSql() { switch ($this->_state->query_type) { case 1: return $this->_build_sql4query(); case 2: case 21: return $this->_build_sql4insert(); case 3: case 31: return $this->_build_sql4update(); case 4: return $this->_build_sql4delete(); default: $this->_trigger_exception('unknown sql type'); } } /** * 方法别名: 打印sql语句 * @access public */ public function print() { return $this->getSql(); } /** * 方法别名: 打印Sql语句 * @access public */ public function dump() { return $this->getSql(); } /** * 构造删除用的Sql语句 * @access private */ private function _build_sql4delete() { // 如果where条件不存在,则终止操作 if (empty($this->_state->table) || empty($this->_state->where)) { return false; } $this->_state->sql_statement = 'DELETE FROM `' . $this->_state->table . '` WHERE ' . $this->_state->where . $this->_state->limit; return $this->_state->sql_statement; } /** * 构造更新用的Sql语句 * @access private * @return string */ private function _build_sql4update() { // 如果where条件不存在,则终止操作 if (empty($this->_state->table) || empty($this->_state->where)) { self::_trigger_runtime_exception('Attempted unconditionally update. Stop.'); } $this->_state->sql_statement = 'UPDATE `' . $this->_state->table . '` SET ' . $this->_state->update_field . ' WHERE ' . $this->_state->where . $this->_state->limit; return $this->_state->sql_statement; } /** * 构造插入用的Sql语句 * @access private */ private function _build_sql4insert() { if (empty($this->_state->table)) { return false; } if (empty($this->_state->insert_field)) { $this->_state->sql_statement = empty($this->_state->on_duplicate_key_update) ? 'INSERT INTO `' . $this->_state->table . '` VALUES ' . $this->_state->insert_values : 'INSERT INTO `' . $this->_state->table . '` VALUES ' . $this->_state->insert_values . ' ON DUPLICATE KEY UPDATE ' . $this->_state->on_duplicate_key_update; } else { $this->_state->sql_statement = empty($this->_state->on_duplicate_key_update) ? 'INSERT INTO `' . $this->_state->table . '` ( ' . $this->_state->insert_field . ' ) VALUES ' . $this->_state->insert_values : 'INSERT INTO `' . $this->_state->table . '` ( ' . $this->_state->insert_field . ' ) VALUES ' . $this->_state->insert_values . ' ON DUPLICATE KEY UPDATE ' . $this->_state->on_duplicate_key_update; } return $this->_state->sql_statement; } /** * 构造查询用的Sql语句 * @access private * @return string */ private function _build_sql4query() { if (empty($this->_state->table)) { self::_trigger_runtime_exception('No table was specified while querying'); } $this->_state->sql_statement = sprintf('SELECT %s FROM %s %s %s %s %s %s %s', $this->_state->field, $this->_state->table, $this->_state->join, empty($this->_state->where) ? '' : 'WHERE ' . $this->_state->where, $this->_state->group, $this->_state->having, $this->_state->order, $this->_state->limit); return $this->_state->sql_statement; } /** * 触发错误并销毁sql状态存储对象 * @access public * @param string $msg */ private function _trigger_exception(string $msg) { $this->_state = null; throw new \PDOException($msg); } /** * 准备Sql语句 * @access public * @param string $sql 原始sql语句 * @return self */ public function sql(string $sql): self { $this->_init_sqlstatement(); $this->_state->is_sql_native = 1; if (trim($sql) == '') { $this->_trigger_exception('no sql found'); } else { switch (strtolower($sql[0])) { case 's': // select 查询语句 $this->_state->query_type = 1; break; case 'i': // insert 插入语句 $this->_state->query_type = 2; break; case 'u': // update 更新语句 $this->_state->query_type = 3; break; case 'd': if (strtolower($sql[1]) == 'e') { // delete 删除语句 $this->_state->query_type = 4; break; } else { // 不支持风险语句drop $this->_trigger_exception('operation type error'); } default: $this->_trigger_exception('operation type error'); } $this->_state->sql_statement = str_ireplace('*_', $this->_prefix, trim($sql)); } return $this; } /** * 准备绑定的参数 * @access public */ public function bind($params): self { $this->_state->bind_params = $params; return $this; } /** * 使用cache存储 * @param int $ttl 生存秒 * @param string $key */ public function cache(int $ttl = 30, string $key = '') { $this->_state->cache = 1; $this->_state->cache_ttl = abs((int)$ttl); $this->_state->cache_key = $key; return $this; } /** * @desc 参数绑定处理 * @access private * @param object $pdo_statement * @param array $params * @return object */ private static function preparation_loop($pdo_statement, $params) { foreach ($params as $k => $v) { switch (gettype($v)) { case 'integer': case 'double': $pdo_statement->bindValue($k + 1, $v, \PDO::PARAM_INT); break; case 'string': $pdo_statement->bindValue($k + 1, $v, \PDO::PARAM_STR); break; case 'boolean': $pdo_statement->bindValue($k + 1, ($v ? '1' : '0'), \PDO::PARAM_BOOL); break; case 'NULL': $pdo_statement->bindValue($k + 1, null, \PDO::PARAM_NULL); break; case 'resource': $pdo_statement->bindValue($k + 1, $v, \PDO::PARAM_LOB); break; case 'array': self::_trigger_runtime_exception('An array with > 2 dimensions was passed into the where clause'); } } return $pdo_statement; } /** * 预编译执行SQL * @access private * @param string $sql 带有占位符的sql语句 * @param array $params 绑定的参数 * @param int $sql_type 执行类型 1 多行 2 单行 3 单个 4 插入 5 更新 * @return mixed 查询结果,或执行后受影响的行数 * @author qinchao <castor@happycoworker.com> */ private function _run($sql, $params = [], $sql_type = 1) { if ($params === []) { $pdo_statement = $this->_mysql_handler->pdo_instance->query($sql); } else if (empty($sql)) { $this->_trigger_exception('No sql.'); } else { $pdo_statement = $this->_mysql_handler->pdo_instance->prepare($sql); } if ($this->_mysql_handler->pdo_instance->errorCode() != '00000') { $errInfo = $this->_mysql_handler->pdo_instance->errorInfo(); if ($errInfo[1] === 2013 || $errInfo[1] === 2016 || is_string($errInfo[2]) && strpos($errInfo[2], 'STMT_PREPARE') !== false) { // 此时已断线 $this->_reconnect(); return $this->_run($sql, $params, $sql_type); } else { // 其他错误 if ($this->_log == 1) { error_log(@json_encode(array_merge($this->_mysql_handler->pdo_instance->errorInfo(), [$sql]), JSON_PRETTY_PRINT), 3, SWOOLE_LOG_FILE); } $this->_trigger_exception(json_encode($this->_mysql_handler->pdo_instance->errorInfo(), JSON_UNESCAPED_UNICODE)); } } if ($pdo_statement === false) { $this->_state = null; return false; } if ($params !== []) { if (is_array($params)) { $pdo_statement = self::preparation_loop($pdo_statement, $params); $pdo_statement->execute($params); } else { $pdo_statement = self::preparation_loop($pdo_statement, [$params]); $pdo_statement->execute(); } } switch ($sql_type) { case 1: $r = $pdo_statement->fetchAll(\PDO::FETCH_ASSOC); if ($r === false) { $r = []; } break; case 2: $r = $pdo_statement->fetch(\PDO::FETCH_ASSOC); if ($r === false) { $r = []; } break; case 3: $r = $pdo_statement->fetchColumn(); break; case 4: // 插入 $r = $pdo_statement->rowCount(); if ($r && $this->_state->get_last_inserted_id == 1) { $r = $this->_mysql_handler->pdo_instance->lastInsertId(); } $pdo_statement = null; $this->_state = null; return $r; case 5: // 更新 case 6: // 删除 $r = $pdo_statement->rowCount(); $pdo_statement = null; $this->_state = null; return $r; default: self::_trigger_runtime_exception("Illegal query type."); } if ($this->_state->cache == 1) { $this->_writeInRedis($r); } $pdo_statement = null; $this->_state = null; return $r; } /** * 往缓存里写数据 * @access private */ private function _writeInRedis($data) { $r = RedisWorker::connect($this->_redis_config['db']); if (trim((string)$this->_state->cache_key) == '') { $this->_state->cache_key = substr(md5($this->_state->sql_statement . json_encode($this->_state->bind_params)), 4, 24); } if (!$r->exists($this->_state->cache_key)) { $r->set($this->_state->cache_key, (string)json_encode($data)); $r->expire($this->_state->cache_key, $this->_state->cache_ttl); } $r->close(); } /** * 从缓存取数据 * @access private */ private function _read_from_redis($sql_type) { $r = RedisWorker::connect($this->_redis_config['db']); if (trim((string)$this->_state->cache_key) == '') { $result = $r->get(substr(md5($this->_state->sql_statement . (count($this->_state->bind_params) > 0 ? json_encode($this->_state->bind_params) : '')), 4, 24)); if (!$result) { $r->close(); return $this->_run($this->_state->sql_statement, $this->_state->bind_params, $sql_type); } else { $this->_state = null; $r->close(); return json_decode($result, true); } } else { $result = $r->get($this->_state->cache_key); if (!$result) { // 结果已过期 $r->close(); return $this->_run($this->_state->sql_statement, $this->_state->bind_params, $sql_type); } else { $this->_state = null; $r->close(); return json_decode($result, true); } } } // 预编译方式读取多行 private function _all() { if (trim($this->_state->sql_statement) == '') { $this->_trigger_exception('No sql'); } else { if ($this->_state->cache == 1) { $result = $this->_read_from_redis(1); } else { $result = $this->_run($this->_state->sql_statement, $this->_state->bind_params, 1); } return $result; } } /** * 预编译方式获取1行 * @return array|false */ private function _fetch() { if (trim($this->_state->sql_statement) == '') { $this->_trigger_exception('No sql'); } else { if ($this->_state->cache == 1) { $result = $this->_read_from_redis(2); } else { $result = $this->_run($this->_state->sql_statement, $this->_state->bind_params, 2); } return $result; } } /** * 预编译方式读取1个 * @access private */ private function _grab() { if (trim($this->_state->sql_statement) == '') { $this->_trigger_exception('No sql'); } else { if ($this->_state->cache == 1) { $result = $this->_read_from_redis(3); } else { $result = $this->_run($this->_state->sql_statement, $this->_state->bind_params, 3); } return $result; } } /** * @desc 呼叫匿名方法 * @access public * @param $fn * @param $args * @return Worker|void */ public function __call($fn, $args) { switch (count($args)) { case 1: // where+字段名 $this->_init_sqlstatement(); if (strtolower(substr($fn, 0, 5)) == 'where') { $field = strtolower(substr($fn, 5)); if (empty($this->_state->where)) { $this->_state->where = $field . ' = ?'; } else { $this->_state->where .= ' AND ' . $field . ' = ?'; } $this->_state->bind_params[] = $args[0]; } return $this; } } /** * @desc 触发错误并销毁sql状态存储对象 * @access private * @param string $msg */ private static function _trigger_runtime_exception(string $msg) { throw new \Exception($msg); } /** * 关闭连接 * @access public * @return void */ public function close() { $this->_mysql_handler->close(); } /** * 析构 * @access public */ public function __destruct() { $this->_mysql_handler->close(); } }
注意: 这些代码是阻塞的, 不适用于协程版的swoole。