PHP5.5の「大文字小文字を区別しない比較がロケールに依存しなくなる」について
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 1,687
PHP5.5の「下位互換性のない変更点」(下記ページ)
http://php.net/manual/ja/migration55.incompatible.php
に記載されている「大文字小文字を区別しない比較がロケールに依存しなくなる」の内容について具体的にどのロケール及びコードで過去バージョンと差異が発生するのか、ご教示頂けないでしょうか。
認識しているロケールごとにmb_internal_encodingも変更して確認してみましたが、全てPHP5.5とPHP5.4で同じ結果が返ってきます。
(ロケールによって結果は異なりますが・・・)
PHP5.4と5.5で結果が異なる具体的なコードをご教示頂けると有難いです。
確認したソースコードの一部(環境:CentOS6.4-ファイルの文字コードはUTF8)
<?php
error_reporting(E_ALL);
function sl($locale, $str) {
$arEncode = array('', 'UCS-4','UCS-4BE','UCS-4LE','UCS-2','UCS-2BE','UCS-2LE','UTF-32','UTF-32BE','UTF-32LE','UTF-16','UTF-16BE','UTF-16LE','UTF-7','UTF7-IMAP','UTF-8','ASCII','EUC-JP','SJIS','eucJP-win','SJIS-win','ISO-2022-JP','ISO-2022-JP-MS','CP932','CP51932','SJIS-mac','JIS','JIS-ms','CP50220','CP50220raw','CP50221','CP50222','ISO-8859-1','ISO-8859-2','ISO-8859-3','ISO-8859-4','ISO-8859-5','ISO-8859-6','ISO-8859-7','ISO-8859-8','ISO-8859-9','ISO-8859-10','ISO-8859-13','ISO-8859-14','ISO-8859-15','byte2be','byte2le','byte4be','byte4le','BASE64','HTML-ENTITIES','7bit','8bit','EUC-CN','CP936','GB18030','HZ','EUC-TW','CP950','BIG-5','EUC-KR','UHC','ISO-2022-KR','Windows-1251','Windows-1252','CP866','KOI8-R','ArmSCII-8');
foreach ($arEncode as $encode) {
mb_internal_encoding($encode);
ex($locale, $str);
}
}
function ex($locale, $str) {
if (setlocale(LC_ALL, $locale) === false) {
error_log('Locale not found: ' . $locale) . PHP_EOL;
return;
}
echo STRTOUPPER($str) . PHP_EOL;
echo MB_STRTOUPPER($str) . PHP_EOL;
echo STRCASECMP($str, STRTOUPPER($str)) . PHP_EOL;
echo STRLEN($str) . PHP_EOL;
echo MB_STRLEN($str) . PHP_EOL;
echo SUBSTR($str, 10, 10) . PHP_EOL;
echo STRIPOS($str, chr(0xE6)) . PHP_EOL;
echo STRPOS($str, chr(0xE6)) . PHP_EOL;
echo STR_REPLACE(chr(0xE6), '日本語', $str) . PHP_EOL;
echo STR_IREPLACE(chr(0xE6), '日本語', $str) . PHP_EOL;
echo UCFIRST($str) . PHP_EOL;
}
$str = null;
for($i=0xC0; $i<=0xFF; $i++){
$str .= chr($i);
if ($i == 0xDF) $str .= 'abc日本語';
}
sl('C',$str);
sl('POSIX',$str);
sl('aa_DJ',$str);
sl('aa_DJ.iso88591',$str);
sl('aa_DJ.utf8',$str);
sl('aa_ER',$str);
sl('aa_ER.utf8',$str);
/*文字数制限により省略(「locale -a」の結果全て)*/
sl('zh_TW.utf8',$str);
sl('zu_ZA',$str);
sl('zu_ZA.iso88591',$str);
sl('zu_ZA.utf8',$str);
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
PHP5.5より前の環境が無いため検証はあまりできていませんが、そもそもそこで述べていることに誤解があるようです。
そのページで述べている大文字小文字無視のマッチングは、関数名やクラス名や定数名についての話であり、strcasecmp等の関数での処理での話ではありません。たとえば、hoi
という関数があった場合、HOI
でも呼び出せるというところの大文字小文字の無視です。5.4以前ではトルコ語の環境にするとHOİ
( İ はIの上または右上方に・がありますが、環境によっては正しく表示されません)でも呼び出せたようですが、5.5以降は言語環境に関係なくASCIIでのルールにのみ従うようになったため、呼び出せなくなったと言うことです。
<?php
function hoi()
{
echo "hoi";
}
HOİ(); // Iではなくİ。5.4以前でトルコ語なら呼び出せる。
なお、ここでのロケールはシステムのロケールだと思われます。マルチバイト文字の扱い方の変更に過ぎないmb_internal_encodingでは設定できず、PHPを呼び出す前にシステム(OS)のロケールを変更しないと確認できないかも知れません。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.34%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/05/14 07:55
https://bugs.php.net/bug.php?id=18556
2016/05/14 23:08
ご回答有難うございます。
http://b60.uchb.net/linux/2014/09/articles4php55.html
を拝見して、そうゆう事象が発生するなのか?と勘違いしておりましたが、
こちらの内容って、この問題とは別の問題っぽいですね。
クラス名、関数名、定数名ってASCIIコードでのみ定義できるものと思っておりましたが、違うんですね。
>これは、マルチバイト文字セット (UTF-8 も含む) で非 ASCII 文字に対して大文字小文字を区別しないマッチングをしているコードで問題になるかもしれません。
>ヨーロッパ諸国語のアクセント記号などがこれにあてはまります。
上記の記載内容からヨーロッパ諸国で通常使用される「ISO-8859-1」での「アクセント記号(0xC0~0xFF)」は
マルチバイトじゃないような。(マルチバイトを扱える文字コードでアクセント記号を使用した場合って意味???)
eripongさん
ご回答有難うございます。
ご指摘頂いたバグについて、バグ投稿された際に記載されているスクリプトは、
私の環境ではVer.5.4と5.5でも正常に実行されてましたが、
「raccyさん」の回答頂いた内容からも、こちらが怪しいですね。
raccyさん、eripongさん
ご回答頂いた内容を踏まえて、再度、事象の再現確認をしたいと思います。
(再現できるかはわかりませんが。)
ですので、解決済み及びベストアンサーのマークは2、3日お待ち頂けますでしょうか。
2016/05/16 23:25 編集
醜い投稿となり申し訳ございません。
```PHP
<?php
class aÀ{} //「À」(0xC0)
class InfoBlob{} //「I」(0x49)
class i{} //「i」(0x69)
class g{} //「g」(0x67)
class E{} //「E」(0x45)
function sl($locale) {
if (setlocale(LC_ALL, $locale) === false) {
error_log('Locale not found: ' . $locale) . PHP_EOL;
return;
}
if(!class_exists('aÀ')) echo 'aÀ:'. $locale . PHP_EOL; //①「À」(0xC0)
if(!class_exists('aà')) echo 'aà:'. $locale . PHP_EOL; //②「à」(0xE0)
if(!class_exists('InfoBlob')) echo 'InfoBlob:'. $locale . PHP_EOL; //③「I」(0x49)
if(!class_exists('infoBlob')) echo 'infoBlob:'. $locale . PHP_EOL; //④「i」(0x69)
echo strtolower('INFOBLOB') . PHP_EOL; //⑤小文字変換
if(!class_exists('İ')) echo 'I:'. $locale . PHP_EOL; //⑥「İ」(0x0130)
if(!class_exists('Ġ')) echo 'G:'. $locale . PHP_EOL; //⑦「Ġ」(0x0117)
if(!class_exists('ė')) echo 'e:'. $locale . PHP_EOL; //⑧「ė」(0x0120)
}
sl('C');
sl('POSIX');
sl('aa_DJ');
//・・・(省略)
sl('ja_JP');
sl('ja_JP.eucjp');
sl('ja_JP.ujis');
sl('ja_JP.utf8');
sl('japanese');
sl('japanese.euc');
//・・・(省略)
sl('zu_ZA');
sl('zu_ZA.iso88591');
sl('zu_ZA.utf8');
```
**【出力結果】**
※以下の日本語ロケールは以下を指します。
ja_JP
ja_JP.eucjp
ja_JP.ujis
ja_JP.utf8
japanese
japanese.euc
- ①if(!class_exists('aÀ')) echo 'aÀ:'. $locale . PHP_EOL; //①「À」(0xC0)
→5.4でのみ309個(全733個中)出力 ※日本語ロケールの出力なし
(詳細は省略)
→5.5では出力なし
- ②if(!class_exists('aà')) echo 'aà:'. $locale . PHP_EOL; //②「à」(0xE0)
→5.4, 5.5とも全ロケールで出力される
- ③if(!class_exists('InfoBlob')) echo 'InfoBlob:'. $locale . PHP_EOL; //③「I」(0x49)
→5.4でのみ16個出力 ※日本語ロケールの出力なし
InfoBlob:az_AZ
InfoBlob:az_AZ.utf8
InfoBlob:crh_UA
InfoBlob:crh_UA.utf8
InfoBlob:ku_TR
InfoBlob:ku_TR.iso88599
InfoBlob:ku_TR.utf8
InfoBlob:tr_CY
InfoBlob:tr_CY.iso88599
InfoBlob:tr_CY.utf8
InfoBlob:tr_TR
InfoBlob:tr_TR.iso88599
InfoBlob:tr_TR.utf8
InfoBlob:tt_RU.utf8@iqtelif
InfoBlob:tt_RU@iqtelif
InfoBlob:turkish
→5.5では出力なし
- ④if(!class_exists('infoBlob')) echo 'infoBlob:'. $locale . PHP_EOL; //④「i」(0x69)
→5.4, 5.5とも出力なし
- ⑤echo strtolower('INFOBLOB') . PHP_EOL; //⑤小文字変換
→5.4でのみ③で出力されるロケールについては「InfoBlob」。それ以外は「infoBlob」。
- ⑥if(!class_exists('İ')) echo 'I:'. $locale . PHP_EOL; //⑥「İ」(0x0130)
→5.4でのみ③の出力と同一のロケールを出力。
→5.5では出力なし
- ⑦if(!class_exists('Ġ')) echo 'G:'. $locale . PHP_EOL; //⑦「Ġ」(0x0117)
→5.4, 5.5とも出力なし
- ⑧if(!class_exists('ė')) echo 'e:'. $locale . PHP_EOL; //⑧「ė」(0x0120)
→5.4, 5.5とも出力なし
**【考察】**
- 考察①
①②の結果、Ver5.4ではアクセント記号を使用したクラス名はロケールによっては使用できない。
(アクセント記号のクラス名は大文字、小文字は全てのロケールで異なると判定される。)
- 考察②
③④⑤⑥⑦⑧の結果、Ver5.4ではトルコ語及びトルコ語の規則を使用するアゼルバイジャン(az)、クルド(ku)等のロケールでの大文字「I」が正しく扱えなかった為、クラス名、関数名の大文字、小文字と大文字、小文字の相違を伴う関数の結果が正しく返されなかった。
https://bugs.php.net/bug.php?id=18556
→考察②での特定のロケールで大文字「I」に問題があった為、ロケールに依存しないよう修正された。
結果、考察①のようにアクセント記号にて定義されたものが、ロケールによって認識しないものが、認識されるようになった?
**【疑問】**
- 疑問①
http://php.net/manual/ja/language.oop5.basic.php
のクラス名に以下の文字を使用できるとあるが、
__^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$__
⑥⑦⑧で使用した文字コード「İ」「Ġ」「ė」は
範囲外であるが、正しく認識している?
- 疑問②
「À」(0xC0) と「à」(0xE0)は大文字小文字の関係にない?
長文で分かりにくい内容となり恐縮ですが、考察と疑問について、ご指摘頂ける方がございましたら、宜しくお願いします。
2016/05/17 21:29
PHPはそもそもASCII以外の文字を考慮していません。文字列はただの8bitのcharの羅列でしか見ていません。PHPにはソースコード自体の文字コードを指定する方法はありませんし、mbstringはそれを特定のエンコードと見なして処理しているだけに過ぎません。Shift_JISで"表"を"表\"とする必要があるのも8bitのcharの羅列でしか見ていないからです。ですので、Unicodeで見ても意味がありません。もしUTF-8でエンコードしている場合はU+80以上の文字は全て\x80-\xffの範囲に入ります。注意すべき所はここら辺かなと思います。
2016/05/22 22:39
raccyさん、eripongさん
貴重なアドバイスを頂き、有難うございました。
これからも活用させて頂きたいと思いますので、目に留まった際には、
宜しくお願い致します。