[2] 
[[PHP]]
の
[DFN[[CODE[locale_accept_from_http]]]],
[DFN[[CODE[Locale::acceptFromHttp]]]]
は
[[HTTP]]
[CODE[Accept-Language:]]
[[ヘッダー値]]から適当な[[ロケール]]を返すものです。


[REFS[

-
[1] [CITE@en[[[PHP]]: Locale::acceptFromHttp - Manual]], [TIME[2023-05-17T04:00:50.000Z]], [TIME[2023-05-17T08:28:00.036Z]] <https://www.php.net/manual/en/locale.acceptfromhttp.php>

]REFS]



[3] 
[[HTTP]] の [CODE[Accept-Language:]] [[ヘッダー値]]っぽい感じの入力を与えると、
何か[[ロケールID]]っぽいものが返ってきます。


[4] 
[[HTTP]] の [CODE[Accept-Language:]] は簡単そうに見えて実は複雑な構文を持っているので、
[[HTTP]] に精通していないと間違って実装してしまいがちです。
だから一般の開発者がいちいちそれを意識しないで処理できる機能を提供しようという言語開発者の判断は正しい。
自分でやらないで言語機能を使おうと判断したアプリケーション開発者の判断もまた正しい。


[5] 
でもそこに落とし穴が潜んでいて、なんかいい感じにやってくれるわーという[[フレームワーク]]の機能は往々にしてなんとなくいい感じで動いてそうだわーという雰囲気だけ提供してくれて、
あちことに罠が設置されてたりするんですよねえ。これがその実例。



[6] 
ドキュメントには[[ロケール識別子]]が返されると書いてありますが [SRC[>>1]]、
それがどんなものなのかはよくわかりません。
ドキュメントを隅から隅まで読み込めばどこかに書かれているのかもしれませんが、
少なくてもこのページ [SRC[>>1]] には書いてありません。
このページに唯一あるのは返ってくる値の例 [CODE[en_US]] で [SRC[>>1]]、
ここから [[POSIXロケール識別子]]的なものが得られるのかなあ、
という感触がありますが、それが正しいのかどうか。

[7] 
公式ドキュメントの例示は [CODE[$locale]] という[[変数]]に結果を代入していますけど、
巷で流通している利用例だと [CODE[$lang]] という[[変数]]に代入していることもあったりします。
つまり[[ロケール]]と[[言語]]がわからない人もこれを使っているということ。
そりゃあわからないですよね、公式ドキュメントにも何も書いてないもん。
[CODE[Accept-Language]] ([[言語]]) から[[ロケール]]を取得する、
という少々わかりにくいことしてるのに、説明がないと混乱する人が出てくるのは必然でしょう。

[8] 
あとこれ、[[ヘッダー値]]に入ってるものをただ返すのではなくて、
どれか一番使えるものを返してくれるらしいです。 [SRC[>>1]]
てきとーな値を入れて渡しても、そのまま返ってくるわけではなくて、
使えるものを選んでくれるそうな。
でもそれってどうやって選ぶんでしょうか?
引数は[[ヘッダー値]]1つだけですよ。
その肝心なところがドキュメントに全然書いてないのが謎。
隅から隅まで探せばどこかにあるのかもしれませんけど、
少なくてもこのページには書いてなくて。
(ユーザーコメントには、指定できないって書いてあるw)



- [9] [CITE@en[php-src/locale_methods.c at master · php/php-src · GitHub]], [TIME[2023-05-17T09:16:25.000Z]] <https://github.com/php/php-src/blob/master/ext/intl/locale/locale_methods.c#:~:text=locale_accept_from_http>



[10] 
ソースコードを見ると、利用可能な[[ロケール]]の一覧を用意して、それと一致するものが返されてくるみたいですねえ。
そしてその一覧は直接指定することはできないっぽい。

