SQLite是一种嵌入式的关系型数据库,所有数据都存储在一个单一文件中,不需要独立的服务进程。在PHP中操作SQLite最常用的方式就是通过PDO扩展,它提供了统一的API接口,让开发者能够以一致的方式处理MySQL、PostgreSQL、SQLite等不同类型的数据库。使用PDO连接SQLite只需要构造正确的DSN字符串,再设置相应的错误模式,就能快速开始读写数据。

PDO连接SQLite的DSN构造与初始化
PDO连接数据库的核心是DSN(Data Source Name),它告诉PDO使用哪个驱动以及数据库的具体位置。对于SQLite,DSN以sqlite:开头,后面紧跟数据库文件的路径。如果文件不存在,SQLite默认会尝试新建一个空数据库,但这取决于目录的写入权限。例如,要连接当前目录下的app.db文件,可以这样写:
$dsn = 'sqlite:' . __DIR__ . '/app.db';
try {
$pdo = new PDO($dsn);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo '数据库连接成功';
} catch (PDOException $e) {
echo '连接失败:' . $e->getMessage();
}
这里使用了__DIR__常量来获取当前脚本所在目录,拼接出绝对路径,避免相对路径随工作目录变化导致找不到文件。同时,通过setAttribute方法将错误模式设置为PDO::ERRMODE_EXCEPTION,这样任何数据库操作出错时都会抛出异常,便于统一处理。如果不设置错误模式,PDO默认使用ERRMODE_SILENT,错误代码只能通过errorCode和errorInfo手动检查,容易遗漏问题。
除了文件数据库,SQLite还支持内存数据库,DSN直接写为sqlite::memory:。内存数据库在连接关闭后数据全部丢失,但读写速度极快,常用于单元测试或临时数据缓存。需要注意的是,每个new PDO('sqlite::memory:')都会创建一个独立的空数据库,不同连接之间无法共享数据。如果希望同一个内存数据库被多个PDO实例访问,需要使用PDO::ATTR_PERSISTENT持久连接,或者通过共享缓存方式连接。
使用预处理语句执行查询与写入
预处理语句是PDO提供的最重要的安全特性之一。它能够将SQL语句的结构与数据分离,从根本上避免SQL注入攻击。在SQLite中,预处理语句同样完全可用,而且因为SQLite是嵌入式数据库,预处理不会带来额外的网络开销,性能损失几乎可以忽略。下面是一个带参数的查询示例:
$stmt = $pdo->prepare('SELECT id, name, email FROM users WHERE age > :min_age');
$stmt->execute(['min_age' => 18]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
echo $row['name'] . ' - ' . $row['email'] . PHP_EOL;
}
在这个例子中,SQL语句中的:min_age是命名占位符。执行时传入一个关联数组,键名与占位符对应。PDO会自动对值进行转义和类型处理,即使传入的数据包含恶意内容,也不会改变SQL语句的逻辑。对于简单的查询,也可以使用问号占位符,但命名占位符在参数较多时可读性更好。
写入数据同样使用预处理语句。插入、更新和删除操作通常需要返回受影响的行数,可以通过execute方法的返回值或rowCount方法获取。以下代码展示了如何安全地插入一条用户记录:
$stmt = $pdo->prepare('INSERT INTO users (name, email, age) VALUES (:name, :email, :age)');
$stmt->execute([
'name' => '张三',
'email' => 'zhangsan@ipipp.com',
'age' => 25
]);
$lastId = $pdo->lastInsertId();
echo '新插入的记录ID:' . $lastId;
使用lastInsertId可以获取自增主键的值,这在后续需要关联表操作时非常有用。如果不需要主键,可以忽略该调用。值得注意的是,SQLite的lastInsertId只在同一连接内有效,且必须在插入后立即调用,否则可能被其他插入操作覆盖。
事务处理与错误排查
SQLite支持完整的ACID事务,PDO提供了beginTransaction、commit和rollBack三个方法来控制事务。事务在批量写入或多表关联更新时尤其重要,可以保证数据的一致性。下面是一个批量插入数据并回滚的示例:
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare('INSERT INTO logs (message, created_at) VALUES (?, ?)');
foreach ($logList as $log) {
$stmt->execute([$log['message'], $log['time']]);
}
$pdo->commit();
echo '全部日志写入成功';
} catch (Exception $e) {
$pdo->rollBack();
echo '事务回滚:' . $e->getMessage();
}
事务开启后,所有写操作都会在内存中暂存,直到调用commit才真正写入磁盘。如果中途出现异常,调用rollBack可以撤销所有未提交的修改。需要注意的是,SQLite在事务期间会锁定数据库文件,对于并发写入的场景需要合理控制事务持续时间,避免锁等待。
在使用PDO连接SQLite时,有几个常见的错误需要留意。第一个是驱动未启用,可以通过命令php -m查看输出中是否包含pdo_sqlite模块。Windows环境下如果使用官方PHP安装包,该模块通常已内置,但Linux下可能需要单独安装php-sqlite3包。第二个错误是数据库文件所在目录没有写入权限,导致连接时无法创建或修改文件。第三个是路径错误,例如在Windows上使用反斜杠路径时,应先确保字符串正确转义或使用正斜杠。例如,C:\\sqlite\\app.db在PHP字符串中应写成'C:\sqlite\app.db'或使用__DIR__拼接。
SQLite特有的高级用法与优化
SQLite虽然轻量,但也可以通过PRAGMA语句调整数据库行为。例如,设置journal_mode=WAL可以启用预写日志模式,提高并发读性能并减少锁冲突。设置synchronous=NORMAL可以在保证安全的前提下提升写入速度。这些PRAGMA语句可以通过PDO的exec方法执行:
$pdo->exec('PRAGMA journal_mode = WAL');
$pdo->exec('PRAGMA foreign_keys = ON');
第一条语句将日志模式切换为WAL,第二条开启外键约束。SQLite默认不启用外键约束,这意味着即使插入无关联的无效外键值也不会报错。在需要强数据完整性的场景下,建议每次连接后都执行PRAGMA foreign_keys = ON。此外,SQLite的并发读取能力较强,但写入是串行的,单个进程同一时间只能有一个写操作。如果应用面临高并发写入,可以考虑将数据分片到多个SQLite文件,或者使用其他数据库作为替代。
对于小型Web应用或本地工具,SQLite通常足够应对日常需求。PDO的抽象层让开发者可以随时切换数据库,只要保持SQL语句的兼容性即可。掌握PDO连接SQLite的基础知识和常见陷阱,能够显著提升开发效率,减少不必要的调试时间。建议在项目初期就统一使用异常错误模式,并始终使用预处理语句,这样代码既安全又易于维护。