From c7003a7139f3c3a58b120136333e1e665e75a2f9 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Sun, 21 Aug 2022 16:08:20 +0800 Subject: [PATCH] add mysql support --- config/global.php | 14 + src/Globals/global_functions.php | 22 + src/ZM/Event/Listener/WorkerEventListener.php | 32 + src/ZM/Store/MySQL/MySQLConnection.php | 131 ++++ src/ZM/Store/MySQL/MySQLDriver.php | 48 ++ src/ZM/Store/MySQL/MySQLException.php | 9 + src/ZM/Store/MySQL/MySQLPool.php | 108 ++++ src/ZM/Store/MySQL/MySQLQueryBuilder.php | 31 + src/ZM/Store/MySQL/MySQLStatement.php | 123 ++++ src/ZM/Store/MySQL/MySQLStatementWrapper.php | 239 +++++++ src/ZM/Store/MySQL/MySQLWrapper.php | 597 ++++++++++++++++++ tests/ZM/Utils/ZMUtilTest.php | 8 +- 12 files changed, 1361 insertions(+), 1 deletion(-) create mode 100644 src/ZM/Store/MySQL/MySQLConnection.php create mode 100644 src/ZM/Store/MySQL/MySQLDriver.php create mode 100644 src/ZM/Store/MySQL/MySQLException.php create mode 100644 src/ZM/Store/MySQL/MySQLPool.php create mode 100644 src/ZM/Store/MySQL/MySQLQueryBuilder.php create mode 100644 src/ZM/Store/MySQL/MySQLStatement.php create mode 100644 src/ZM/Store/MySQL/MySQLStatementWrapper.php create mode 100644 src/ZM/Store/MySQL/MySQLWrapper.php diff --git a/config/global.php b/config/global.php index 5790f328..805d7d6f 100644 --- a/config/global.php +++ b/config/global.php @@ -80,4 +80,18 @@ $config['file_server'] = [ ], ]; +/* MySQL 数据库连接配置,框架将自动生成连接池,支持多个连接池 */ +$config['mysql'] = [ + [ + 'pool_name' => '', // 默认只有一个空名称的连接池,如果需要多个连接池,请复制此段配置并修改参数和名称 + 'host' => '127.0.0.1', // 填写数据库服务器地址后才会创建数据库连接 + 'port' => 3306, + 'username' => 'root', + 'password' => 'ZhamaoTEST', + 'dbname' => 'zm', + 'charset' => 'utf8mb4', + 'pool_size' => 64, + ], +]; + return $config; diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index 30265618..15c76222 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -12,6 +12,8 @@ use ZM\Container\ContainerInterface; use ZM\Context\Context; use ZM\Logger\ConsoleLogger; use ZM\Middleware\MiddlewareHandler; +use ZM\Store\MySQL\MySQLException; +use ZM\Store\MySQL\MySQLWrapper; // 防止重复引用引发报错 if (function_exists('zm_internal_errcode')) { @@ -165,3 +167,23 @@ function app(string $abstract = null, array $parameters = []) return resolve($abstract, $parameters); } + +/** + * 获取 MySQL 调用的类 + * + * @throws MySQLException + */ +function mysql(string $name = '') +{ + return new MySQLWrapper($name); +} + +/** + * 获取构建 MySQL 的类 + * + * @throws MySQLException + */ +function mysql_builder(string $name = '') +{ + return (new MySQLWrapper($name))->createQueryBuilder(); +} diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index ec9718bd..c275648d 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -142,4 +142,36 @@ class WorkerEventListener }); $handler->handleAll(); } + + /** + * 初始化各种连接池 + * + * TODO:未来新增其他db的连接池 + * + * @throws ConfigException + * @throws MySQLException + */ + private function initConnectionPool() + { + // 清空 MySQL 的连接池 + foreach (MySQLPool::getAllPools() as $name => $pool) { + MySQLPool::destroyPool($name); + } + + // 读取 MySQL 配置文件 + $conf = ZMConfig::get('global.mysql'); + if (is_array($conf) && !is_assoc_array($conf)) { + // 如果有多个数据库连接,则遍历 + foreach ($conf as $conn_conf) { + if ($conn_conf['host'] !== '') { + MySQLPool::create($conn_conf['pool_name'], $conn_conf); + } + } + } elseif (is_assoc_array($conf)) { + // 这种情况也支持,但是不推荐 + if ($conf['host'] !== '') { + MySQLPool::create($conf['pool_name'], $conf); + } + } + } } diff --git a/src/ZM/Store/MySQL/MySQLConnection.php b/src/ZM/Store/MySQL/MySQLConnection.php new file mode 100644 index 00000000..e8e430cf --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLConnection.php @@ -0,0 +1,131 @@ +debug('Constructing...'); + $this->conn = MySQLPool::pool($params['dbName'])->get(); + $this->pool_name = $params['dbName']; + } + + public function __destruct() + { + logger()->debug('Destructing!!!'); + MySQLPool::pool($this->pool_name)->put($this->conn); + } + + /** + * @param mixed $sql + * @param mixed $options + */ + public function prepare($sql, $options = []) + { + try { + logger()->debug('Running SQL prepare: ' . $sql); + $statement = $this->conn->prepare($sql, $options); + assert($statement !== false); + } catch (PDOException $exception) { + throw new MySQLException($exception->getMessage(), $exception->getCode(), $exception); + } + return new MySQLStatement($statement); + } + + /** + * @throws MySQLException + */ + public function query(...$args) + { + try { + $statement = $this->conn->query(...$args); + assert($statement !== false); + } catch (PDOException $exception) { + throw new MySQLException($exception->getMessage(), $exception->getCode(), $exception); + } + return new MySQLStatement($statement); + } + + public function quote($value, $type = ParameterType::STRING) + { + return $this->conn->quote($value, $type); + } + + /** + * @param mixed $sql + * @throws MySQLException + */ + public function exec($sql) + { + try { + logger()->debug('Running SQL exec: ' . $sql); + $statement = $this->conn->exec($sql); + assert($statement !== false); + return $statement; + } catch (PDOException $exception) { + throw new MySQLException($exception->getMessage(), $exception->getCode(), $exception); + } + } + + /** + * @param null|mixed $name + * @throws MySQLException + */ + public function lastInsertId($name = null) + { + try { + return $name === null ? $this->conn->lastInsertId() : $this->conn->lastInsertId($name); + } catch (PDOException $exception) { + throw new MySQLException($exception->getMessage(), $exception->getCode(), $exception); + } + } + + public function beginTransaction() + { + return $this->conn->beginTransaction(); + } + + public function commit() + { + return $this->conn->commit(); + } + + public function rollBack() + { + return $this->conn->rollBack(); + } + + public function errorCode() + { + return $this->conn->errorCode(); + } + + public function errorInfo() + { + return $this->conn->errorInfo(); + } + + /** + * @return mixed + */ + public function getPoolName() + { + return $this->pool_name; + } +} diff --git a/src/ZM/Store/MySQL/MySQLDriver.php b/src/ZM/Store/MySQL/MySQLDriver.php new file mode 100644 index 00000000..2205248e --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLDriver.php @@ -0,0 +1,48 @@ +debug('Requiring new connection'); + return new MySQLConnection($params); + } + + public function getDatabasePlatform(): MySqlPlatform + { + return new MySqlPlatform(); + } + + public function getSchemaManager($conn) + { + return new MySqlSchemaManager($conn); + } + + public function getName() + { + return 'pdo_mysql_pool'; + } + + public function getDatabase($conn) + { + $conf = ZMConfig::get('global.mysql'); + + if ($conn instanceof MySQLConnection) { + foreach ($conf as $v) { + if (($v['name'] ?? $v['dbname']) === $conn->getPoolName()) { + return $v['dbname']; + } + } + } + return ''; + } +} diff --git a/src/ZM/Store/MySQL/MySQLException.php b/src/ZM/Store/MySQL/MySQLException.php new file mode 100644 index 00000000..ec11f4fd --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLException.php @@ -0,0 +1,9 @@ + 连接池列表 + */ + private static $pools = []; + + /** + * 通过配置文件创建一个 MySQL 连接池 + * + * @throws MySQLException + */ + public static function create(string $name, array $config) + { + $size = $config['pool_size'] ?? 128; + $connect_str = 'mysql:host={host};port={port};dbname={dbname};charset={charset}'; + $table = [ + '{host}' => $config['host'], + '{port}' => $config['port'], + '{dbname}' => $config['dbname'], + '{charset}' => $config['charset'] ?? 'utf8mb4', + ]; + $connect_str = str_replace(array_keys($table), array_values($table), $connect_str); + self::checkExtension(); + switch (Driver::getActiveDriverClass()) { + case WorkermanDriver::class: + self::$pools[$name] = new WorkermanObjectPool($size, PDO::class, $connect_str, $config['username'], $config['password'], $config['options'] ?? []); + break; + case SwooleDriver::class: + self::$pools[$name] = new SwooleObjectPool($size, PDO::class, $connect_str, $config['username'], $config['password'], $config['options'] ?? []); + } + } + + /** + * 获取一个数据库连接池 + * + * @param string $name 连接池名称 + */ + public static function pool(string $name): PoolInterface + { + if (!isset(self::$pools[$name])) { + throw new RuntimeException("Pool {$name} not found"); + } + return self::$pools[$name]; + } + + /** + * 获取所有数据库连接池 + * + * @return PoolInterface[] + */ + public static function getAllPools(): array + { + return self::$pools; + } + + /** + * 销毁数据库连接池 + * + * @param string $name 数据库连接池名称 + */ + public static function destroyPool(string $name) + { + unset(self::$pools[$name]); + } + + /** + * 检查数据库启动必要的依赖扩展,如果不符合要求则抛出异常 + * + * @throws MySQLException + */ + public static function checkExtension() + { + ob_start(); + phpinfo(); // 这个phpinfo是有用的,不能删除 + $str = ob_get_clean(); + $str = explode("\n", $str); + foreach ($str as $v) { + $v = trim($v); + if ($v == '') { + continue; + } + if (mb_strpos($v, 'API Extensions') === false) { + continue; + } + if (mb_strpos($v, 'pdo_mysql') === false) { + throw new MySQLException(zm_internal_errcode('E00028') . '未安装 mysqlnd php-mysql扩展。'); + } + } + } +} diff --git a/src/ZM/Store/MySQL/MySQLQueryBuilder.php b/src/ZM/Store/MySQL/MySQLQueryBuilder.php new file mode 100644 index 00000000..3eff9a9d --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLQueryBuilder.php @@ -0,0 +1,31 @@ +getConnection()); + $this->wrapper = $wrapper; + } + + /** + * @throws DbException + * @return int|MySQLStatementWrapper + */ + public function execute() + { + if ($this->getType() === self::SELECT) { + return $this->wrapper->executeQuery($this->getSQL(), $this->getParameters(), $this->getParameterTypes()); + } + return $this->wrapper->executeStatement($this->getSQL(), $this->getParameters(), $this->getParameterTypes()); + } +} diff --git a/src/ZM/Store/MySQL/MySQLStatement.php b/src/ZM/Store/MySQL/MySQLStatement.php new file mode 100644 index 00000000..ea192d7c --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLStatement.php @@ -0,0 +1,123 @@ +statement = $obj; + } + + public function closeCursor() + { + return $this->statement->closeCursor(); + } + + public function columnCount() + { + return $this->statement->columnCount(); + } + + public function setFetchMode($fetchMode, $arg2 = null, $arg3 = []) + { + if ($arg2 !== null && $arg3 !== []) { + return $this->statement->setFetchMode($fetchMode, $arg2, $arg3); + } + if ($arg2 !== null && $arg3 === []) { + return $this->statement->setFetchMode($fetchMode, $arg2); + } + if ($arg2 === null && $arg3 !== []) { + return $this->statement->setFetchMode($fetchMode, $arg2, $arg3); + } + + return $this->statement->setFetchMode($fetchMode); + } + + public function fetch($fetchMode = PDO::FETCH_ASSOC, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0) + { + return $this->statement->fetch($fetchMode, $cursorOrientation, $cursorOffset); + } + + public function fetchAll($fetchMode = PDO::FETCH_ASSOC, $fetchArgument = null, $ctorArgs = null) + { + if ($fetchArgument === null && $ctorArgs === null) { + return $this->statement->fetchAll($fetchMode); + } + if ($fetchArgument !== null && $ctorArgs === null) { + return $this->statement->fetchAll($fetchMode, $fetchArgument); + } + + return $this->statement->fetchAll($fetchMode, $fetchArgument, $ctorArgs); + } + + public function fetchColumn($columnIndex = 0) + { + return $this->statement->fetchColumn($columnIndex); + } + + public function bindValue($param, $value, $type = ParameterType::STRING) + { + return $this->statement->bindValue($param, $value, $type); + } + + public function bindParam($param, &$variable, $type = ParameterType::STRING, $length = null) + { + return $this->statement->bindParam($param, $variable, $type, $length); + } + + public function errorCode() + { + return $this->statement->errorCode(); + } + + public function errorInfo() + { + return $this->statement->errorInfo(); + } + + public function execute($params = null) + { + return $this->statement->execute($params); + } + + public function rowCount() + { + return $this->statement->rowCount(); + } + + public function getIterator(): Traversable + { + while (($result = $this->statement->fetch()) !== false) { + yield $result; + } + } + + /** + * @deprecated 最好不使用此方法,此方法可能存在 Bug + * @return mixed + */ + public function current() + { + if (method_exists($this->statement, 'current')) { + return $this->statement->current(); + } + return null; + } +} diff --git a/src/ZM/Store/MySQL/MySQLStatementWrapper.php b/src/ZM/Store/MySQL/MySQLStatementWrapper.php new file mode 100644 index 00000000..9304f478 --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLStatementWrapper.php @@ -0,0 +1,239 @@ +stmt = $stmt; + } + + /** + * 获取结果的迭代器 + * wrapper method + * @return ResultStatement + */ + public function getIterator() + { + return $this->stmt->getIterator(); + } + + /** + * 获取列数 + * wrapper method + * @return int + */ + public function columnCount() + { + return $this->stmt->columnCount(); + } + + /** + * wrapper method + * @throws MySQLException + * @return array|false|mixed + */ + public function fetchNumeric() + { + try { + return $this->stmt->fetchNumeric(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + * @return array|false|mixed + */ + public function fetchAssociative() + { + try { + return $this->stmt->fetchAssociative(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + * @return false|mixed + */ + public function fetchOne() + { + try { + return $this->stmt->fetchOne(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function fetchAllNumeric(): array + { + try { + return $this->stmt->fetchAllNumeric(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function fetchAllAssociative(): array + { + try { + return $this->stmt->fetchAllAssociative(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function fetchAllKeyValue(): array + { + try { + return $this->stmt->fetchAllKeyValue(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function fetchAllAssociativeIndexed(): array + { + try { + return $this->stmt->fetchAllAssociativeIndexed(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function fetchFirstColumn(): array + { + try { + return $this->stmt->fetchFirstColumn(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function iterateNumeric(): Traversable + { + try { + return $this->stmt->iterateNumeric(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function iterateAssociative(): Traversable + { + try { + return $this->stmt->iterateAssociative(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function iterateKeyValue(): Traversable + { + try { + return $this->stmt->iterateKeyValue(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function iterateAssociativeIndexed(): Traversable + { + try { + return $this->stmt->iterateAssociativeIndexed(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + */ + public function iterateColumn(): Traversable + { + try { + return $this->stmt->iterateColumn(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws MySQLException + * @return int + */ + public function rowCount() + { + try { + return $this->stmt->rowCount(); + } catch (Throwable $e) { + throw new MySQLException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + */ + public function free(): void + { + $this->stmt->free(); + } +} diff --git a/src/ZM/Store/MySQL/MySQLWrapper.php b/src/ZM/Store/MySQL/MySQLWrapper.php new file mode 100644 index 00000000..1314a0a6 --- /dev/null +++ b/src/ZM/Store/MySQL/MySQLWrapper.php @@ -0,0 +1,597 @@ +connection = DriverManager::getConnection(['driverClass' => MySQLDriver::class, 'dbName' => $name]); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + public function __destruct() + { + $this->connection->close(); + } + + /** + * wrapper method + */ + public function getDatabase(): string + { + return $this->connection->getDatabase(); + } + + /** + * wrapper method + */ + public function isAutoCommit(): bool + { + return $this->connection->isAutoCommit(); + } + + /** + * wrapper method + */ + public function setAutoCommit(bool $auto_commit) + { + $this->connection->setAutoCommit($auto_commit); + } + + /** + * wrapper method + * @throws DbException + * @return array|false + */ + public function fetchAssociative(string $query, array $params = [], array $types = []) + { + try { + return $this->connection->fetchAssociative($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws DbException + * @return array|false + */ + public function fetchNumeric(string $query, array $params = [], array $types = []) + { + try { + return $this->connection->fetchNumeric($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * @throws DbException + * @return false|mixed + */ + public function fetchOne(string $query, array $params = [], array $types = []) + { + try { + return $this->connection->fetchOne($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + */ + public function isTransactionActive(): bool + { + return $this->connection->isTransactionActive(); + } + + /** + * @param string $table 表 + * @throws DbException + */ + public function delete(string $table, array $criteria, array $types = []): int + { + try { + return $this->connection->delete($table, $criteria, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param int $level Sets the transaction isolation level + */ + public function setTransactionIsolation(int $level): int + { + return $this->connection->setTransactionIsolation($level); + } + + /** + * wrapper method + */ + public function getTransactionIsolation(): ?int + { + return $this->connection->getTransactionIsolation(); + } + + /** + * wrapper method + * @param string $table 表名 + * @throws DbException + */ + public function update(string $table, array $data, array $criteria, array $types = []): int + { + try { + return $this->connection->update($table, $data, $criteria, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $table 表名 + * @throws DbException + */ + public function insert(string $table, array $data, array $types = []): int + { + try { + return $this->connection->insert($table, $data, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $str The name to be quoted + */ + public function quoteIdentifier(string $str): string + { + return $this->connection->quoteIdentifier($str); + } + + /** + * wrapper method + * @param mixed $value + * @param null|int|string|Type $type + */ + public function quote($value, $type = ParameterType::STRING) + { + return $this->connection->quote($value, $type); + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return array> + */ + public function fetchAllNumeric(string $query, array $params = [], array $types = []): array + { + try { + return $this->connection->fetchAllNumeric($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return array> + */ + public function fetchAllAssociative(string $query, array $params = [], array $types = []): array + { + try { + return $this->connection->fetchAllAssociative($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + */ + public function fetchAllKeyValue(string $query, array $params = [], array $types = []): array + { + try { + return $this->connection->fetchAllKeyValue($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return array> + */ + public function fetchAllAssociativeIndexed(string $query, array $params = [], array $types = []): array + { + try { + return $this->connection->fetchAllAssociativeIndexed($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return array + */ + public function fetchFirstColumn(string $query, array $params = [], array $types = []): array + { + try { + return $this->connection->fetchFirstColumn($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return Traversable> + */ + public function iterateNumeric(string $query, array $params = [], array $types = []): Traversable + { + try { + return $this->connection->iterateNumeric($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return Traversable> + */ + public function iterateAssociative(string $query, array $params = [], array $types = []): Traversable + { + try { + return $this->connection->iterateAssociative($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return Traversable + */ + public function iterateKeyValue(string $query, array $params = [], array $types = []): Traversable + { + try { + return $this->connection->iterateKeyValue($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return Traversable> + */ + public function iterateAssociativeIndexed(string $query, array $params = [], array $types = []): Traversable + { + try { + return $this->connection->iterateAssociativeIndexed($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $query SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return Traversable + */ + public function iterateColumn(string $query, array $params = [], array $types = []): Traversable + { + try { + return $this->connection->iterateColumn($query, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $sql SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * + * @throws DbException + */ + public function executeQuery(string $sql, array $params = [], array $types = [], ?QueryCacheProfile $qcp = null): MySQLStatementWrapper + { + try { + $query = $this->connection->executeQuery($sql, $params, $types, $qcp); + return new MySQLStatementWrapper($query); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $sql SQL query + * @param array|array $params Query parameters + * @param array|array $types Parameter types + * @throws DbException + */ + public function executeCacheQuery(string $sql, array $params, array $types, QueryCacheProfile $qcp): MySQLStatementWrapper + { + try { + $query = $this->connection->executeCacheQuery($sql, $params, $types, $qcp); + return new MySQLStatementWrapper($query); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $sql SQL statement + * @param array|array $params Statement parameters + * @param array|array $types Parameter types + * + * @throws DbException + * @return int|string the number of affected rows + */ + public function executeStatement(string $sql, array $params = [], array $types = []) + { + try { + return $this->connection->executeStatement($sql, $params, $types); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + */ + public function getTransactionNestingLevel(): int + { + return $this->connection->getTransactionNestingLevel(); + } + + /** + * wrapper method + * @param null|string $name name of the sequence object from which the ID should be returned + * @return false|int|string a string representation of the last inserted ID + */ + public function lastInsertId(?string $name = null) + { + return $this->connection->lastInsertId($name); + } + + /** + * overwrite method to $this->connection->transactional() + * @throws DbException + * @return mixed + */ + public function transactional(Closure $func) + { + $this->beginTransaction(); + try { + $res = $func($this); + $this->commit(); + return $res; + } catch (Throwable $e) { + $this->rollBack(); + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws DbException + */ + public function setNestTransactionsWithSavepoints(bool $nest_transactions_with_savepoints) + { + try { + $this->connection->setNestTransactionsWithSavepoints($nest_transactions_with_savepoints); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + */ + public function getNestTransactionsWithSavepoints(): bool + { + return $this->connection->getNestTransactionsWithSavepoints(); + } + + /** + * wrapper method + */ + public function beginTransaction(): bool + { + return $this->connection->beginTransaction(); + } + + /** + * wrapper method + * @throws DbException + */ + public function commit(): bool + { + try { + return $this->connection->commit(); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws DbException + */ + public function rollBack(): bool + { + try { + return $this->connection->rollBack(); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $savepoint the name of the savepoint to create + * @throws DbException + */ + public function createSavepoint(string $savepoint) + { + try { + $this->connection->createSavepoint($savepoint); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $savepoint the name of the savepoint to release + * @throws DbException + */ + public function releaseSavepoint(string $savepoint) + { + try { + $this->connection->releaseSavepoint($savepoint); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @param string $savepoint the name of the savepoint to rollback to + * @throws DbException + */ + public function rollbackSavepoint(string $savepoint) + { + try { + $this->connection->rollbackSavepoint($savepoint); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws DbException + */ + public function setRollbackOnly() + { + try { + $this->connection->setRollbackOnly(); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * wrapper method + * @throws DbException + */ + public function isRollbackOnly(): bool + { + try { + return $this->connection->isRollbackOnly(); + } catch (Throwable $e) { + throw new DbException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * overwrite method to $this->connection->createQueryBuilder + */ + public function createQueryBuilder(): MySQLQueryBuilder + { + return new MySQLQueryBuilder($this); + } + + public function getConnection(): Connection + { + return $this->connection; + } +} diff --git a/tests/ZM/Utils/ZMUtilTest.php b/tests/ZM/Utils/ZMUtilTest.php index 8c5bc84d..0c79db43 100644 --- a/tests/ZM/Utils/ZMUtilTest.php +++ b/tests/ZM/Utils/ZMUtilTest.php @@ -1,10 +1,16 @@