引言:
PHP 调用 SQLite 数据库主要通过 SQLite3 扩展和 PDO_SQLITE 驱动实现。本文系统讲解环境配置、两种连接方式、数据增删改查、预处理语句防护 SQL 注入、事务处理、内存数据库及错误处理机制,帮助 PHP 开发者快速掌握轻量级嵌入式数据库的使用方法。

PHP 的 SQLite 数据库调用完全指南

SQLite 是一种轻量级的嵌入式关系型数据库引擎,与 PHP 配合使用非常便捷。它不像 MySQL 或 PostgreSQL 那样需要独立的服务器进程,而是将整个数据库存储为一个普通的文件,通过直接读写该文件来完成所有数据库操作。PHP 原生内置了对 SQLite 的支持,无需额外安装复杂的数据库服务,这使得它非常适合小型 Web 应用、移动应用后台、嵌入式系统以及数据分析和缓存等场景。

本文将系统讲解 PHP 中操作 SQLite 数据库的完整流程,涵盖环境配置、两种主流扩展的使用、数据增删改查、预处理语句防护、事务处理以及错误处理机制。

一、环境准备:安装与启用 SQLite 扩展

在开始编码之前,需要确保 PHP 环境已经安装并启用了 SQLite 相关的扩展。

1. 确认扩展状态

PHP 的 SQLite3 扩展在大多数发行版中默认是启用的。可以通过 php -m 命令检查是否已加载:

1
php -m | grep -i sqlite

正常输出应包含 sqlite3pdo_sqlite,表示扩展已可用。

2. 各平台安装方式

Ubuntu/Debian 系统

1
sudo apt install php8.1-sqlite3

安装后扩展会自动启用。

Windows 系统
php.ini 配置文件中取消 extension=php_sqlite3.dll 前的注释符号(分号),保存后重启 Web 服务器。同时需要确保 libsqlite3.dll 文件在系统 PATH 路径中。

macOS 系统(使用 Homebrew)

1
2
3
brew install php@8.2
# PHP 通常已包含 sqlite3 扩展,若缺失则通过 pecl 安装
pecl install sqlite3

3. 验证扩展安装

创建一个包含 phpinfo(); 的 PHP 文件,在浏览器中访问,搜索 sqlite3pdo_sqlite 确认扩展已加载。

二、两种连接方式

PHP 连接 SQLite 主要有两种方式:SQLite3 扩展(面向对象,专为 SQLite 设计)和 PDO_SQLITE 驱动(数据库抽象层)。

方式一:SQLite3 扩展(原生,专为 SQLite 设计)

SQLite3 扩展是 PHP 官方为 SQLite 提供的原生接口,以面向对象的方式提供完整的 SQLite 操作能力。

特点

  • 无需额外依赖,PHP 默认启用
  • 专为 SQLite 优化,API 简洁直观
  • 适合 SQLite 专属项目

示例:创建连接与获取版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
// 连接到 SQLite 数据库(文件不存在时会自动创建)
$db = new SQLite3('test.db');

// 获取 SQLite 版本信息
$version = SQLite3::version();
echo "版本字符串: " . $version['versionString'] . "\n";
echo "版本号: " . $version['versionNumber'] . "\n";

// 也可以通过 SQL 查询获取版本
$version = $db->querySingle('SELECT SQLITE_VERSION()');
echo "SQL 查询版本: " . $version . "\n";

// 关闭连接(可选,脚本结束时自动关闭)
$db->close();
?>

SQLite 的连接字符串就是一个文件路径。如果文件不存在,SQLite 会自动创建它。:memory: 是特殊值,表示在内存中创建数据库,数据不会持久化保存。

方式二:PDO_SQLITE 驱动(数据库抽象层)

PDO(PHP Data Objects)提供了统一的数据库访问接口,使用 PDO_SQLITE 驱动可以让代码在多种数据库之间平滑切换。

