鬼車のメンテナンス終了と2025年現在のPHPのUnicode対応

鬼車のメンテナンス終了と2025年現在のPHPのUnicode対応

Cybozu Blog Fes 2025、てきめんの番です

この記事は、CYBOZU SUMMER BLOG FES '25 の記事です。

こんにちは。サイボウズではGaroonのYukimiチームにいるてきめん(@youkidearitai)です。PHPのコミッターもしており、mbstringやUnicodeの面倒を業務時間をちょっと頂きながらOSS活動をしています。

今回はPHPの状況についてお話します

今回のCYBOZU SUMMER BLOG FES '25では、そんなPHPの状況をお話します。

  • 鬼車のメンテナンス終了によるmb_ereg関数群(mbregex)の行方について
  • 追加したPHP 8.5の機能について

個人のブログである https://tekitoh-memdhoi.info でもお話しているのですが、今回はこの場を借りてもうちょい宣伝させてください。

鬼車のメンテナンス終了

今回はこれが強く主張したいことであると思っています。鬼車(Oniguruma、https://github.com/kkos/oniguruma)が2025年4月24日にPublic Archiveとなり、メンテナンスが終了しました。

PHPでは mb_ereg 関数をはじめとした、mbregexと呼ばれる関数群が影響を受けます。現在、廃止するか、メンテナンスを継続してでも使うかで揺れています。

なお、廃止する方向になるのであれば、PHP 8.6でDeprecated、PHP 9で廃止となるという予定を立てています (https://wiki.php.net/rfc/eol-oniguruma)。継続するのであれば、mbstringのもう一人のメンテナーであるAlexさんが言うような何処かで引き継ぐという案があります (https://github.com/php/php-src/pull/19258#issuecomment-3227175498)。

正直なところ、Garoonでもmb_ereg関数は使っている箇所がありますが、現在ではPCREで統一してもいいというふうな話もあり、廃止に持ってくなら持っていってもいいんじゃないか、というふうに社内でも話している最中であります。このように、古いコードではmb_eregはよく使われており、ここが悩みどころです。

皆さんの環境ではどうでしょうか。

PHP 8.5の機能

新たに grapheme_levenshtein 関数を追加しました。なお、 mb_levenshtein 関数が否決されました。

grapheme_levenshtein 関数

書記素クラスター単位でレーベンシュタイン距離 を取り、その結果をintegerで返す関数です。もともと、文字列(バイナリ)単位でレーベンシュタイン距離を取るlevenshtein 関数がありましたが、マルチバイト関数がありませんでした。

書記素クラスターとは

書記素クラスターとは、ざっくり言えば「見たままの文字」1文字を指します。例えば、🙆‍♂️は4コードポイント使っていますが、1文字に見えるかと思います(古いOSを使用してたり、対応していないエディタでは🙆と♂が見えるかもしれません)。また、絵文字のみならず、異体字セレクタのように、「細かい字の違い」を表現するのに使ったりします(例えば、渡邉さんの邉の字がいい例です)。

ここのあたりは過去に私てきめんが発表した資料もご参照いただけますと理解しやすくなるかと思いますので、ご参照いただけますと幸いです。

grapheme関数にロケールを追加

grapheme関数にはロケールを指定できるパラメーターがなかったため、ロケールを追加しました。なお、ロケールと言いましたが、実際にはもっと複雑な構文も追加できます。UnicodeではこれをLDML(Locale Data Markup Language)といいます。

https://www.unicode.org/reports/tr35/

ロケールというと、ja_JP とか en_US とかがあると思いますが、LDMLではもっと指定できる内容が増えます。grapheme関数で役に立つと思われる引数の例をご紹介します。

  • u-ks-identic
    • 例として ja_JP-u-ks-identic
    • 異体字セレクタを判別できる
  • u-ks-level1
    • 例として de_DE-u-ks-level1
    • 例えばドイツ語のエスツェット(ß)とssをマッチできる

これはマッチの強さ(match strength)といいます。最初、ロケールというとラテン語圏の話かな?と思っていたので、例えば発音ベースの関数などが候補に上がっていましたが、まさかの異体字セレクタでも影響があることから、全世界に影響があると主張してこの形に収めました。

使用例としてはこのようになります。

<?php
$nabe = '邊';
$nabe_E0101 = "邊󠄁";
var_dump(grapheme_levenshtein($nabe, $nabe_E0101)); // 0と計算される
var_dump(grapheme_levenshtein($nabe, $nabe_E0101, locale: "ja_JP-u-ks-identic")); // 1と計算される

なお、 grapheme_striposgrapheme_stristr ではマッチの強さは、元々 u-ks-level2 相当に強制されています。

ちなみに、ロケールを指定しなかった場合には、元々のロケールである「rootロケール」が使われます。昔からこの関数を使っている場合には後方互換性が保たれます。

まとめ

現在、PHPコミッターとして活動している部分と、その中で困っている部分を紹介させていただきました。フィードバックは https://github.com/php/php-src にもらえると嬉しいです。