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