php-src

其他

php/php-src

PHP 官方解释器,面向 Web 开发的脚本语言引擎。

项目简介

php-src 是 PHP 语言的参考实现,PHP 是一种流行的通用脚本语言,尤其适合 Web 开发。项目使用 C 语言编写,包含 Zend 引擎、大量内置扩展和完善的测试套件,驱动着从个人博客到全球大型网站的各种应用。

README 预览

\n    \n        \n    \n\n\n# The PHP Interpreter\n\nPHP is a popular general-purpose scripting language that is especially suited to\nweb development. Fast, flexible and pragmatic, PHP powers everything from your\nblog to the most popular websites in the world.\n\nPHP is distributed under the [Modified BSD License](LICENSE)\n(SPDX-License-Identifier: `BSD-3-Clause`).\n\n[](https://github.com/php/php-src/actions/workflows/test.yml)\n[](https://issues.oss-fuzz.com/issues?q=project:php)\n\n## Documentation\n\nThe PHP manual is available at [php.net/docs](https://www.php.net/docs).\n\n## Installation\n\n### Prebuilt packages and binaries\n\nPrebuilt packages and binaries can be used to get up and running fast with PHP.\n\nFor Windows, the PHP binaries can be obtained from\n[windows.php.net](https://windows.php.net). After extracting the archive the\n`*.exe` files are ready to use.\n\nFor other systems, see the [installation chapter](https://www.php.net/install).\n\n### Building PHP source code\n\n*For Windows, see [Build your own PHP on Windows](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2).*\n\nFor a minimal PHP build from Git, you will need autoconf, bison, and re2c. For\na default build, you will additionally need libxml2 and libsqlite3.\n\nOn Ubuntu, you can install these using:\n\n```shell\nsudo apt install -y pkg-config build-essential autoconf bison re2c libxml2-dev libsqlite3-dev\n```\n\nOn Fedora, you can install these using:\n\n```shell\nsudo dnf install re2c bison autoconf make ccache libxml2-devel sqlite-devel\n```\n\nOn MacOS, you can install these using `brew`:\n\n```shell\nbrew install autoconf bison re2c libiconv libxml2 sqlite\n```\n\nor with `MacPorts`:\n\n```shell\nsudo port install autoconf bison re2c libiconv libxml2 sqlite3\n```\n\nGenerate configure:\n\n```shell\n./buildconf\n```\n\nConfigure your build. `--enable-debug` is recommended for development, see\n`./configure --help` for a full list of options.\n\n```shell\n# For develo

常见问题 (5)

故障排除
如何修复 PHP file_get_contents 处理 chunked HTTP 响应失败时 filters.c/dechunk 过滤器出现的 'memory leaks detected' 问题?

这是 PHP HTTP stream wrapper 中一个已确认的 bug(可在 PHP 8.5.12 和 master 分支的 8.7.0-dev 上复现)。当响应使用 Transfer-Encoding: chunked 时,wrapper 会在头部解析期间创建一个 dechunk 过滤器。如果后续的某个头部行格式错误,错误路径(php_stream_url_wrap_http_ex() 中的 out: 标签)永远不会释放该过滤器,因为此时 stream 为 NULL,导致每次失败的请求泄漏 96 字节(80 字节的过滤器结构体 + 16 字节的 dechunk 数据)。修复方法是修补 ext/standard/http_fopen_wrapper.c 中的 out: 清理部分:

/* On the error path stream is NULL and the filter is never appended
 * to readfilters below, so it must be freed here. */
if (header_info.transfer_encoding != NULL && stream == NULL) {
    php_stream_filter_free(header_info.transfer_encoding);
    header_info.transfer_encoding = NULL;
}

将此代码添加到 efree(header_info.location); 之后(在 master 分支上约为第 1230 行)。该补丁已经过验证:复现测试报告 0 处泄漏,且格式正确的 chunked 响应不受影响。应用补丁后请从源码重新编译 PHP。如果无法重新编译,由于泄漏量很小(每次请求 96 字节)且仅在遇到格式错误的 chunked 响应时发生,因此在生产环境中禁用 report_memleaks 可以掩盖症状,但并不能修复泄漏本身。

来源 Issue #24508
故障排除
为什么 PHP 在将钩子属性传递给函数时,使用 opcache.jit=1201 会发生 segfault?

