phpenv 不管理扩展,LevelDB 扩展需为每个 PHP 版本单独编译:必须用同一份 LevelDB 源码编译动态库,并用当前 phpenv 版本的 php-config 配置扩展,确保 leveldb.so 落入对应 extension_dir,且 PHP 进程有目录读写权限、显式启用 create_if_missing,存取需手动处理序列化与空字节。

phpenv 本身不管理扩展,它只管 PHP 版本切换;LevelDB 扩展必须为每个 PHP 版本单独编译安装,且不能靠 pecl install leveldb(因为官方 PECL 没收录该扩展)。
为什么 phpenv 下装 leveldb 容易失败
常见错误是:用系统自带的 libleveldb-dev + phpenv 切换的 PHP 版本混搭编译,导致 undefined symbol: _ZN7leveldb2DB4Open —— 这是 ABI 不兼容的典型报错,不是代码问题,是底层库和扩展二进制对不上。
- 必须用同一份 LevelDB 源码(推荐
git clone https://github.com/google/leveldb)编译出.so,再用它去编译php-leveldb扩展 -
phpenv切换版本后,php-config路径会变,./configure --with-php-config=...必须指向当前激活版本的php-config(如~/.phpenv/versions/8.2.12/bin/php-config) - 别用
sudo make install往系统目录写,而是确保make install输出的leveldb.so落在当前 PHP 版本的extension_dir下(可用php -i | grep extension_dir查)
给 phpenv 管理的 PHP 版本装 leveldb.so 的实操步骤
假设你已用 phpenv 安装了 PHP 8.2.12,并执行了 phpenv shell 8.2.12:
- 先装 LevelDB 动态库:
git clone https://github.com/google/leveldb<br>cd leveldb<br>cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON .<br>make && sudo make install
(这会把libleveldb.so放进/usr/local/lib) - 再装 PHP 扩展:
git clone https://github.com/reeze/php-leveldb<br>cd php-leveldb<br>~/.phpenv/versions/8.2.12/bin/phpize<br>./configure --with-leveldb=/usr/local --with-php-config=~/.phpenv/versions/8.2.12/bin/php-config<br>make && make install
- 确认
leveldb.so已落进正确目录:ls $(php -r "echo ini_get('extension_dir');")/leveldb.so - 编辑当前版本的配置文件(
~/.phpenv/versions/8.2.12/etc/php.ini),加一行:extension=leveldb.so
new LevelDB() 在 phpenv 环境下仍静默失败?检查这三点
即使扩展加载成功,new LevelDB("/path/to/db") 仍可能构造失败且不报错——这是 C 层直接 segfault 或返回 NULL 导致的。
立即学习“PHP免费学习笔记(深入)”;
-
/path/to/db必须是**已存在、可写、不带尾部斜杠的目录**(/tmp/mydb/❌,/tmp/mydb✅) - PHP 进程用户(CLI 下是当前用户,FPM 下常是
www-data或nginx)必须对目录有读写权限:chmod 755 /tmp/mydb && chown $USER:$USER /tmp/mydb - 必须显式传
create_if_missing => true,否则路径不存在时直接失败:new LevelDB("/tmp/mydb", ["create_if_missing" => true])
put()/get() 行为差异:不是“存什么取什么”
LevelDB 底层只认字节流,PHP 扩展不做自动序列化。这意味着:
-
$db->put("k", 123)存的是字符串"123",不是整数二进制;想存时间戳得自己pack("Q", time()) -
$db->get("missing")返回false,不是null或空字符串;别用empty()判定键是否存在 -
$db->delete("key")总是返回true,哪怕 key 根本没存过——它不反馈是否真删到了 - key 或 value 中含
\0(空字节)可能被 C 层截断,务必提前过滤或 base64 编码
最易忽略的点:批量写必须用 LevelDBWriteBatch,单条 put() 循环一万次,性能比 batch 低一个数量级——这不是 PHP 层能优化的,是 LevelDB WAL 和刷盘机制决定的。



















