208 lines
5.4 KiB
PHP
208 lines
5.4 KiB
PHP
<?php
|
|
/**
|
|
* 数据库驱动抽象层
|
|
*
|
|
* 支持 MySQL (PDO/MySQL) 和 SQLite3 (PDO/SQLite)。
|
|
* 在 config.php 中通过 DB_DRIVER 配置:'mysql' 或 'sqlite'
|
|
*
|
|
* 安装时用户可选择驱动:
|
|
* - MySQL: 需要配置 DB_HOST / DB_PORT / DB_NAME / DB_USER / DB_PASS
|
|
* - SQLite: 需要配置 DB_SQLITE_PATH(数据库文件路径)
|
|
*/
|
|
if (!defined('IN_APP')) exit('Forbidden');
|
|
|
|
class DbDriver
|
|
{
|
|
/** @var PDO|null 当前连接 */
|
|
private static $pdo = null;
|
|
|
|
/** @var string 驱动类型 'mysql' | 'sqlite' */
|
|
private static $driver = 'mysql';
|
|
|
|
/**
|
|
* 获取 PDO 实例(单例)。
|
|
* @return PDO
|
|
*/
|
|
public static function connect()
|
|
{
|
|
if (self::$pdo !== null) return self::$pdo;
|
|
|
|
self::$driver = defined('DB_DRIVER') ? DB_DRIVER : 'mysql';
|
|
|
|
try {
|
|
if (self::$driver === 'sqlite') {
|
|
$path = defined('DB_SQLITE_PATH') ? DB_SQLITE_PATH : (__DIR__ . '/../data/database.sqlite');
|
|
$dir = dirname($path);
|
|
if (!is_dir($dir)) mkdir($dir, 0755, true);
|
|
self::$pdo = new PDO('sqlite:' . $path);
|
|
self::$pdo->exec('PRAGMA journal_mode=WAL');
|
|
self::$pdo->exec('PRAGMA foreign_keys=ON');
|
|
} else {
|
|
// MySQL(默认)
|
|
$host = defined('DB_HOST') ? DB_HOST : 'localhost';
|
|
$port = defined('DB_PORT') ? DB_PORT : '3306';
|
|
$name = defined('DB_NAME') ? DB_NAME : 'mall';
|
|
$user = defined('DB_USER') ? DB_USER : 'root';
|
|
$pass = defined('DB_PASS') ? DB_PASS : '';
|
|
$charset = 'utf8mb4';
|
|
$dsn = "mysql:host={$host};port={$port};dbname={$name};charset={$charset}";
|
|
self::$pdo = new PDO($dsn, $user, $pass, [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
]);
|
|
}
|
|
} catch (PDOException $e) {
|
|
if (defined('DEBUG') && DEBUG) {
|
|
throw $e;
|
|
}
|
|
die('数据库连接失败,请检查配置。' . (DEBUG ? ' [' . $e->getMessage() . ']' : ''));
|
|
}
|
|
|
|
return self::$pdo;
|
|
}
|
|
|
|
/**
|
|
* 获取当前驱动类型。
|
|
*/
|
|
public static function getDriver()
|
|
{
|
|
return self::$driver;
|
|
}
|
|
|
|
/**
|
|
* 是否为 SQLite。
|
|
*/
|
|
public static function isSqlite()
|
|
{
|
|
return self::$driver === 'sqlite';
|
|
}
|
|
|
|
/**
|
|
* 是否为 MySQL。
|
|
*/
|
|
public static function isMysql()
|
|
{
|
|
return self::$driver === 'mysql';
|
|
}
|
|
|
|
/**
|
|
* 获取原始 PDO 对象。
|
|
*/
|
|
public static function pdo()
|
|
{
|
|
return self::connect();
|
|
}
|
|
|
|
/**
|
|
* 关闭连接。
|
|
*/
|
|
public static function close()
|
|
{
|
|
self::$pdo = null;
|
|
}
|
|
|
|
/**
|
|
* 执行 SQL 并返回影响行数。
|
|
*/
|
|
public static function exec($sql)
|
|
{
|
|
return self::connect()->exec($sql);
|
|
}
|
|
|
|
/**
|
|
* 查询并返回全部行。
|
|
*/
|
|
public static function queryAll($sql, $params = [])
|
|
{
|
|
$stmt = self::connect()->prepare($sql);
|
|
$stmt->execute($params);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* 查询并返回一行。
|
|
*/
|
|
public static function queryOne($sql, $params = [])
|
|
{
|
|
$stmt = self::connect()->prepare($sql);
|
|
$stmt->execute($params);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
/**
|
|
* 查询并返回单个值。
|
|
*/
|
|
public static function queryScalar($sql, $params = [])
|
|
{
|
|
$row = self::queryOne($sql, $params);
|
|
return $row ? reset($row) : null;
|
|
}
|
|
|
|
/**
|
|
* 获取最后插入 ID。
|
|
*/
|
|
public static function lastInsertId()
|
|
{
|
|
return self::connect()->lastInsertId();
|
|
}
|
|
|
|
/**
|
|
* 在事务中执行回调。
|
|
*/
|
|
public static function transaction(callable $callback)
|
|
{
|
|
$pdo = self::connect();
|
|
try {
|
|
$pdo->beginTransaction();
|
|
$result = $callback($pdo);
|
|
$pdo->commit();
|
|
return $result;
|
|
} catch (Exception $e) {
|
|
$pdo->rollBack();
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查表是否存在。
|
|
*/
|
|
public static function tableExists($table)
|
|
{
|
|
if (self::isSqlite()) {
|
|
$name = str_replace(self::getPrefix(), '', $table);
|
|
return self::queryScalar(
|
|
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?",
|
|
[$name]
|
|
) > 0;
|
|
} else {
|
|
$db = defined('DB_NAME') ? DB_NAME : '';
|
|
return self::queryScalar(
|
|
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=? AND table_name=?",
|
|
[$db, $table]
|
|
) > 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取表前缀。
|
|
*/
|
|
public static function getPrefix()
|
|
{
|
|
return defined('DB_PREFIX') ? DB_PREFIX : '';
|
|
}
|
|
|
|
/**
|
|
* 列出所有表名。
|
|
*/
|
|
public static function listTables()
|
|
{
|
|
if (self::isSqlite()) {
|
|
return self::queryAll("SELECT name FROM sqlite_master WHERE type='table'");
|
|
} else {
|
|
$db = defined('DB_NAME') ? DB_NAME : '';
|
|
return self::queryAll("SELECT table_name AS name FROM information_schema.tables WHERE table_schema=?", [$db]);
|
|
}
|
|
}
|
|
}
|