这是 PHP 中的一个 JIT 编译器 bug(GH-24398),影响优化级别 1 下的函数 JIT(opcache.jit=1201、1211、1101、1251)。当带有 get 钩子的属性作为参数传递给一个在运行时才解析的函数时(例如条件声明的函数,或从命名空间中以非限定名称调用的函数),JIT 会错误地处理 ZEND_FETCH_OBJ_FUNC_ARG,并在第二次调用时崩溃,表现为段错误(signal 11)或 ZEND_SEND_FUNC_ARG_SPEC_VAR_UNUSED_HANDLER 中的 UBSan 空指针偏移错误。修复方法:升级到包含 GH-24402 修复(提交 cfe77b82a6)的 PHP 版本。在此之前可采用的临时规避方案:设置 opcache.jit=disable,或切换到 opcache.jit=tracing,或使用不会触发该 bug 的 JIT 级别,例如 1202、1205 或 1254。

来源 Issue #24398
故障排除
为什么即使写入会话数据失败,session_write_close() 仍返回 true?

这是有意设计的、有文档记载的行为(参见 PHP 手册中 session_write_close() 的返回值部分)。该函数仅在没有活动会话时返回 false;在所有其他情况下均返回 true,包括当保存处理器的 write() 方法失败时。写入失败只会触发类似 "session_write_close(): Failed to write session data using user defined save handler" 的警告。

检测写入失败的变通方法:
1. 在自定义的 SessionHandlerInterface 实现中,记录你自己的 write() 方法的返回值(例如,将其存储在类属性中),并在调用 session_write_close() 之后检查它。
2. 使用错误处理器捕获该警告:

$writeFailed = false;
set_error_handler(function ($no, $msg) use (&$writeFailed) {
    if (str_contains($msg, 'Failed to write session data')) { $writeFailed = true; }
    return true;
});
session_write_close();
restore_error_handler();

不要依赖 session_write_close() 的返回值来检测写入错误。

来源 Issue #24561
故障排除
如何修复使用 opcache tracing JIT 时出现的 PHP 段错误(退出码 139)或 zend_jit_trace_exit 断言失败?

这是 PHP 的 tracing JIT 中一个已确认的 bug(GH-24389)。当热点 trace 中被内联的函数以引用绑定的变量被调用,且操作数类型发生变化(例如在迭代之间从 int 变为 float)时,在 ZEND_JIT_TRACE_ENTER 上为寄存器分配的被调用方 CV 生成的类型守卫没有 opline,因此 trace 退出时会读取未初始化的 EX(opline)。Release 构建会发生段错误(segfault);debug 构建会在 ext/opcache/jit/zend_jit_trace.c 中以 "zend_jit_trace_exit: Assertion ... opline ... failed" 中止。

修复方法:将 PHP 升级到包含提交 b4ca62e317(PR GH-24411)的构建版本,该提交已解决此崩溃问题。

如果无法升级,可采用以下变通方法:
1. 在保留 OPcache 的情况下禁用 JIT:使用 php -d opcache.enable_cli=1 -d opcache.jit=disable 运行,或在 php.ini 中设置 opcache.jit=disable。
2. 避免在值类型会跨调用变化的热点循环中使用引用别名($xr = &$x、foreach ($data as &$v)),例如先遍历 int 数组再遍历 float 数组的情况。

来源 Issue #24389
故障排除
为什么在另一次 serialize() 调用期间,从 __destruct() 中调用 PHP 的 serialize() 会导致段错误(segfault)或产生错误的反向引用(back references)?

根本原因:PHP 的 serialize() 不可重入。如果在 php_var_serialize_destroy() 正在销毁外层调用的 var_hash 时触发了析构函数(例如某个仅由 var_hash 保持存活的对象,或某个在 var_hash 释放时被回收的 GC 循环),且该析构函数调用了 serialize(),则嵌套调用会复用外层的 var_hash。后果:(1) 嵌套输出会得到错误的回溯引用(r:N 编号会从外层调用继续递增,例如输出 r:6; 而不是 r:2;);(2) 如果嵌套调用扩展了哈希表,外层的销毁过程会遍历已释放的内存 → use-after-free → Segmentation fault (exit 139),回溯栈为 zval_ptr_dtor → zend_hash_destroy → php_var_serialize_destroy → zif_serialize。已在 PHP 8.4.26 和 8.5.11(NTS,官方 php Docker 镜像)上确认;这很可能是测试套件中间歇性段错误的根本原因(例如 TYPO3/PHPUnit 中 GC 回收某个 DI 容器,而该容器的析构函数会序列化一个缓存条目)。在 PHP 核心修复落地之前的临时解决方案:1) 绝不要在 __destruct() 中调用 serialize()(或任何可能触发它的操作)——应改为延迟/缓存该工作。2) 在测试套件中,在 serialize() 之前立即调用 gc_collect_cycles();,使待处理的循环引用垃圾和析构函数在序列化上下文之外运行。3) 避免让对象仅通过正在被序列化的数据保持可达。复现方法:一个类的 __destruct() 对一个大数组调用 serialize(),且该类通过 __serialize() 在另一个对象内部被序列化,可稳定触发崩溃。

来源 Issue #24373