代码 PHP 8.6 弃用 mb_ereg* 与 mb_split:Oniguruma 停维护后怎么排查、怎么迁到 PCRE /u

2026-09-24 21:12:23

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_* 不是改个函数名,下面几点需要逐条确认(部分差异建议自己写对比用例验证):

  1. 分隔符和 /upreg_* 的模式必须带分隔符,写 /.../u 才有 UTF-8 语义;不带 /u 是字节匹配,和原来的行为是两回事。

  2. \w\d\s 的含义会变。PHP 的 /u 会启用 PCRE2 的 UTF-8 与 Unicode 字符属性,\w 之类的简写类变成 Unicode 感知,字母数字的判定范围比 Oniguruma 下更宽或更窄都可能出现。凡是原来靠 \w 划边界的逻辑,都要拿真实数据回归一遍。

  3. \p{...} 的属性名不完全通用\p{L}\p{Han}\p{Hiragana} 这类在 PCRE2 里可用,但 Oniguruma 里的一些别名(例如 \p{Word})在 PCRE2 下是否等价、是否需要换成 \p{Xwd},需要自行验证。

  4. 替换串的转义规则不同preg_replace()$1 / \1 引用捕获组,替换文本里出现的 $\ 必须转义(preg_quote() 只处理模式,不处理替换串)。原来 mb_ereg_replace() 的替换串写法要整体改写。

  5. 非法 UTF-8 输入的处理。带 /upreg_* 遇到非法 UTF-8 会直接返回 falsePREG_BAD_UTF8_ERROR),不会像按字节匹配那样继续跑。入口处补 mb_check_encoding() 之类的校验,或显式处理失败分支。

  6. 全局选项没有对应物mb_regex_set_options() 设的 i/m/s/x 等全局开关,迁移后要落到每个模式的修饰符或 preg_* 的 flags 参数上,不要依赖某个隐式全局状态。

  7. 复杂模式。命名捕获组、递归、回溯控制这些语法两套引擎大体兼容但不完全等价,边界复杂的模式建议先补测试再换。

暂时不改的话

官方提供的 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 转为错误。
  • 五个新的保留标识符letnamespace(作为类常量名)、isreadonly(函数名)、_(常量与编译期别名),目标是在 PHP 9 移除。
  • 弃用的类型检查别名与函数is_double()is_integer()is_long()doubleval()strcoll()SORT_LOCALE_STRINGmetaphone()spl_classes()spl_object_hash()(改用 spl_object_id());mysqli::stmt_init()mysqli_get_charset()define()$case_insensitive 参数;$allow_stringfalse 时给 is_a() / is_subclass_of() 传字符串;从 finally 块返回(行为变更);在期望数组的位置传对象(涉及 array_walkdeflate_init / inflate_init、zlib / bzip2 流过滤器、mb_convert_variableshttp_build_query)。这些在 PHP 8.6 只是提示,移除目标是 PHP 9。

另有两条 8.6 的行为变化(php.watch/versions/8.6):array_filter()$mode 收到非法值时抛 ValueErrortrim()ltrim()rtrim()chop() 默认开始裁掉 Form-Feed(\f)。

复制全文 生成海报 PHP 正则 mbstring 迁移

推荐文章

程序员茄子在线接单