[11] 
だから利用可能な一覧に [CODE[ja]] や [CODE[ja_JP]] が(たまたま)入っていたりして、
[CODE[Accept-Language]] の値次第で [CODE[ja]] が返ってきたり [CODE[ja_JP]]
が返ってきたりするんですねえ。
そんなことはドキュメントに書いてないので、
いろんな[[ブラウザー]]がいろんな値を送ってくるのを経験しないとわからないわけです。
これはバグの温床っすな。

[12] 
ちな、
[CODE[Accept-Language:]] は [[IETF言語タグ]]なので、
[CODE[[[Accept-Language:]] [[ja-JP]]]]
が指定されると[[ロケールID]]として [CODE[ja_JP]] が返ってくる。これは罠。


[13] 
利用可能な[[ロケール]]の一覧はどこからやってくるかというと

- [14] [CITE[[[ICU]] 73.1: ures.h File Reference]], [TIME[2023-04-13T21:37:12.000Z]], [TIME[2023-05-17T09:22:33.800Z]] <https://unicode-org.github.io/icu-docs/apidoc/dev/icu4c/ures_8h.html#aa59a731931be953ead03206933f3c0f0>

[CODE[ures_openAvailableLocales]] という [[ICU]] の[[関数]]が呼び出されてます。

[[ICU]] の仕様によって [[PHP]] の挙動が変わるって、そんなことちゃんと理解して[[Webアプリケーション]]作ってる開発者いるのかなあ?
この一覧をちゃんと思った通りのものにするにはどこのどのファイルをいじればいいんだろう?

[16] では肝心の [CODE[Accept-Language]] から[[ロケール識別子]]への変換はどうやっているのかというと、

- [15] [CITE@en[Search · uloc_acceptLanguageFromHTTP · [[GitHub]]]], [TIME[2023-05-17T09:31:21.000Z]] <https://github.com/unicode-org/icu/search?q=uloc_acceptLanguageFromHTTP>


これもやっぱり [[ICU]] の
[CODE[uloc_acceptLanguageFromHTTP]]
を使っている模様。

[17] てことは返ってくる結果は[[Unicodeロケール識別子]]の [[POSIXロケール識別子]]ぽい味付けのやつなんかな?


[19] 
[CODE[Accept-Language:]] というのはつまり外部入力なわけで、
どんな入力が与えられるかわからないからちゃんと処理しないと[[セキュリティー]]的にやばいんですが、

- [18] [CITE@ja[JVNDB-2016-004091 - [[JVN]] iPedia - 脆弱性対策情報データベース]], [TIME[2018-02-20T02:49:21.000Z]], [TIME[2023-05-17T09:37:23.554Z]] <https://jvndb.jvn.jp/ja/contents/2016/JVNDB-2016-004091.html>

こういう[[脆弱性]]の報告が実際にあって、与えられた
[CODE[Accept-Language:]]
の[[文字列]]が長すぎるときにいかれてました。

この修正は [[PHP]] 側でなされてるようですけど、
[CODE[Accept-Language:]] なんて10中8,9どころか9.999くらい外部入力に決まってるんですから、
そもそも [[ICU]] がちゃんとしてないと駄目な案件なんですよね。

で、 [[PHP]] というかおおよそ [[WAF]] はそういう不正な入力が来たらどうするかというのを常に考えないといけない性質のソフトウェアで、
外部ライブラリーを使うときもそれがその意味で安全かどうか評価して決めないといけない。
それが出来てないという意味では [[PHP]] もあかんのです。



[20] 
[[ICU]] ってあの天下の [[IBM]] の作ったライブラリーで今は [[Unicode Consortium]]
が仕切ってて、なんだか立派なちゃんとしたソフトウェアなんじゃないか、
って漠然としたイメージがあるんじゃないかと思いますけど (その分なんとなくとっつきにくいイメージもありますけど)、
実態はこのレベルなんで、そのまま[[Webアプリケーション]]開発に使っちゃうとあとで泣きを見るやつですね。

[21] 
[[Webアプリケーション]]開発には [[Web]] をちゃんと熟知してる技術者が作ったソフトウェアを使いましょう。

[22] ってそれ [[PHP]] は・・・





