$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
mb_trim関数を作りました
Search
てきめん tekimen
PRO
May 11, 2024
Programming
1
1.2k
mb_trim関数を作りました
PHPカンファレンス香川の資料です #phpconkagawa
てきめん tekimen
PRO
May 11, 2024
Tweet
Share
More Decks by てきめん tekimen
See All by てきめん tekimen
PHP 8.5の裏話
youkidearitai
PRO
0
100
CJK and Unicode From a PHP Committer
youkidearitai
PRO
0
220
PHP Internals わいわい #3 mb_*関数を作ってみよう
youkidearitai
PRO
0
110
Windows版php-srcデバッグ方法
youkidearitai
PRO
1
88
PHP Internals わいわい #1 の資料
youkidearitai
PRO
1
1.5k
PHPの次期バージョンはこの時期どうなっているのか - Internalsの開発体制について - PHPカンファレンス小田原
youkidearitai
PRO
1
950
文字とはなにか - PHPの文字コード処理について - PHP Lovers Meetup #5
youkidearitai
PRO
1
340
はじめてのOSSコントリビュート
youkidearitai
PRO
11
4.8k
文字とはなにか - PHPの文字コード処理について -
youkidearitai
PRO
0
1.5k
Other Decks in Programming
See All in Programming
「コードは上から下へ読むのが一番」と思った時に、思い出してほしい話
panda728
PRO
38
26k
Tinkerbellから学ぶ、Podで DHCPをリッスンする手法
tomokon
0
130
TUIライブラリつくってみた / i-just-make-TUI-library
kazto
1
390
非同期処理の迷宮を抜ける: 初学者がつまづく構造的な原因
pd1xx
1
720
AWS CDKの推しポイントN選
akihisaikeda
1
240
バックエンドエンジニアによる Amebaブログ K8s 基盤への CronJobの導入・運用経験
sunabig
0
160
Go コードベースの構成と AI コンテキスト定義
andpad
0
130
エディターってAIで操作できるんだぜ
kis9a
0
730
まだ間に合う!Claude Code元年をふりかえる
nogu66
5
840
ゲームの物理 剛体編
fadis
0
350
Giselleで作るAI QAアシスタント 〜 Pull Requestレビューに継続的QAを
codenote
0
190
20251212 AI 時代的 Legacy Code 營救術 2025 WebConf
mouson
0
170
Featured
See All Featured
Unsuck your backbone
ammeep
671
58k
The Invisible Side of Design
smashingmag
302
51k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
The Cost Of JavaScript in 2023
addyosmani
55
9.4k
Art, The Web, and Tiny UX
lynnandtonic
304
21k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.1k
The Pragmatic Product Professional
lauravandoore
37
7.1k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
1.8k
Balancing Empowerment & Direction
lara
5
800
Build your cross-platform service in a week with App Engine
jlugia
234
18k
The Art of Programming - Codeland 2020
erikaheidi
56
14k
BBQ
matthewcrist
89
9.9k
Transcript
mb_trimを作りました
自己紹介 てきめん • https://tekitoh-memdhoi.info • X: @youkidearitai • https://github.com/youkideari tai
• PHP 8.4で複数の関数を作った – mb_trim – mb_ucfirst、mb_lcfirst – grapheme_str_split オレ
遡ること2022年 • とあるFeature request(機 能追加のリクエスト)が来る • それがマルチバイト対応の trim関数を作ってくれない かというもの •
しかし、実装してくれる人が 現れず…
そのときのてきめんの状況 • 2022年8月はまだ8.1でmbstringが変わると認識 してなかった • 2022年12月からmbstringのレビュー作業に加 わった • mb_trimのfeature requestは認識してた
時は流れ • 2023年9月、ぼくがこの Feature requestを拾う – このころのぼく は、mbstringのレビュー も一通り落ち着いて余力 があったことが大きな要
因
実装するしかない! • もう自分でRFC書いた ほうがいいよと言われる • 皆もmake sense(理に かなってる)と言ってる 以上、もうしない理由が なかった
RFCの草案をもらう • 8ctopusさんが草案を書い てくれてた • こちらを参考に、ちょっと修 正 – mb_ltrimとmb_rtrimを追 加
– $encodingを追加
PHP Internalsへ投稿 • 8ctopusさんが internalsメーリングリ ストへメールを送れな かったことから、代理で 「ぼくもほしいのでどう 思う?」と送ってみた
PoCの作成 • 概念実証コードを作成 し、プルリクエストにしま した – この時点では、Draft pull requestとしました
RFCを記述するWiki Karma Request • RFCを記述するのが初めてだったので、まずアカウントを作り ます • アカウントを作ったら、Internalsメーリングリストへ「RFC Karma Request」を投げます
– タイトルはこのままで「〇〇したいです」を添えれば大体許可さ れます – 許可されたらWikiの編集権をもらえます
RFCの作成 • RFCを作成していきます • Wikiなので、空いてるURLに記述できます – 今回は https://wiki.php.net/rfc/mb_trim としました •
先程の中身を記述していきます
RFCの考慮 • RFCは他人にわかるようにいろいろなことを考えま す • 一番考えたのは$encodingの部分でした – trim関数には「..」という、範囲を指定する表記法がある のですが、これを実現するのは不可能に思いました •
というのも、文字コードごとに範囲が違いますし、Unicodeが 広すぎるのも問題です
RFCをInternalsメーリングリストへ送信 • メーリングリストへ送信して、議論します • この状態になったら、RFCを「Under Discussion」 にします – Under Discussionを少なくとも2週間続けます
• ただ、議論のすべてに答えることが必要でその合意形成に1 年とか経ったりするものもたくさんあったりします
Votingに入る • 投票フェーズに入ります • 期間は2週間+αを置きます • ここで2/3のYesを貰えれば可決(Accepted)となり ます • mb_trimはここで可決をもらいました
そして実装へ • 先程のプルリクエストを普通のプルリクエストにし ます • レビューを受けます – ここが結構おもしろかった
めっちゃアツいレビュー • 主にnielsdosさ ん、alexdowadさんか らレビューを受ける
128コードポイント以上の対応 • $stringが128コードポ イント以上の処理に対 応してないよとレビュー を受ける – そういえばそうだった
解決策が思い浮かばない、寝る • 色々と試行錯誤してた けどむずくて無理!JST だともう寝る時間だ寝 るってなっててnielsdos さんに教えを請うすが た – 似たような実装で解決
コードポイントって何 • Unicodeにおけるコードポイントとは、U+xxxxの xxxxの部分で、0x0から0x10FFFFの16進数が入る – https://www.unicode.org/glossary/#code_point – 何らかの文字がここに割り当てられるのでこれを1文字 として数える事が多い
線形探索以外になんかあるの!? • $charactersを1コードポイ ントずつ線形(O(N))に探索 してたらここはHashTable を使えばいいよと教えを請 う – 最初、どういうこと?ってなっ てnielsdosさんに聞いてる
ぼく
HashTableでO(1)にする • HashTableのキー部分に コードポイントをあてがうと いうテクニック(値はなん でもいい)でO(1)にすると いうテクニックを教えても らった – すげえ!ってなった
ハッシュテーブル(HashTable)ってなに • キーにハッシュ関数を与えてあげて、一意 のアドレスに値を格納・取り出すデータ構 造 – ハッシュ関数というくらいですから、md5と かsha1とかでもいいのです • 衝突を気にしなければO(1)で済ませられ
る – 衝突が絡むとO(N)になったりするけど今 回は衝突を考える必要はなかった • php-src では HashTable という構造体 が用意されている
さっきから言ってるO(N)とかってなに • 計算量、オーダーのこと • O(N)はN個あったら最悪N回計算し ないといけないことを示す – 0個では0回 – 線形探索がこれ
• O(1)は何個あっても1回で済む – 0個でも1回だけど – ハッシュテーブルはこっち
XORによるビット演算のテクニック • 左側のtrimが終わった らフラグをfalseにして たけど、mode自体を XORすればフラグ消せ るよと教えられる – すげー!ってなりました
ついにapprovedをもらう • nielsdosさんによるapproved をもらう – すごくお世話になりましたマジで ありがとうございます • alexdowadさんに「squashし てもらっていい?」ということで
git rebase -i とgit push – forceする – php-srcではsquashでひとまと めにしてます
Landed on master • NEWSの追 加、UPGRADINGの追 加などをしてマージと いうかsquash commitされました
Optimizations for mb_trim #12803 • nielsdosさんがmb_trim 関数のパフォーマンス調 整をしてくれて、 4文字 (コードポイント)までは線
形探索したほうが速いとわ かった – O(1)が0個でも1回計算し なきゃならないところもあり うる
Build failed at mbstring_arginfo.h on Windows(Visual C++) #13789 • Windowsの日本語のVisual
C++でビルドができないという 問題を(あの)廣川さんから メールでもらう – $charactersをUTF-8バイト列 でコンパイルしていたのが問 題 – コンパイル時のフラグにUTF- 8であることを加えることで対 処
mb_trim() inaccurate $characters default value #13815 • mb_trim関数群で文字コードが UTF-8じゃないときにtrimしない というIssue
– inaccurateが「正しくない」なの で、最初なんのことかわからな かった… – 結局、$charactersを決め打ちし てるのがよくないので、NULLが 正しいのでは?となる
Fix GH-13815: mb_trim() inaccurate $characters default value #13820 • nielsdosさんによるプルリク
エスト – $charactersをNULLにしま しょうというプルリクエスト – ただし、RFCの内容を書き換 えるためPHP Internalsで議 論する必要になり議論を行っ た
mb_trimのデフォルト値変えてもいい? • PHP Internalsメーリングリストで、mb_trimのデフォルト 値を変えてもいいかと質問してみる • 変えるしかないならそれでいいのではという意見 • RFCは必要ないとの意見をもらう •
ぼくは色々ごちゃごちゃ動いてた – mb_trimのRFC書き換えて怒られた(?)り、戻したり
結果として書き換わりました • $charactersはデフォルト値NULLとなり、NULLのとき にRFCで決まっていたコードポイントを削除するように なりました • mb_trim($str, encoding: “Shift_JIS”); などのときに
正しく削除できるようになりました • こういう設計ミスがあったときに参考にしてもらえれば
使い方 • mb_[lr]?trim関数はいわゆる全角スペース(U+3000)もtrimできます – PCREで言うところの\sなどをサポートしています • ユースケースの一つとして、mb_ltrim関数を使ってUTF-8 BOMを削除できます – mb_ltrim($str,
“\u{FEFF}”); • 例えばmb_substrでZero Width Joiner(ZWJ)を含む絵文字などでZWJが残ったと き、mb_rtrim関数を使ってZWJを削除できます – mb_substr(mb_rtrim(“ ”, “\u{200D}”), 0, 2); 🙇♂️ • 複数の文字(コードポイント)にも対応してます – mb_trim($str, “ \n\r\u{200D}\u{FEFF}”); • もちろん、他の文字エンコーディングにも対応しています – mb_trim($str, encoding: “shift_JIS”); • マニュアルはPHP 8.4に合わせて書き出すと思います
まとめ • mb_trim、mb_ltrim、mb_rtrim関数を作りました – どんなタイミングで作ったのかを説明しました • PHP Internalsとのコミュニケーションについて説明しまし た •
レビューを経てめちゃくちゃすごく勉強になりました • PHP 8.4で入るこれらの関数をお楽しみに
Appendix: mb_trimのアルゴリズム • 1コードポイントごとに左から右へ処理をしていく • 128コードポイントごとにto_wcharメソッドが呼び出される • 左側にtrimする文字があればleftを1足す • 左側のtrimする文字がなければ右側のtrimに移るため、フラグを
rightへの処理にするため、XORでMB_RTRIMに変更 • 右側にtrimする文字があればrightを1足す • 右側にtrimする文字がなければrightを0にリセット • 下記のような擬似コードでmb_substrする: – mb_substr($str, left, mb_strlen($str) – (right + left));