LoginSignup
20
8

【PHP8.4】全角スペースをtrimできるようになるぞー

Last updated at Posted at 2023-11-13

なんとなくRFCを見ていたらMultibyte for trim function mb_trim, mb_ltrim and mb_rtrimというRFCが投票に入っていました。

というわけで、以下はこのRFCの紹介です。

PHP RFC: Multibyte for trim function mb_trim, mb_ltrim and mb_rtrim

Introduction

PHPには、マルチバイトのtrim関数がありません。
preg_replace("/^\s+|\s+$/u", '', $string)で概ね想定した挙動になりますが、関数を予め用意しておくことでコードの可読性とわかりやすさを向上させることができるでしょう。
また、トリッキーになりがちなこの処理を標準化することができます。
この機能は多くのPHP開発者にとって有用であり、mbstringモジュールはこれで完成にまた一歩近づきます。

ユースケースのひとつとして、mb_ltrimによるBOMの削除が考えられます。

mb_ltrim($string, "\u{FEFF}\u{FFFE}");

Proposal

mb_trim関数を追加します。

function mb_trim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string {}

function mb_ltrim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string {}

function mb_rtrim(string $string, string $characters = " \f\n\r\t\v\x00\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}", ?string $encoding = null): string {}

以下の文字がtrimされます。

trim関数と同じ。

U+0020 半角スペース
U+0009 \t
U+000A \n
U+000B \v
U+000D \r

・あまり一般的でないせいか、trim関数では削除されなかった改頁。

U+000C \f

trim関数では削除されるけど正規表現\sでは削除されない。

U+0000 \0

・区切り文字カテゴリ。

U+0020 SPACE
U+00A0 NO-BREAK SPACE
U+1680 OGHAM SPACE MARK
U+2000 EN QUAD
U+2001 EM QUAD
U+2002 EN SPACE
U+2003 EM SPACE
U+2004 THREE-PER-EM SPACE
U+2005 FOUR-PER-EM SPACE
U+2006 SIX-PER-EM SPACE
U+2007 FIGURE SPACE
U+2008 PUNCTUATION SPACE
U+2009 THIN SPACE
U+200A HAIR SPACE
U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR
U+202F NARROW NO-BREAK SPACE
U+205F MEDIUM MATHEMATICAL SPACE
U+3000 IDEOGRAPHIC SPACE

・その他、正規表現\sに含まれる。

U+0085 NEXT LINE (NEL)
U+180E MONGOLIAN VOWEL SEPARATOR

なお、trim関数には存在する範囲指定..には対応しません。
理由としてはUNICODEの文字幅が非常に広く、検索が困難であり、また文字コードによってマッピングが異なるためです。
たとえばひらがなは、UTF-8では[あ-ゞ]、EUC-JPでは[あ-ゝゞ]、Shift_JISでは[あ-ん]となります。

Backward Incompatible Changes

ユーザランドで同名の関数を定義していた場合は動かなくなります。

Proposed PHP Version(s)

PHP8.x

RFC Impact

RFCの影響。

To SAPIs

SAPIに上記関数が追加されます。

To Existing Extensions

mbstringエクステンションに上記関数が追加されます。

To Opcache

Opcacheの変更はありません。

New Constants

新しい定数はありません。

php.ini Defaults

ini設定はありません。

Open Issues

実装チケット

Voting

投票期間は2023/11/02~2023/11/17、投票の2/3の賛成で可決されます。
この投票期間、RFCに書かれてない。

2023/11/13時点では賛成10反対0の賛成多数であり、おそらく受理されます。

Implementation

プルリクエスト

感想

trimの第二引数に全角スペース渡せばいいじゃない。

var_dump(trim(' 山', ' ')); // '山'
var_dump(trim(' あ', ' ')); // ''

' 山'はきちんと想定どおりにtrimされますが、' あ'のほうはどういうわけか'あ'も消滅し、空文字になってしまいました。
なんだこれバグか?

この''、実際は空文字ではなく2バイトある謎の''です。

trimの第二引数$charactersは全角に対応していません。
第二引数に渡した' 'は、全角スペースではなく'\xe3\x80\x80'と解釈されてしまいます。
また'あ'は'\xe3\x81\x82'です。
結果としてtrim(' あ', ' ')trim('\xe3\x80\x80\xe3\x81\x82', '\xe3\x80\x80')となって結果は'\x81\x82'となりますが、そんな文字は存在しないので空文字に見えるというわけです。

この第二引数$charactersにマルチバイト文字を渡せないという条件の罠、関数のドキュメントには全く記載がないんですよね。
注意喚起の記事などもほとんど見当たりません。
知ってる人にとっては当然だと思うかもしれませんが、そうでもない人もたくさん使っている言語がPHPです。
これ引っかかってる人それなりにいるのではなかろうか。

ところでRFCではmb_ltrimmb_rtrimには第三引数$encodingを渡すことができるのに、mb_trimに渡すことができないようになっています。
なんでだろうと思ったらプルリクには入ってました。
RFCの更新忘れのようです。
確認していませんが、もしかしたら他にも抜け漏れがあるかもしれませんね。
気付いたらプルリクでも送ってあげればきっと喜ばれることでしょう。
私は権限ないのでできませんが。
この記事では修正しています。

RFCではmb_trimが実装されるタイミングはPHP8.xとしか書かれていませんが、PHP8.3は既にフィーチャーフリーズしているので、おそらくPHP8.4での導入となるでしょう。
楽しみですね。

実装者は@youkidearitaiです。
ここ最近PHP本体のソースコード、特にmbstringに多くの貢献をしているすごい人です。
今後の活躍にも期待が持てますね。

20
8
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
8