Composer 从不触发 C/C++ 编译,仅检查 PHP 扩展是否启用;C 扩展需手动安装系统依赖、编译扩展并配置 php.ini;Docker/CI 中须固化依赖声明与校验;FFI 调用 C++ 库需严格对齐符号、内存布局与类型。

Composer 从不触发 C/C++ 编译,它只检查 ext-* 是否启用
你写 "ext-gd": "*" 或 "ext-openssl": "^8.2",Composer 就只做一件事:调用 extension_loaded() 检查 PHP 是否已加载该扩展。它不会碰 gcc、不调 phpize、不下载 .so 文件,更不管理 libgd-dev 或 openssl-dev 这类系统库。
常见错误现象:
- 本地
composer install成功,但new \GDImage()报Class not found—— 实际是gd扩展没启用,或系统缺libpng-dev导致编译失败 - Docker 中只跑
composer install,没装apt-get install libjpeg-dev,结果ext-gd编译时 silently fallback 到无 JPEG 支持版本 -
composer.lock里锁了"ext-igbinary": "3.2.14",但宿主机根本没装igbinary扩展,PHP 启动直接报PHP Warning: Module 'igbinary' already loaded或 segfault
本地动态编译必须由外部工具链完成,不是 Composer 职责
要让 C 扩展在本地可用,你得自己走完这一整条链:
- 先装系统依赖:
sudo apt-get install libpng-dev libjpeg-dev libfreetype6-dev(Ubuntu)或brew install jpeg libpng freetype(macOS) - 再装 PHP 扩展本身:用
pecl install gd,或手动phpize && ./configure && make && sudo make install - 最后启用:确认
php.ini里有extension=gd.so,且没有重复加载 - 验证:运行
php -m | grep gd和php -r "var_dump(gd_info());"
注意:pecl install 不等于“自动适配”——它只是帮你跑一遍 configure,而 configure 脚本是否能找到头文件、链接库,完全取决于你提前装的系统 dev 包是否齐全、路径是否在 PKG_CONFIG_PATH 里。
立即学习“C++免费学习笔记(深入)”;
如何让本地编译行为可复现、可协作
靠人肉记忆或口头约定装什么库,迟早出问题。关键是要把编译前提固化下来:
- 在项目根目录放一个
.devcontainer.json或Vagrantfile,声明apt_packages/brew_packages - Dockerfile 中明确分层:
RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev ...必须在RUN pecl install gd之前 - CI 脚本里加校验步骤:
php -r "if (!extension_loaded('gd')) die('ext-gd missing');",别等跑业务逻辑才暴露 - 用
composer config platform.ext-gd "1.0"只能骗过依赖解析,不能让imagecreatefromjpeg()真工作——这是最常被误用的“快捷方式”
FFI 场景下,C++ 共享库的符号与内存布局必须手工对齐
如果你用 FFI::cdef() 直接调 C++ 库,那连 PHP 扩展这层封装都绕过了,责任全在你自己身上:
- C++ 函数必须包在
extern "C"里,否则nm -D libxxx.so看到的是_Z12my_funcv这种 mangled 名,FFI::cdef()找不到入口 - 结构体定义必须和 C++ 侧完全一致:字段顺序、
__attribute__((packed))、填充字节位置,差一个字节就可能读到乱码或 segfault -
FFI::cdef()声明里的int对应 C++ 的int32_t,不是long;char*要配合FFI::newString()传入,不能直接传 PHP 字符串 - 别在每次请求里 new FFI 实例——把
FFI::cdef()结果缓存为静态属性,否则开销远超调用本身
真正难的从来不是写几行 composer require,而是确保从 libssl-dev 到 ext-openssl 再到 FFI::load() 这整条链上,每个环节的 ABI、符号、内存布局都严丝合缝。漏掉任意一环,报错都不会告诉你具体哪错了,只会给你一个 segfault 或空数组。


