示例:使用 PDO 连接 SQLite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
try {
// 创建 PDO 连接
$pdo = new PDO('sqlite:/path/to/database.db');

// 设置错误模式为异常
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 设置默认提取模式
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

echo "成功连接到 SQLite 数据库!";
} catch (PDOException $e) {
die("连接失败:" . $e->getMessage());
}

// 关闭连接(设置为 null)
$pdo = null;
?>

两种方式对比

特性 SQLite3 扩展 PDO_SQLITE
数据库支持 仅 SQLite 支持 12+ 种数据库
接口风格 面向对象 面向对象
预处理语句 支持(prepare/bindValue 支持(prepare/execute
命名参数 支持(:id 格式) 支持(:name 格式)
异常处理 需手动检查 原生支持 try-catch
适用场景 SQLite 专属项目 跨数据库项目或需要统一接口

三、核心数据库操作

以下操作以 SQLite3 扩展为主,同时给出 PDO 的对应写法。

1. 创建表与插入数据(exec)

exec 方法用于执行不返回结果集的 SQL 语句(如 CREATE、INSERT、UPDATE、DELETE)。

SQLite3 扩展示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
$db = new SQLite3('test.db');

// 创建表
$sql = "CREATE TABLE IF NOT EXISTS cars (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INT
)";
$db->exec($sql);

// 插入多条数据
$cars = [
['Audi', 52642],
['Mercedes', 57127],
['Skoda', 9000],
['Volvo', 29000],
['Bentley', 350000],
['Citroen', 21000],
['Hummer', 41400],
['Volkswagen', 21600]
];

foreach ($cars as $car) {
$db->exec("INSERT INTO cars (name, price) VALUES ('{$car[0]}', {$car[1]})");
}

echo "数据插入成功!";
$db->close();
?>

PDO 对应写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$pdo = new PDO('sqlite:test.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 创建表
$pdo->exec("CREATE TABLE IF NOT EXISTS cars (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INT
)");

// 插入数据
$pdo->exec("INSERT INTO cars (name, price) VALUES ('Audi', 52642)");
$pdo->exec("INSERT INTO cars (name, price) VALUES ('Mercedes', 57127)");

echo "数据插入成功!";
$pdo = null;
?>

2. 查询数据(query 与 fetchArray)

query 方法执行 SQL 查询并返回结果对象,通过 fetchArray 逐行获取数据。

SQLite3 扩展示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$db = new SQLite3('test.db');

// 执行查询
$result = $db->query('SELECT id, name, price FROM cars ORDER BY price DESC');

// 遍历结果
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
echo "ID: {$row['id']} - Name: {$row['name']} - Price: {$row['price']}\n";
}

// 也可以使用 SQLITE3_NUM 获取索引数组
$result = $db->query('SELECT * FROM cars');
while ($row = $result->fetchArray(SQLITE3_NUM)) {
echo "{$row[0]} {$row[1]} {$row[2]}\n";
}

$db->close();
?>

PDO 对应写法

1
2
3
4
5
6
7
8
9
10
11
<?php
$pdo = new PDO('sqlite:test.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->query('SELECT id, name, price FROM cars ORDER BY price DESC');
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "ID: {$row['id']} - Name: {$row['name']} - Price: {$row['price']}\n";
}

$pdo = null;
?>

3. 单值查询(querySingle)

querySingle 方法用于执行查询并直接返回第一行第一列的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$db = new SQLite3('test.db');

// 获取记录总数
$count = $db->querySingle('SELECT COUNT(*) FROM cars');
echo "共有 {$count} 条记录\n";

// 获取最高价格
$maxPrice = $db->querySingle('SELECT MAX(price) FROM cars');
echo "最高价格: {$maxPrice}\n";

$db->close();
?>

4. 更新与删除数据

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$db = new SQLite3('test.db');

// 更新数据
$db->exec("UPDATE cars SET price = 30000 WHERE name = 'Skoda'");
echo "更新成功\n";

// 删除数据
$db->exec("DELETE FROM cars WHERE name = 'Hummer'");
echo "删除成功\n";

$db->close();
?>

四、获取最后插入的 ID

当使用 INTEGER PRIMARY KEY 列时,SQLite 会自动为插入的行生成递增的 ID。可以通过 lastInsertRowID 方法获取最后插入行的 ID。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
$db = new SQLite3('test.db');

// 创建表
$db->exec("CREATE TABLE IF NOT EXISTS friends (
id INTEGER PRIMARY KEY,
name TEXT
)");

// 插入数据
$db->exec("INSERT INTO friends (name) VALUES ('Tom')");
$db->exec("INSERT INTO friends (name) VALUES ('Rebecca')");
$db->exec("INSERT INTO friends (name) VALUES ('Jim')");

// 获取最后插入的 ID
$lastId = $db->lastInsertRowID();
echo "最后插入的 ID 是: {$lastId}\n"; // 输出: 3

$db->close();
?>

五、预处理语句与 SQL 注入防护

预处理语句是防御 SQL 注入攻击的标准做法,也是构建安全应用的关键环节。

SQLite3 扩展中的预处理语句

SQLite3 扩展支持两种占位符语法:问号占位符命名占位符(以 : 开头)。

使用命名占位符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$db = new SQLite3('test.db');

// 准备预处理语句
$stmt = $db->prepare('SELECT * FROM cars WHERE id = :id');

// 绑定值(参数1:占位符,参数2:值,参数3:类型)
$stmt->bindValue(':id', 3, SQLITE3_INTEGER);

// 执行查询
$result = $stmt->execute();

// 获取结果
$row = $result->fetchArray(SQLITE3_ASSOC);
echo "ID: {$row['id']} - Name: {$row['name']} - Price: {$row['price']}\n";

$db->close();
?>

使用问号占位符

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$db = new SQLite3('test.db');

// 问号占位符
$stmt = $db->prepare('SELECT * FROM cars WHERE id = ?');
$stmt->bindValue(1, 1, SQLITE3_INTEGER); // 参数索引从 1 开始

$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_NUM);
echo "{$row[0]} {$row[1]} {$row[2]}\n";

