根本原因是PHP底层OpenSSL/cURL拒绝验证未被信任的证书链,需将根CA证书正确注入系统或配置php.ini中curl.cainfo与openssl.cafile为同一绝对路径的完整PEM文件,并确保权限正确、顺序为叶证书→中间→根CA,而非禁用TLS或切换镜像。

根本不是 Composer 本身不支持自签名证书,而是 PHP 底层 OpenSSL/cURL 拒绝加载未被信任的证书链。必须把根 CA 证书正确注入系统或 PHP 配置,而不是关 TLS 或换镜像。
确认 PHP 实际使用的证书路径
别猜路径,直接让 PHP 自己说:
运行 php -r "print_r(openssl_get_cert_locations());",重点关注 default_cert_file 和 ini_cafile 的值。如果它们为空、指向不存在的文件、或文件大小为 0,就说明 PHP 根本没加载任何可信 CA。
接着用 ls -l /path/to/default_cert_file 检查该文件是否存在、是否可读。常见坑是权限设成 777(PHP 主动拒绝加载)、文件为空、或指向一个已损坏的 curl-ca-bundle.crt(尤其 Homebrew PHP 用户)。
把自签名根证书追加到系统 CA bundle
这是最稳定的做法,影响所有 PHP HTTPS 请求(composer、file_get_contents、cURL 都生效)。
确保你手上有 PEM 格式的根 CA 证书(以 -----BEGIN CERTIFICATE----- 开头,不是 .pfx 或 .der):
- Linux(Debian/Ubuntu):
sudo cp your-root.crt /usr/local/share/ca-certificates/,再执行sudo update-ca-certificates - CentOS/RHEL:
sudo cat your-root.crt >> /etc/pki/tls/certs/ca-bundle.crt,或用sudo trust extract-compat刷新 - macOS:双击
.crt→ “钥匙串访问” → 选“系统”钥匙串 → 右键证书 → “显示简介” → “信任” → 设为“始终信任” - Windows:双击
.crt→ “安装证书” → “本地计算机” → 存储位置选“受信任的根证书颁发机构”
为 Composer 单独指定 cafile(容器/CI 场景)
当无法修改系统证书库(比如 Alpine 容器、GitHub Actions runner),就让 Composer 显式用你提供的证书文件。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
把 PEM 格式证书(含完整链:叶证书 → 中间 → 根 CA)放到固定路径,例如 /certs/company-root.crt:
- 设全局配置:
composer config -g cafile /certs/company-root.crt - 注意:这个配置只对
composer命令生效,不影响 PHP 其他组件;且如果用了vendor/bin/composer,它默认忽略全局配置,得在项目composer.json的config段里写:"cafile": "/certs/company-root.crt"
别指望 composer config -g cafile 能修底层 TLS 握手失败——它只覆盖 Composer 自己封装的 HTTP 客户端,而 Git 克隆、插件调用、甚至部分包下载仍走原生 cURL/SSL 扩展。
为什么 openssl.cafile 和 curl.cainfo 必须同时配
PHP 内部 HTTPS 请求分两路:一部分走 cURL 扩展,一部分走 OpenSSL 扩展(比如 stream_context_create() 场景)。只设一个,另一路仍 fallback 到空信任链。
在 php.ini 中必须同时添加:
curl.cainfo = "/path/to/your/cacert.pem" openssl.cafile = "/path/to/your/cacert.pem"
路径必须是绝对路径,用英文双引号包裹,避免单反斜杠;改完后必须新开终端(CLI 模式不热加载),Apache/Nginx 必须完整重启服务(reload 不行)。
最容易被忽略的是:证书文件里必须包含完整的信任链(根 CA + 所有中间证书),顺序必须是叶证书在前、根 CA 在最后;否则即使路径对、权限对,OpenSSL 依然会报 unable to get local issuer certificate。

















