From 731e1d81dae7c4fa3ab88c777cfd290d1f285aa0 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Tue, 6 Jun 2023 20:23:19 +0800 Subject: [PATCH] update to 3.2.0, add zm_sqlite() (portable sqlite) --- src/Globals/global_defines_app.php | 3 ++ src/Globals/global_functions.php | 27 +++++++++++ src/ZM/Event/Listener/WorkerEventListener.php | 6 ++- src/ZM/Framework.php | 2 +- src/ZM/Store/Database/DBConnection.php | 46 ++++++++++++++++--- src/ZM/Store/Database/DBPool.php | 34 ++++++++++++++ src/ZM/Store/Database/DBWrapper.php | 25 ++++++---- 7 files changed, 126 insertions(+), 17 deletions(-) diff --git a/src/Globals/global_defines_app.php b/src/Globals/global_defines_app.php index 69ba36d9e..4e9016087 100644 --- a/src/Globals/global_defines_app.php +++ b/src/Globals/global_defines_app.php @@ -55,6 +55,9 @@ const LOAD_MODE_SRC = 0; // 从 src 加载 const LOAD_MODE_VENDOR = 1; // 从 vendor 加载 +const ZM_DB_POOL = 1; // 数据库连接池 +const ZM_DB_PORTABLE = 2; // SQLite 便携数据库 + /* 定义工作目录 */ define('WORKING_DIR', getcwd()); diff --git a/src/Globals/global_functions.php b/src/Globals/global_functions.php index d442c90f7..ea1404ba4 100644 --- a/src/Globals/global_functions.php +++ b/src/Globals/global_functions.php @@ -25,6 +25,7 @@ use ZM\Plugin\ZMPlugin; use ZM\Schedule\Timer; use ZM\Store\Database\DBException; +use ZM\Store\Database\DBPool; use ZM\Store\Database\DBQueryBuilder; use ZM\Store\Database\DBWrapper; use ZM\Store\KV\KVInterface; @@ -254,6 +255,32 @@ function sql_builder(string $name = ''): DBQueryBuilder return (new DBWrapper($name))->createQueryBuilder(); } +/** + * 获取一个便携 SQLite 操作类 + * + * @param string $name 使用的 SQLite 连接文件名 + * @param bool $create_new 是否在文件不存在时创建新的 + * @param bool $keep_alive 是否维持 PDO 对象以便优化性能 + * @throws DBException + */ +function zm_sqlite(string $name, bool $create_new = true, bool $keep_alive = true): DBWrapper +{ + return DBPool::createPortableSqlite($name, $create_new, $keep_alive); +} + +/** + * 获取便携 SQLite 操作类的 SQL 语句构造器 + * + * @param string $name 使用的 SQLite 连接文件名 + * @param bool $create_new 是否在文件不存在时创建新的 + * @param bool $keep_alive 是否维持 PDO 对象以便优化性能 + * @throws DBException + */ +function zm_sqlite_builder(string $name, bool $create_new = true, bool $keep_alive = true): DBQueryBuilder +{ + return zm_sqlite($name, $create_new, $keep_alive)->createQueryBuilder(); +} + /** * 获取 Redis 操作类 * diff --git a/src/ZM/Event/Listener/WorkerEventListener.php b/src/ZM/Event/Listener/WorkerEventListener.php index 65f82ebdb..4debb73e8 100644 --- a/src/ZM/Event/Listener/WorkerEventListener.php +++ b/src/ZM/Event/Listener/WorkerEventListener.php @@ -95,7 +95,7 @@ public function onWorkerStart999(): void }); // 注册各种池子 - $this->initConnectionPool(); + $this->initDBConnections(); // 加载用户代码资源 $this->initUserPlugins(); @@ -144,6 +144,7 @@ public function onWorkerStop999(): void if (is_a(config('global.kv.use', \LightCache::class), LightCache::class, true)) { LightCache::saveAll(); } + DBPool::resetPortableSQLite(); logger()->debug('{is_task}Worker 进程 #{id} 正在停止', ['is_task' => ProcessStateManager::isTaskWorker() ? 'Task' : '', 'id' => ProcessManager::getProcessId()]); if (Framework::getInstance()->getDriver()->getName() !== 'swoole') { @@ -260,9 +261,10 @@ private function dispatchInit(): void * * @throws DBException|RedisException */ - private function initConnectionPool(): void + private function initDBConnections(): void { DBPool::resetPools(); + DBPool::resetPortableSQLite(); RedisPool::resetPools(); } } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 61f530515..00ee162df 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -50,7 +50,7 @@ class Framework public const VERSION_ID = 720; /** @var string 版本名称 */ - public const VERSION = '3.1.14'; + public const VERSION = '3.2.0'; /** * @var RuntimePreferences 运行时偏好(环境信息&参数) diff --git a/src/ZM/Store/Database/DBConnection.php b/src/ZM/Store/Database/DBConnection.php index 1303edeea..8afb13c1a 100644 --- a/src/ZM/Store/Database/DBConnection.php +++ b/src/ZM/Store/Database/DBConnection.php @@ -8,25 +8,49 @@ use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\ParameterType; +use ZM\Store\FileSystem; class DBConnection implements Connection { + private int $db_type; + /** @var \PDO */ private object $conn; private $pool_name; - public function __construct($params) + public function __construct(private array $params) { - logger()->debug('Constructing...'); - $this->conn = DBPool::pool($params['dbName'])->get(); - $this->pool_name = $params['dbName']; + $this->db_type = $params['dbType'] ?? ZM_DB_POOL; + if ($params['dbType'] === ZM_DB_POOL) { + // 默认连接池的形式, + logger()->debug('Constructing...'); + $this->conn = DBPool::pool($params['dbName'])->get(); + $this->pool_name = $params['dbName']; + } elseif ($params['dbType'] === ZM_DB_PORTABLE) { + $connect_str = 'sqlite:{filename}'; + if (FileSystem::isRelativePath($params['filename'])) { + $params['filename'] = zm_dir(config('global.data_dir') . '/db/' . $params['filename']); + FileSystem::createDir(zm_dir(config('global.data_dir') . '/db')); + } + $table = [ + '{filename}' => $params['filename'], + ]; + // 如果文件不存在则创建,但如果设置了 createNew 为 false 则不创建,不存在就直接抛出异常 + if (!file_exists($params['filename']) && ($params['createNew'] ?? true) === false) { + throw new DBException("Database file {$params['filename']} not found!"); + } + $connect_str = str_replace(array_keys($table), array_values($table), $connect_str); + $this->conn = new \PDO($connect_str); + } } public function __destruct() { - logger()->debug('Destructing!!!'); - DBPool::pool($this->pool_name)->put($this->conn); + if ($this->db_type === ZM_DB_POOL) { + logger()->debug('Destructing!!!'); + DBPool::pool($this->pool_name)->put($this->conn); + } } /** @@ -126,4 +150,14 @@ public function getPoolName() { return $this->pool_name; } + + public function getDbType(): int + { + return $this->db_type; + } + + public function getParams(): array + { + return $this->params; + } } diff --git a/src/ZM/Store/Database/DBPool.php b/src/ZM/Store/Database/DBPool.php index 8e17fd44e..9ead3f99a 100644 --- a/src/ZM/Store/Database/DBPool.php +++ b/src/ZM/Store/Database/DBPool.php @@ -21,6 +21,11 @@ class DBPool */ private static array $pools = []; + /** + * @var array 持久化的便携 SQLite 连接对象缓存 + */ + private static array $portable_cache = []; + /** * 重新初始化连接池,有时候连不上某个对象时候可以使用,也可以定期调用释放链接 * @@ -43,6 +48,16 @@ public static function resetPools(): void } } + /** + * 重新初始化所有的便携 SQLite 连接(其实就是断开) + */ + public static function resetPortableSQLite(): void + { + foreach (self::$portable_cache as $name => $wrapper) { + unset(self::$portable_cache[$name]); + } + } + /** * 通过配置文件创建一个 MySQL 连接池 * @@ -180,4 +195,23 @@ public static function checkMysqlExtension() } } } + + /** + * 创建一个便携的 SQLite 处理类 + * + * @param string $name SQLite 文件名 + * @param bool $create_new 如果数据库不存在,是否创建新的库 + * @throws DBException + */ + public static function createPortableSqlite(string $name, bool $create_new = true, bool $keep_alive = true): DBWrapper + { + if ($keep_alive && isset(self::$portable_cache[$name])) { + return self::$portable_cache[$name]; + } + $db = new DBWrapper($name, ['dbType' => ZM_DB_PORTABLE, 'createNew' => $create_new]); + if ($keep_alive) { + self::$portable_cache[$name] = $db; + } + return $db; + } } diff --git a/src/ZM/Store/Database/DBWrapper.php b/src/ZM/Store/Database/DBWrapper.php index 5a1c636f0..0dd9b17a5 100644 --- a/src/ZM/Store/Database/DBWrapper.php +++ b/src/ZM/Store/Database/DBWrapper.php @@ -18,17 +18,25 @@ class DBWrapper * DBWrapper constructor. * @throws DBException */ - public function __construct(string $name) + public function __construct(string $name, array $options = []) { + // 初始化配置 + $db_type = $options['dbType'] ?? ZM_DB_POOL; try { - $db_list = config()->get('global.database'); - if (isset($db_list[$name]) || (is_countable($db_list) ? count($db_list) : 0) === 1) { - if ($name === '') { - $name = array_key_first($db_list); + if ($db_type === ZM_DB_POOL) { + // pool 为连接池格式 + $db_list = config()->get('global.database'); + if (isset($db_list[$name]) || (is_countable($db_list) ? count($db_list) : 0) === 1) { + if ($name === '') { + $name = array_key_first($db_list); + } + $this->connection = DriverManager::getConnection(['driverClass' => $this->getConnectionClass($db_list[$name]['type']), ...$options]); + } else { + throw new DBException('Cannot find database config named "' . $name . '" !'); } - $this->connection = DriverManager::getConnection(['driverClass' => $this->getConnectionClass($db_list[$name]['type']), 'dbName' => $name]); - } else { - throw new DBException('Cannot find database config named "' . $name . '" !'); + } elseif ($db_type === ZM_DB_PORTABLE) { + // portable 为sqlite单文件模式 + $this->connection = DriverManager::getConnection(['driverClass' => SQLiteDriver::class, 'filename' => $name, ...$options]); } } catch (\Throwable $e) { throw new DBException($e->getMessage(), $e->getCode(), $e); @@ -38,6 +46,7 @@ public function __construct(string $name) public function __destruct() { $this->connection->close(); + $this->connection->close(); } /**