$db->close();
?>

插入数据时使用预处理语句

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$db = new SQLite3('test.db');

$stmt = $db->prepare('INSERT INTO cars (name, price) VALUES (:name, :price)');
$stmt->bindValue(':name', 'Tesla', SQLITE3_TEXT);
$stmt->bindValue(':price', 75000, SQLITE3_INTEGER);
$stmt->execute();

echo "插入成功,ID: " . $db->lastInsertRowID() . "\n";

$db->close();
?>

始终检查 prepare 的返回值:如果 SQL 语句有语法错误,prepare 会返回 false。如果不检查就调用 execute(),会导致致命错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
$db = new SQLite3('test.db');

$stmt = $db->prepare('SELECT * FROM cars WHERE id = :id');
if ($stmt === false) {
die("预处理失败: " . $db->lastErrorMsg());
}

$stmt->bindValue(':id', 1, SQLITE3_INTEGER);
$result = $stmt->execute();

while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
print_r($row);
}

$db->close();
?>

PDO 预处理语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
$pdo = new PDO('sqlite:test.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// 使用命名参数
$stmt = $pdo->prepare('SELECT * FROM cars WHERE id = :id');
$stmt->execute([':id' => 3]);

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
print_r($row);
}

// 使用问号占位符
$stmt = $pdo->prepare('SELECT * FROM cars WHERE id = ?');
$stmt->execute([1]);

$pdo = null;
?>

六、事务处理

SQLite 支持 ACID 事务,保证数据的一致性和完整性。虽然 SQLite3 扩展没有专门的事务方法,但可以通过执行 SQL 语句来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
$db = new SQLite3('test.db');

