PHP 8.6 弃用 mb_ereg* 与 mb_split:Oniguruma 停维护后怎么排查、怎么迁到 PCRE /u
PHP 8.6 把 mbstring 的正则功能整体标记为弃用,PHP 9.0 移除。来源是 Zend 官方博客:PHP 8.6。
为什么是现在:Oniguruma 在 2025 年 4 月结束维护
mbstring 的正则部分不是 PHP 自己实现的,底层是 Oniguruma。Oniguruma 在 2025 年 4 月停止维护,PHP 项目无法再指望上游提供安全修复,所以把 mbstring 的正则功能整体弃用——包括 mb_ereg*() 系列和 mb_split(),并且在 PHP 9.0 移除。
对仍然依赖这套功能的代码,官方建了一个 PECL 包 mb_onig 作为兜底;但推荐做法是改用带 /u 修饰符的 PCRE 函数。
受影响的范围
按弃用范围,需要排查的至少包括:
mb_ereg()、mb_eregi()mb_ereg_replace()、mb_eregi_replace()、mb_ereg_replace_callback()mb_split()mb_ereg_search()及mb_ereg_search_*系列- 配套的
mb_regex_encoding()、mb_regex_set_options()(是否逐条列入弃用清单,需以对应 RFC / UPGRADING 文件为准,自行验证)
先排查:现有代码里还有多少处
按函数名扫一遍,注意 mb_ereg 会连带匹配到 mb_ereg_search_*:
grep -rnE '\bmb_(ereg|eregi|ereg_replace|eregi_replace|ereg_replace_callback|split|regex_encoding|regex_set_options)' --include='*.php' .
更省事的做法是让运行时自己报:在 PHP 8.6 下把 E_DEPRECATED 打开(error_reporting 里包含它,或直接 E_ALL),跑一遍测试套件,弃用提示会指到具体文件和行号。
迁移到 PCRE 的几个坑
把 mb_ereg* 换成 preg_* 不是改个函数名,下面几点需要逐条确认(部分差异建议自己写对比用例验证):
分隔符和
/u。preg_*的模式必须带分隔符,写/.../u才有 UTF-8 语义;不带/u是字节匹配,和原来的行为是两回事。\w、\d、\s的含义会变。PHP 的/u会启用 PCRE2 的 UTF-8 与 Unicode 字符属性,\w之类的简写类变成 Unicode 感知,字母数字的判定范围比 Oniguruma 下更宽或更窄都可能出现。凡是原来靠\w划边界的逻辑,都要拿真实数据回归一遍。\p{...}的属性名不完全通用。\p{L}、\p{Han}、\p{Hiragana}这类在 PCRE2 里可用,但 Oniguruma 里的一些别名(例如\p{Word})在 PCRE2 下是否等价、是否需要换成\p{Xwd},需要自行验证。替换串的转义规则不同。
preg_replace()用$1/\1引用捕获组,替换文本里出现的$和\必须转义(preg_quote()只处理模式,不处理替换串)。原来mb_ereg_replace()的替换串写法要整体改写。非法 UTF-8 输入的处理。带
/u的preg_*遇到非法 UTF-8 会直接返回false(PREG_BAD_UTF8_ERROR),不会像按字节匹配那样继续跑。入口处补mb_check_encoding()之类的校验,或显式处理失败分支。全局选项没有对应物。
mb_regex_set_options()设的i/m/s/x等全局开关,迁移后要落到每个模式的修饰符或preg_*的 flags 参数上,不要依赖某个隐式全局状态。复杂模式。命名捕获组、递归、回溯控制这些语法两套引擎大体兼容但不完全等价,边界复杂的模式建议先补测试再换。
暂时不改的话
官方提供的 PECL 包是 mb_onig,适合存量代码短期过渡。无论用哪种兜底方式,都要记得这批函数在 PHP 9.0 会被移除。
PHP 8.6 的其他弃用项
同一版本还有一批变更,数字与清单如下(phpnews.net 汇总):
- 限制 filter chain 数量:通过 Stream API 使用
php://filter可以串联多个过滤器,现在上限为 16,可通过filter.max_filter_count配置,目的是收敛 LFI/RCE 攻击面。 - 弃用
__construct()和__destruct()的返回值:PHP 8.6 给弃用提示,PHP 9.0 转为错误。 - 五个新的保留标识符:
let、namespace(作为类常量名)、is、readonly(函数名)、_(常量与编译期别名),目标是在 PHP 9 移除。 - 弃用的类型检查别名与函数:
is_double()、is_integer()、is_long()、doubleval();strcoll()与SORT_LOCALE_STRING;metaphone();spl_classes();spl_object_hash()(改用spl_object_id());mysqli::stmt_init()、mysqli_get_charset();define()的$case_insensitive参数;$allow_string为false时给is_a()/is_subclass_of()传字符串;从finally块返回(行为变更);在期望数组的位置传对象(涉及array_walk、deflate_init/inflate_init、zlib / bzip2 流过滤器、mb_convert_variables、http_build_query)。这些在 PHP 8.6 只是提示,移除目标是 PHP 9。
另有两条 8.6 的行为变化(php.watch/versions/8.6):array_filter() 的 $mode 收到非法值时抛 ValueError;trim()、ltrim()、rtrim()、chop() 默认开始裁掉 Form-Feed(\f)。