PHP 8.0 默认内置 SQLite3 扩展,但连接成功需确保扩展已加载、路径真实可写且权限正确,并严格区分 exec()(用于 DDL/DML,返回布尔)与 query()(仅用于 SELECT,返回 SQLite3Result)。

PHP 8.0 默认启用 SQLite3 扩展,无需额外安装,直接 new SQLite3 就能连 —— 但路径、权限、错误处理这三处不注意,90% 的连接失败都发生在这儿。
确认 SQLite3 扩展已加载(不是可选,是前提)
PHP 8.0 虽默认包含 SQLite3 类,但扩展仍可能被禁用。不能只靠 class_exists('SQLite3') 判断,得看实际加载状态:
- 运行
php -m | grep sqlite(CLI)或访问phpinfo()页面,确认输出里有sqlite3模块(不是pdo_sqlite) - 检查
php.ini中extension=sqlite3未被注释,且没写成extension=php_sqlite3.dll(Windows 下旧写法已过时) - 如果用 Docker 或云环境,某些精简镜像会移除该扩展,需手动
docker-php-ext-install sqlite3
new SQLite3() 的路径必须真实存在且可写
传入的数据库文件路径不是“只要写对名字就行”,而是直接影响能否创建/打开:
- 相对路径如
'data.db'依赖当前工作目录(getcwd()),Web 请求中常为 Web 根目录,CLI 中则是执行命令的目录 —— 二者不一致就报Unable to open database - 绝对路径最稳,比如
/var/www/html/storage/app.db,但必须确保 PHP 进程用户(如www-data)对该目录有w权限 - 目录不存在会直接失败,SQLite 不会自动创建父级目录;文件不存在则自动创建,但前提是目录可写
- 别用
__DIR__ . '/db.sqlite'这种拼接 —— Windows 下反斜杠易出错,统一用dirname(__FILE__) . DIRECTORY_SEPARATOR . 'db.sqlite'或__DIR__ . '/db.sqlite'(PHP 7.4+ 支持正斜杠跨平台)
执行 SQL 时用 exec() 还是 query()?看返回值类型
这两个方法底层都走 sqlite3_exec() C 函数,但语义和返回值完全不同,混用会导致逻辑断裂:
立即学习“PHP免费学习笔记(深入)”;
-
exec():专用于 DDL(CREATE/DROP)和 DML(INSERT/UPDATE/DELETE),成功返回true,失败返回false—— 必须配合lastErrorMsg()查错 -
query():只用于SELECT和EXPLAIN,成功返回SQLite3Result对象,失败才返回false;遍历结果必须用fetchArray(),不能直接foreach - 误把
INSERT丢给query(),它会返回SQLite3Result(内部为空),但你不检查就继续读,fetchArray()会返回false,容易漏掉错误 - 事务控制要用
beginTransaction()+commit()/rollback(),它们内部调用的是exec(),所以也遵循相同返回规则
内存数据库 ':memory:' 的坑比想象中多
虽然 new SQLite3(':memory:') 看似简单,但它在 PHP 8.0 下有几处隐性限制,尤其在 Web 场景下极易误用:
- 字符串必须严格是
':memory:'—— 多空格(' :memory:')、少冒号('memory:')、加引号("':memory:'")全都会失败 - 每个
new实例完全独立,$_SESSION或static属性里存一个SQLite3对象,下次请求就失效 —— 它不跨请求,更不跨进程 - 不支持
PRAGMA journal_mode = WAL,调了也没效果;也不支持ATTACH,哪怕 attach 另一个':memory:'实例也会报错 - 适合单元测试或单次数据转换(如 CSV 导入临时查重),不适合替代缓存或 session 存储
真正麻烦的不是连不上,而是连上了却在 query() 后直接 foreach 结果,或者把 exec() 的返回当 SQLite3Result 用 —— 这些问题不会报致命错误,但数据就静默丢了。



