try {
// 开始事务
$db->exec('BEGIN TRANSACTION');

// 执行多个操作
$db->exec("UPDATE cars SET price = price + 1000 WHERE name = 'Audi'");
$db->exec("UPDATE cars SET price = price + 500 WHERE name = 'Skoda'");

// 提交事务
$db->exec('COMMIT');
echo "事务提交成功\n";
} catch (Exception $e) {
// 回滚事务
$db->exec('ROLLBACK');
echo "事务回滚:" . $e->getMessage() . "\n";
}

$db->close();
?>

PDO 事务处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
$pdo = new PDO('sqlite:test.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

try {
$pdo->beginTransaction();

$pdo->exec("UPDATE cars SET price = price + 1000 WHERE name = 'Audi'");
$pdo->exec("UPDATE cars SET price = price + 500 WHERE name = 'Skoda'");

$pdo->commit();
echo "事务提交成功\n";
} catch (PDOException $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
echo "事务回滚:" . $e->getMessage() . "\n";
}

$pdo = null;
?>

七、错误处理

SQLite3 扩展错误处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$db = new SQLite3('test.db');

// 执行可能出错的 SQL
$result = $db->exec("INSERT INTO non_existent_table (name) VALUES ('test')");

if ($result === false) {
// 获取错误信息
echo "错误代码: " . $db->lastErrorCode() . "\n";
echo "错误信息: " . $db->lastErrorMsg() . "\n";
}

$db->close();
?>

PDO 错误处理(异常模式)

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
try {
$pdo = new PDO('sqlite:test.db');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$pdo->exec("INSERT INTO non_existent_table (name) VALUES ('test')");
} catch (PDOException $e) {
echo "错误代码: " . $e->getCode() . "\n";
echo "错误信息: " . $e->getMessage() . "\n";
}

$pdo = null;
?>

八、内存数据库

SQLite 支持在内存中创建数据库,数据不会持久化保存,适合临时缓存或测试场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// 使用 :memory: 作为数据库名称
$db = new SQLite3(':memory:');

$db->exec("CREATE TABLE temp (id INTEGER PRIMARY KEY, value TEXT)");
$db->exec("INSERT INTO temp (value) VALUES ('临时数据')");

$result = $db->query("SELECT * FROM temp");
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
print_r($row);
}

// 脚本结束时数据自动销毁
$db->close();
?>

九、字符转义

在拼接 SQL 语句时,建议使用预处理语句而非手动转义。如果确实需要手动转义,可以使用 escapeString 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$db = new SQLite3('test.db');

// 用户输入
$userInput = "O'Reilly"; // 包含单引号

// 转义字符串
$escaped = SQLite3::escapeString($userInput);

// 安全拼接
$db->exec("INSERT INTO cars (name, price) VALUES ('$escaped', 1000)");

$db->close();
?>

十、推荐的最佳实践

实践建议 说明
使用预处理语句 永远不要直接拼接用户输入到 SQL 中,使用预处理语句防止 SQL 注入
启用外键约束 执行 PRAGMA foreign_keys = ON 启用外键检查,保证数据完整性
使用 WAL 日志模式 执行 PRAGMA journal_mode=WAL 提升并发读写性能
生产环境关闭错误显示 使用 error_reporting(0) 避免暴露敏感信息
合理使用内存数据库 临时数据使用 :memory: 提升性能
及时关闭连接 脚本结束时调用 close()$pdo = null 释放资源

总结

PHP 与 SQLite 的组合因其零配置、开箱即用的特性,成为轻量级 Web 应用、移动应用后端和嵌入式系统的理想选择。

核心要点可归纳为:

  1. 选择扩展:SQLite3 扩展适合 SQLite 专属项目;PDO_SQLITE 适合跨数据库或需要统一接口的项目
  2. 安全第一:始终使用预处理语句防止 SQL 注入
  3. 零配置优势:无需安装数据库服务,数据库就是一个普通文件,便于部署和迁移
  4. 事务处理:使用事务保证数据一致性

掌握这些基础操作后,可以进一步学习 SQLite 的高级特性,如全文搜索(FTS5)、JSON 支持、窗口函数以及自定义聚合函数等,构建更强大的 Web 应用。