PHP の mbstring 拡張モジュールについて調べてみました。mbstring 拡張モジュールは、PHP マニュアル : マルチバイト文字列関数 (mbstring) にも書かれていない機能や制約が多くあります。そのあたりをソースコードを確認しながらまとめてみました。
更新内容については、更新履歴を参照してください。
ソースコードの確認は主に PHP 5.2.5 〜 PHP 5.3.0 で行いました。PHP 4.x に対する記述もありますが、細かいバージョン等については十分に確認できていないものもあります。このページを読んで、間違い、誤字、脱字などがを見つけましたら、はてなの日記、または、メールなどで教えてください。
PHP の mbstring 拡張モジュールは、ソースコードから構築する場合、コンパイル時に静的ライブラリとして組み込む方法と、共有ライブラリとして動的に使用する方法の2通りがあります。
Windows 環境の場合は、mbstring.dll がダウンロードした PHP パッケージの中に含まれています。コンパイルする必要はありません。
PHP のソースコードからコンパイルする場合、configure
のオプションに、--enable-mbstring
を追加します。また、スクリプトファイルを Shift_JIS で記述した場合は --enable-zend-multibyte
も追加します。
PHP 4.3.4 以降では、コンパイル時に --enable-mbstring
を指定すると、マルチバイト正規表現関数も使用できるようになります。--disable-mbregex
とするとマルチバイト正規表現関数が使用できなります。
PHP 4.3.3 以前では、--enable-mbregex
を追加することでマルチバイト正規表現関数が使用できるようになります。
まとめると以下の表のようになります。
- | PHP 4.3.3 以前 | PHP 4.3.4 以降 |
---|---|---|
マルチバイト正規表現関数を有効にする場合 | --enable-mbstring --enable-mbregex |
--enable-mbstring |
マルチバイト正規表現関数を無効にする場合 | --enable-mbstring |
--enable-mbstring --disable-mbregex |
PHP 5.2.10 での mbstring に関するコンパイルオプションは以下の通りです。マルチバイト正規表現のバックトラックを無効にするオプションが追加されました。
--enable-mbstring Enable multibyte string support --disable-mbregex MBSTRING: Disable multibyte regex support --disable-mbregex-backtrack MBSTRING: Disable multibyte regex backtrack check --with-libmbfl[=DIR] MBSTRING: Use external libmbfl. DIR is the libmbfl base install directory [BUNDLED]
PHP 5.3.0 からは、外部の onigruma(マルチバイト正規表現ライブラリ)を使用することができるようになりました。
--enable-mbstring Enable multibyte string support --disable-mbregex MBSTRING: Disable multibyte regex support --disable-mbregex-backtrack MBSTRING: Disable multibyte regex backtrack check --with-libmbfl[=DIR] MBSTRING: Use external libmbfl. DIR is the libmbfl base install directory [BUNDLED] --with-onig[=DIR] MBSTRING: Use external oniguruma. DIR is the oniguruma install prefix. If DIR is not set, the bundled oniguruma will be used
まず、共有ライブラリを作成する必要があります。開発環境を準備し、PHP のソースコードを展開後、phpize
コマンドを使用してからコンパイルします。
例えば、PHP 5.2.5 で mbstring 拡張モジュールを作成する場合は、以下のようにします。
$ tar jxvf php-5.2.5.tar.bz2 $ cd php-5.2.5/ext/mbstring $ phpize $ ./configure --enable-mbstring $ make $ make test $ sudo make install
インストールが終了したら、php.ini に以下の行を記述します。extension_dir
は PHP のバージョンや PHP のコンパイルオプションによって変わります。インストールされた場所を指定してください。
extension_dir = "/usr/local/lib/php/extensions/no-debug-non-zts-20060613/" extension=mbstring.so
ダウンロードした PHP のパッケージの中に、php_mbstring.dll が含まれています。
php.ini で extension_dir
と extension
を設定します。
extension_dir = "C:/php/ext" extension=php_mbstring.dll
php.ini には、以下のような mbstring の設定があります。詳しくは php.ini の mbstring 設定オプション (PHP マニュアル) を参照してください。
日本語を使用するのであれば、mbstring.language
を "Japanese
" に、mbstring.internal_encoding
を PHP スクリプトを書いている文字コード(UTF-8
、または、EUC-JP
など)と合わせておくと良いと思います。
オプション名 | デフォルト値 | 使用可能バージョン | 備考 |
---|---|---|---|
mbstring.language |
"neutral" | PHP 4.3.0 以降 | mbstring の言語設定です。設定可能な値については、mb_language() 関数の引数に設定可能な言語一覧を参照してください。 |
mbstring.internal_encoding |
NULL | PHP 4.0.6 以降 | mbstring の内部文字コードを設定します。 |
mbstring.http_input |
"pass" | PHP 4.0.6 以降 | HTTP 入力文字コードを設定します。 |
mbstring.http_output |
"pass" | PHP 4.0.6 以降 | HTTP 出力文字コードを設定します。 |
mbstring.detect_order |
NULL | PHP 4.0.6 以降 | 文字コード検出の文字コード順を定義します。 |
mbstring.script_encoding |
NULL | PHP 4.3.0 以降 | スクリプトファイルの文字コードを設定できます(カンマ区切りで複数指定可能)。
zend_multibyte が有効な場合
(PHP のコンパイルオプションに --enable-zend-multibyte を指定した場合)に使用可能です。 |
mbstring.substitute_character |
none | PHP 4.0.6 以降 | 文字コード変換時に無効な文字を置き換える代替文字を定義します。
デフォルトでは none ですが、実際には、? (63 を設定したのと同じ)
に置き換えられます。詳細は文字コード変換で不正文字列を変換した場合の代替文字を参照してください。 |
mbstring.func_overload |
"0" | PHP 4.2.0 以降 | 一部のシングルバイト関数を mbstring 関数に置き換えるかどうかを設定します。 |
mbstring.encoding_translation |
"0" | PHP 4.3.0 以降 | 入力された HTTP クエリを内部文字コードに自動変換するかどうかを設定します (※1)。 |
mbstring.strict_detection |
"0" | PHP 5.1.2 以降 | 厳密な文字コード検出を行うかどうかを設定します。 mb_detect_encoding() 関数の strict(第3引数) と php.ini の mbstring.strict_detection も参照してください。 |
mbstring.http_output_conv_mimetypes |
"^(text/|application/xhtml\\+xml)" | PHP 5.3.0 以降 | 出力時に文字コード変換を行う場合の MIME タイプを正規表現で指定します。 この正規表現にマッチした MIME タイプの場合、文字コード変換を行います(※2)。 |
※1 mbstring.encoding_translation
を有効にした場合、mbstring.encoding_translation
は mbstring.http_input
の設定に従って、入力 HTTP クエリ文字列を内部文字コード(mbstring.internal_encoding
)に変換します。ただし、文字コードの検出に失敗すると、文字化けの原因になります。
また、PHP 4.3.2 以前で HTML フォームの enctype に multipart/form-data を指定する場合、以下の点に注意が必要です。
注意: PHP 4.3.2 およびそれ以前のバージョンの場合、HTML フォームの enctype が multipart/form-data に 設定された場合、mbstring は、POST データの文字エンコーディングを変換しません。この場合、文字列を 内部文字エンコーディングに変換してやる必要があります。 PHP 4.3.3 以降、HTML フォームの enctype が multipart/form-data に設定され、かつ、php.ini において mbstring.encoding_translation に On が指定されている場合、POST データの変数とアップロードされた ファイルの名前の文字エンコーディングは、内部文字エンコーディングに変換されます。ただし、クエリキー に関しては、変換されません。
※2 mbstring.http_output_conv_mimetypes
は出力時に指定した文字コードに変換する機能(ob_start()
と mb_output_handler()
を使用)が、Content-Type に XHTML(application/xhtml+xml)を指定した場合、文字コードが変換されないという問題を解消するために追加されました(参考: [PHP-dev 1398] mb_output_handlerによる文字エンコーディング変換について)。
php.ini で mbstring.internal_encoding
を指定しなかった場合、mbstring.language
の設定に対応した mbstring
の内部文字コードが設定されます。
実際には以下のようになります。
mbstring.language | 内部文字コード |
---|---|
neutral (未設定) | ISO-8859-1 |
English | ISO-8859-1 |
Japanese | EUC-JP |
Uni | UTF-8 |
mbstring.language
に何も設定しない場合、neutral
設定されます。
mb_language()
で言語設定を変更した場合は internal_encoding
は変更されません。
PHP のコンパイル時の configure
オプションに --enable-zend-multibyte
を追加すると、PHP のスクリプトファイルの文字コードが指定できるようになります。php.ini または、.htaccess の mbstring.script_encoding
で文字コードを指定します。
mbstring.script_encoding
に文字コードを設定すると、スクリプトファイルを読み込む際に、指定した文字コードを内部文字コード(internal_encoding
)に変換します。このため、スクリプトファイルの文字コードに SJIS(Shift_JIS) や UTF-16 などを使用することができます。通常では、内部文字コードには、SJIS, UTF-16, ISO-2022-JP などを設定しても想定通りに動作しません。
php.ini では、mbstring セクションに以下のような設定を追加します(複数指定や "auto
" も設定可能)。
mbstring.script_encodeing = "SJIS, EUC-JP, UTF-8"
.htaccess で指定する場合は以下のようにします。
php_value mbstring.script_encodeing "auto"
mbstring では、以下の内部変数が定義されています。mb_get_info()
を使用することで、確認できます。
内部変数名 | 説明 | php.ini での設定 | 設定変更用の関数 |
---|---|---|---|
internal_encoding |
内部文字コード | mbstring.internal_encoding |
mb_internal_encoding() |
http_input |
HTTP 入力文字コード | mbstring.http_input |
関数では設定できません(※1) |
http_output |
HTTP 出力文字コード | mbstring.http_output |
mb_http_output() |
func_overload |
一部のシングルバイト関数を mbstring 関数に置き換える設定(※2) | mbstring.func_overload |
関数では設定できません |
func_overload_list |
func_overload で置き換えた関数のリスト(※3) |
mbstring.func_overload |
関数では設定できません |
mail_charset |
メール送信時の文字コード(※4) | mbstring.language |
mb_language() |
mail_header_encoding |
メール送信時のメールヘッダの文字コード(※4) | mbstring.language |
mb_language() |
mail_body_encoding |
メール送信時の本文の文字コード(※4) | mbstring.language |
mb_language() |
illegal_chars |
不正文字検出数(※5) | php.ini では設定できません | 関数では設定できません |
encoding_translation |
HTTP クエリの入力文字の自動変換設定 | mbstring.encoding_translation |
関数では設定できません |
language |
mbstring の言語設定 | mbstring.language |
mb_language() |
detect_order |
文字コード検出順の設定 | mbstring.detect_order |
mb_detect_order() |
substitute_character |
無効な文字を代替する文字 | mbstring.substitute_character |
mb_substitute_character() |
strict_detection |
厳密な文字コード検出を行うかどうかの設定(※6) | mbstring.strict_detection |
ini_set( "mbstring.strict_detection", 1 ) |
script_encodeing |
スクリプトファイルの文字コード指定 | mbstring.script_encoding |
ini_set( "mbstring.script_encoding", "SJIS" )
(※7) |
http_output_conv_mimetypes |
HTML 出力時に文字コード変換を行う MIME タイプ(正規表現で設定) | mbstring.http_output_conv_mimetypes |
ini_set( "mbstring.http_output_conv_mimetypes", "^(text/|application/xhtml\\+xml)" )
(※8) |
※1 mb_http_input()
では設定変更できそうに思えますが、この関数では設定できません。mb_parse_str()
を実行すると、結果が設定されます。
※2 8進数で設定します。詳しくは 関数のオーバーロード機能(PHP マニュアル)を参照してください。
※3 func_overload
の値によって自動的に設定されます。
※4 language
を変更すると自動的に設定されます。
※5 文字コード変換時に不正文字があった場合にカウントされます。
※6 mb_detect_encoding()
で使用されます。詳しくは、mb_detect_encoding() 関数の strict(第3引数) と php.ini の mbstring.strict_detection で説明しています。
※7 zend_multibyte が有効になっている場合に表示されます。ini_set()
で指定を変更できますが、実際にはスクリプト中での動的な変更はできないようです(PHP 5.2.6 時点)。
※8 PHP 5.3.0 以降で表示されます。デフォルト値は"^(text/|application/xhtml\\+xml)
" です。
mbstring の関数を使用する場合、内部文字コード(internal_encoding
)と変数の文字コードが合っていないと正しく動作しない関数があります。また、PHP スクリプトが書かれたファイルの文字コードについても、意識しておく必要があります。zend_multibyte を有効にしていない場合、PHP ファイルの文字コードが変数の文字コードになります。
多くの mbstring の関数では、引数に文字コードを指定できますが、省略した場合は internal_encoding
が使用されます。また、文字コードは指定できませんが、internal_encoding
使用される関数もあります。
例えば mb_encode_mimeheader()
や mb_send_mail()
では、文字コードを設定する引数はありませんが、internal_encoding
を使用します。もし、引数として渡す文字列の文字コードが internal_encoding
と違う場合、文字化けの原因になります。
mbstring 関数は以下の通りです(マルチバイト正規表現関数を除く)。各関数は PHP マニュアルの関数説明にリンクしています。
返り値の型 | 関数名 | 引数 | 使用可能バージョン | 備考 |
---|---|---|---|---|
bool | mb_check_encoding() |
[ string $var = NULL [, string $encoding = mb_internal_encoding() ]] |
4.4.3 / 5.1.3 以降 | |
string | mb_convert_case() |
string $str , int $mode [, string $encoding = mb_internal_encoding() ] |
4.3.0 以降 | |
string | mb_convert_encoding() |
string $str , string $to_encoding [, mixed $from_encoding ] |
4.0.6 以降 | |
string | mb_convert_kana() |
string $str [, string $option = "KV" [, string $encoding ]] |
4.0.6 以降 | |
string | mb_convert_variables() |
string $to_encoding , mixed $from_encoding , mixed &$vars [, mixed &$... ] |
4.0.6 以降 | |
string | mb_decode_mimeheader() |
string $str |
4.0.6 以降 | |
string | mb_decode_numericentity() |
string $str , array $convmap [, string $encoding ] |
4.0.6 以降 | |
string | mb_detect_encoding() |
string $str [, mixed $encoding_list = mb_detect_order() [, bool $strict = false ]] |
4.0.6 以降 | |
mixed | mb_detect_order() |
[ mixed $encoding_list ] |
4.0.6 以降 | |
string | mb_encode_mimeheader() |
string $str [, string $charset [, string $transfer_encoding [, string $linefeed = "\r\n" [, int $indent = 0 ]]]] |
4.0.6 以降 | |
string | mb_encode_numericentity() |
string $str , array $convmap [, string $encoding [, bool $is_hex = false ]] |
4.0.6 以降 | 第4引数 $is_hex は PHP 5.4.0 から使用可能。 |
array | mb_encoding_aliases() |
string $encoding |
5.3.0 以降 | |
mixed | mb_get_info() |
[ string $type = "all" ] |
4.2.0 以降 | |
mixed | mb_http_input() |
[ string $type = "" ] |
4.0.6 以降 | |
mixed | mb_http_output() |
[ string $encoding ] |
4.0.6 以降 | |
mixed | mb_internal_encoding() |
[ string $encoding = mb_internal_encoding() ] |
4.0.6 以降 | |
mixed | mb_language() |
[ string $language ] |
4.0.6 以降 | |
array | mb_list_encodings() |
void | 5.0.0 以降 | |
string | mb_output_handler() |
string $contents , int $status |
4.0.6 以降 | |
bool | mb_parse_str() |
string $encoded_string [, array &$result ] |
4.0.6 以降 | |
string | mb_preferred_mime_name() |
string $encoding |
4.0.6 以降 | |
bool | mb_send_mail() |
string $to , string $subject , string $message [, string $additional_headers = NULL [, string $additional_parameter = NULL ]] |
4.0.6 以降 | |
string | mb_strcut() |
string $str , int $start [, int $length [, string $encoding ]] |
4.0.6 以降 | |
string | mb_strimwidth() |
string $str , int $start , int $width [, string $trimmarker [, string $encoding ]] |
4.0.6 以降 | |
int | mb_stripos() |
string $haystack , string $needle [, int $offset [, string $encoding ]] |
5.2.0 以降 | |
string | mb_stristr() |
string $haystack , string $needle [, bool $before_needle = false [, string $encoding ]] |
5.2.0 以降 | |
int | mb_strlen() |
string $str [, string $encoding ] |
4.0.6 以降 | |
int | mb_strpos() |
string $haystack , string $needle [, int $offset = 0 [, string $encoding ]] |
4.0.6 以降 | |
string | mb_strrchr() |
string $haystack , string $needle [, bool $part = false [, string $encoding ]] |
5.2.0 以降 | |
string | mb_strrichr() |
string $haystack , string $needle [, bool $part = false [, string $encoding ]] |
5.2.0 以降 | |
int | mb_strripos() |
string $haystack , string $needle [, int $offset = 0 [, string $encoding ]] |
5.2.0 以降 | |
int | mb_strrpos() |
string $haystack , string $needle [, int $offset = 0 [, string $encoding ]] |
4.0.6 以降 | |
string | mb_strstr() |
string $haystack , string $needle [, bool $before_needle = false [, string $encoding ]] |
5.2.0 以降 | |
string | mb_strtolower() |
string $str [, string $encoding = mb_internal_encoding() ] |
4.3.0 以降 | |
string | mb_strtoupper() |
string $str [, string $encoding = mb_internal_encoding() ] |
4.3.0 以降 | |
int | mb_strwidth() |
string $str [, string $encoding ] |
4.0.6 以降 | |
mixed | mb_substitute_character() |
[ mixed $substrchar ] |
4.0.6 以降 | |
int | mb_substr_count() |
string $haystack , string $needle [, string $encoding ] |
4.3.0 以降 | |
string | mb_substr() |
string $str , int $start [, int $length [, string $encoding ]] |
4.0.6 以降 |
mb_language()
は、第1引数(language)に "Japanese
", "ja
", "English
", "en
", "uni
" などが設定可能です(PHP マニュアル : mb_language)。引数の大文字、小文字は区別されません("JA
" と "ja
" は同じです)。また、php.ini の mbstring.language
で設定した場合も機能的にはほぼ同等です。
mb_language()
は、主に以下の2つの機能があります。
mb_convert_encoding()
や mb_convert_variables()
などの引数の入力文字コードに "auto
" を使用した場合に影響を与えます("auto" の展開と文字コードリストを参照)。
mbstring の内部設定である、language
(言語設定)および、メール設定(mail_charset
, mail_header_encoding
, mail_body_encoding
)を変更します。これらの内部設定は mb_get_info()
(PHP 5.1.3 以降で使用可能)で確認できます。
各内部設定値は以下の表のように設定されます。
内部設定値 | 説明 | English (En ) |
Uni |
Japanese (Ja ) |
---|---|---|---|---|
language |
言語設定 | English | Uni | Japanese |
mail_charset |
メールの送信文字コード | ISO-8859-1 | UTF-8 | ISO-2022-JP |
mail_header_encoding |
メールヘッダの文字コード | Quoted-Printable | BASE64 | BASE64 |
mail_body_encoding |
メール本文の文字コード | 8bit | BASE64 | 7bit |
いくつかのマルチバイト文字列関数では、文字コードを指定する引数に "auto
" を設定できます。"auto
" を設定した場合、内部変数 language
(mbstring.language
または、mb_language()
で設定した言語)によって、展開される文字コードリストが変化します。
"auto
" は、以下の関数の引数に設定すると有効に機能します。
mb_convert_encoding()
の第3引数
mb_convert_variables()
の第2引数
mb_detect_encoding()
の第2引数
mb_detect_order()
の第1引数
"auto
" は以下の表のように展開されます。
language | 文字コードリスト |
---|---|
English |
ASCII, UTF-8 |
Uni |
ASCII, UTF-8 |
Japanese |
ASCII, JIS, UTF-8, EUC-JP, SJIS |
language
を設定せずに、"auto
" を使用すると、文字コードリストは "ASCII, UTF-8" になります。この場合、mb_detect_encoding()
の引数で "auto
" を使用しても、JIS、EUC-JP、SJIS として検出されることはありません。
mb_detect_encoding()
や mb_convert_variables()
などの入力文字コードを省略した場合、内部変数 detect_order
の値が使用されます。"auto
" が使用されるわけではありませんので、注意してください。
"auto
" は mb_detect_encoding()
で文字コードを検出する場合には便利ですが、文字コードの検出に失敗し、文字化けの原因となることがあります。
日本語の文字コード検出をしたいのであれば、多くの場合、"auto
" を使用を使用せず、想定される文字コードを列挙した方が確実です。内部変数 detect_order
を設定するか、明示的に検出文字コードリストを設定してください。
上記の "auto
" の設定(ASCII, JIS, UTF-8, EUC-JP, SJIS)では、検出する文字列の文字コードが UTF-8 であると分かっている場合(例えば、HTML への出力を UTF-8 で行っており、ブラウザからも UTF-8 で文字列が送られてくることが期待できる場合)はうまく動作します。しかし、HTML の出力を SJIS (Shift_JIS) で行っている場合、UTF-8 や EUC-JP としても正しい文字列が入力される可能性があります。その場合、誤って文字コードを検出し、文字化けの原因になります。特に、入力文字列が短い場合、誤検出が多くなります。
入力文字列がほとんどの場合、SJIS であることが分かっているのであれば、ASCII, JIS, SJIS, UTF-8, EUC-JP と SJIS を前に持ってくると良いと思います。入力文字列 SJIS でない場合に UTF-8 や EUC-JP として文字コードを検出させるようにすることで、誤検出する可能性が低くなります。
mbstring は他にも様々な言語で使用されることが想定されています。以下は、PHP 5.3.0 で mb_language()
の引数に設定可能な言語一覧です。
設定可能な値 | 省略形 | 備考 |
---|---|---|
neutral |
neutral | 未設定 |
Armenian |
hy | アルメニア語 |
English |
en | 英語 |
German |
de | ドイツ語 |
Japanese |
ja | 日本語 |
Korean |
ko | 韓国語 |
Russian |
ru | ロシア語 |
Simplified Chinese |
zh-cn | 中国語(簡体字) |
Traditional Chinese |
zh-tw | 中国語(繁体字) |
Turkish |
tr | トルコ語 |
Ukrainian |
ua | ウクライナ語 |
uni |
uni | ユニコード |
設定可能な値と省略形の指定では、大文字・小文字は区別されません。
PHP 5.2.5 以前でアルメニア語を指定するには、"Armenian "
と後ろにスペース1文字が必要です。
ウクライナ語は PHP 5.3.0 で追加されました。
mb_check_encoding()
は、PHP 4.4.3、PHP 5.1.3 で追加された関数です。文字列が指定した文字コードとして有効なものかどうかを調べることができます。
ただし、PHP 4.4.3 〜 PHP 4.4.4、PHP 5.1.3 〜 PHP 5.2.0 では、UTF-8 の判定で常に TRUE
を返すという問題があります([PHP-dev 1338] mb_check_encoding() のテスト)。
また、mb_check_encoding()
は引数なしの場合のみ、特殊な挙動になります。
引数を省略せずに使用した場合
$string
="入力文字列"
;$encoding
=mb_internal_encoding()
; // 内部文字コード(internal_encoding)を取得 // 内部文字コードと変数の文字コードが同じかどうかをチェック$result
=mb_check_encoding(
; if ( !$string
,$encoding
)$result
) { // エラー処理 }
PHP 4.4.5 と PHP 5.2.1 以降では、以下の2つの結果は同じになります。
$result
=mb_check_encoding(
$string
,$encoding
)
$result
= ($string
===mb_convert_encoding(
$string
,$encoding
,$encoding
) )
PHP 5.2.6 以前では判定文字列に NULL バイトが含まれていた場合、mb_check_encoding()
の判定が正しくない場合がありましたが、これは PHP 5.2.7 で修正されました。
第2引数を省略した場合
$string
="入力文字列"
; // 内部文字コード(internal_encoding)と変数の文字コードが同じかどうかをチェック$result
=mb_check_encoding(
if ( !$string
)$result
) { // エラー処理 }
第2引数を省略すると、内部文字コード(internal_encoding
)を使用します。
引数なしの場合
$internal_encoding
=mb_internal_encoding()
; // 内部文字コードを取得 // mb_convert_variables() で内部文字コードに変換mb_convert_variables(
; // mb_convert_variables() が正しく変換できたかどうかをチェック$internal_encoding
,"ASCII,eucJP-win,SJIS-win,UTF-8"
,$post1
,$post2
)$result
=mb_check_encoding()
; if ( !$result
) { // エラー処理 }
PHP 4.4.5 以降、PHP 5.2.1 以降では、HTTP クエリの文字コード自動変換機能(mbstring.encoding_translation
)が有効になっていた場合、自動変換に成功したかを判定することができます。成功したかどうかを判定する場合は、スクリプトの最初の方で mb_check_encoding()
を引数なしで使用してください。自動変換が成功した場合は TRUE を、失敗した場合は FALSE を返します。
ただし、mb_check_encoding()
で UTF-8 から別の文字コードへ変換した場合の結果判定には意味がありません。mb_check_encoding()
は内部変数 illegal_chars
が 0 かどうかによって判定を行っています。mbstring のライブラリである、mbfilter は UTF-8 の不正文字の検出を厳密を行っていないため、多くの場合、内部変数 illegal_chars
がカウントされず、結果が TRUE になります。
mb_detect_encoding()
の第3引数に TRUE
を指定すると厳密に文字コードを検出するようになります。第3引数を省略すると、php.ini の mbstring.strict_detection
の設定に従います。
実際の処理としては、ある文字コードの検査中に、マルチバイトの有効な上位バイトが存在しているのに、有効な下位バイトが存在しない文字があった場合、検査中の文字コードは正しいと判定されません。これは、正しくない文字コードで検出しようとした場合に起きやすいものです。文字コードの検出率が高くなりますが、その分、処理は重くなります。
PHP のソースコードを見ると、ext/mbstring/libmbfl/mbfl/mbfilter.c で、mbfl_identify_filter
の flag
フィールドと status
フィールドを使用して処理しているようです。
mb_encode_mimeheader()
の第5引数の indent
は、MIME エンコードの際のインデントを指定できます。
これは、以下の問題に対処するために導入されました。
・Subject が RFC 違反 メールヘッダーはフィールド名とフィールド値(と改行文字)の合計が 76 文字未満としていますが,mb_encode_mimeheader() は 74 文字で ハードコーディングされているため,フィールド値だけで 74 文字で 改行されます.よって 1 行目が「Subject:」の分長くなります. ・Windows は改行文字は「\r\n」でなければ RFC 違反 Windows は引数で渡された値がそのまま SMTP プロトコルに使用され ます.SMTP は CR+LF の改行文字でなければならないので,LF だけ では RFC 違反になります. ・いきなり第 2 引数あるいは(省略値)でエンコードしようとする 「[php-users:####]」のように ASCII で始まる場合でもいきなり MIME エンコードしようとします.その為,一部の ML ドライバーやメーラー などで問題が出る場合があるようです.
どのような場合に使用すべきかについては、以下のように説明されています。
使い方は単純で $head = mb_encode_mimeheader($s, 'ISO-2022-JP', 'B', "\r\n", strlen("Subject: ")); のように5番目の引数にオフセットを指定できるようにしただけです。
PHP マニュアル: mb_encode_mimeheader には、indent
は PHP 5 で追加されたことになっていますが、正確には、PHP 4.4.2 と PHP 5.1.1 から使用可能です。
mb_send_mail()
でも、他の mbstring の関数と同様に、内部文字コード(internal_encoding
) と各引数の文字コードが一致しているかが重要です。
mb_send_mail()
を使用する際に、メール本文(第3引数)を ISO-2022-JP(JIS) に変換してから渡す必要はありません。そのコードは偶然うまく動作しているだけですので、注意してください。
うまく動作しているように見えるのは、ISO-2022-JP の文字列を Shift_JIS や EUC-JP、UTF-8 などの文字コードから ISO-2022-JP に変換しても、変化しないためです。ISO-2022-JP に変換後、mb_send_mail()
を使用すると、メール本文を internal_encoding
から ISO-2022-JP に変換します。再度文字コード変換を行う分、無駄な処理が増えています。
問題が出るケースとしては、UTF-8 でメールを送信する場合です。language
を Uni
にして、先に ISO-2022-JP に変換するような処理を入れた場合、文字化けしたメールが送信されます。
Subject(第2引数:メールの題名)も同様です。変数の文字コードと internal_encoding
が一致していれば、mb_send_mail()
がメールを送信する際に必要な文字コードに変換します。
PHP マニュアルでは、mb_send_mail() 関数の第5引数は、以下のように説明されています。
additional_parameter(
mb_send_mail()
の第5引数)は、MTA へ渡す コマンドライン引数です。sendmail を利用する際に正しい Return-Path を設定するためなどに利用すると便利です。
実際には、sendmail
コマンドで Return-Path を設定するには、-f mailaddress@example.com
のように、実際の送信元のメールアドレスを指定します。
php.ini の mail.force_extra_parameters
を設定している場合、こちらが優先されます。その場合、mb_send_mail()
の第5引数は無視されます。
以下に、mb_send_mail()
で簡単にメールを送信する関数を作成してみました。
メールの送信元と送信先にも日本語の名前を付加したい場合の例です。2行目の mb_internal_encoding()
で、UTF-8 を指定していますが、PHP スクリプトの文字コードに合わせて設定してください。
<?php mb_language( 'Japanese' ); mb_internal_encoding( 'UTF-8' ); // ... メール送信用の設定 send_mail( $subject, $body, $from_name, $from_address, $to_name, $to_address ); function is_mail_addr( $mail_address, $check_domain = FALSE ) { if ( ! preg_match( '!\A[-\\!#$%&\'*+/\w=?^_`{|}~.]+@' . '([-\\!#$%&\'*+/\w=?^_`{|}~]+\.[-\\!#$%&\'*+/\w=?^_`{|}~.]+|localhost)' . '\z!i' , $mail_address ) ) { return FALSE; } if ( $check_domain ) { list ( , $domain ) = explode( '@', $mail_address ); if ( ! checkdnsrr( $domain, 'MX' ) && ! checkdnsrr( $domain, 'A' ) && ! checkdnsrr( $domain, 'CNAME' ) ) { return FALSE; } } return TRUE; } function send_mail( $subject, $body, $from_name, $from_addr, $to_name, $to_addr ) { // メールアドレスの妥当性チェック if ( ! is_mail_addr( $from_addr, TRUE ) ) { return FALSE; } if ( ! is_mail_addr( $to_addr, TRUE ) ) { return FALSE; } $envelope_from = '-f ' . $from_addr; // 本当の送信元のメールアドレス $from_header = mb_encode_mimeheader( $from_name, 'ISO-2022-JP', 'B', "\n", strlen( 'From: ' ) ); $from_header .= "\n <" . $from_addr . '>'; $to_header = mb_encode_mimeheader( $to_name, 'ISO-2022-JP', 'B', "\n", strlen( 'To: ' ) ); $to_header .= "\n <" . $to_addr . '>'; $extra = 'From: ' . $from_header; return mb_send_mail( $to_header, $subject, $body, $extra, $envelope_from ); }
mbstring が提供している文字コード変換関数は以下の2つです。
mb_convert_encoding(string $str
, string $to_encoding
[, mixed $from_encoding
])
mb_convert_variables(string $to_encoding
, mixed $from_encoding
, mixed &$vars
[, mixed &$...
])
mb_convert_encoding() は、第1引数の文字列($str
)の文字コードを第3引数の文字コードリスト($from_encoding
)から第2引数の文字コード($to_encoding
)に変換し、その結果を返します。
mb_convert_variables() は、第3引数以降の複数の配列、オブジェクト、文字列($vars
)の文字コードを第2引数の文字コード($from_encoding
)から第1引数の文字コード($to_encoding
)に変換し、第3引数以降の値に上書きします。第3引数以降の値は全て同じ文字コードにする必要があります。
文字コードリスト(mb_convert_encoding() の第3引数と mb_convert_variables() の第2引数)の指定方法は以下の表の通りです。
指定方法 | 変換元の文字コード | 備考 |
---|---|---|
省略 | 内部文字コード(internal_encoding) | mb_convert_encoding() のみ省略可能 |
NULL | 文字コード変換しない | mb_convert_variables() のみ設定可能 |
文字列(有効な文字コードを1つ) | 指定された文字コード | |
文字列(有効な文字コードを2つ以上:カンマ区切り) | 指定された複数の文字コードの中から自動判別※ | |
文字列("auto") | "auto" の展開と文字コードリスト に従った文字コードリストの中から自動判別※ | |
配列(有効な文字コードを1つ) | 指定された文字コードを使用 | |
配列(有効な文字コードを2つ以上) | 指定された複数の文字コードから自動判別※ |
※自動判別の場合、前から順に指定された文字コードが変換対象の文字列に一致するかどうかを判定します。判定の結果が一致した場合、その文字コードを変換元の文字コードとして使用します。
PHP にも、文字コード変換で入力文字列の文字コードが不正、または、出力文字列の文字コードに文字が存在しない場合の代替文字を設定する機能があります。
php.ini では、mbstring.substitute_character
、PHP スクリプトでは、mb_substitute_character()
で設定可能です。
設定方法は以下の通りです。
php.ini で設定する場合
mbstring.substitute_character
= 63 ; 代替文字として 63(?) を設定mbstring.substitute_character
= 65533 ; 代替文字として U+FFFD を設定mbstring.substitute_character
= "long" ; 変換元の文字の値(例: BAD+C0, BAD+80)を出力するmbstring.substitute_character
= "none" ; 代替文字を出力しない
PHP スクリプト中で設定する場合
mb_substitute_character
(63
); // 代替文字として 63(?) を設定mb_substitute_character
(0xFFFD
); // 代替文字として U+FFFD を設定mb_substitute_character
( 'long
' ); // 変換元の文字の値(例: BAD+C0, BAD+80)を出力するmb_substitute_character
( 'none
' ); // 代替文字を出力しない
PHP マニュアルの mbstring の実行時設定では、設定例として、以下のように記載されていますが、実際にはそのようにはなりません。実際には、代替文字として ?
が出力されます。
mbstring.substitute_character = none ; 文字を出力しない
原因は、ini ファイルで none を設定すると、0(null) に変換されてしまうためです。substitute_character が未設定の場合、substitute_character には 63("?") が設定されます(mb_get_info() で確認可能)。
現状(PHP 5.4.11 以前)では、php.ini で substitute_character に none(代替文字を出力しない)を設定するには、以下の設定をする必要があります。
mbstring.substitute_character = "none"
または、PHP スクリプトの中で文字コード変換関数を実行する前に mb_substitute_character()
を実行します。
mb_substitute_character
('none'
);
以下が、PHP の mbstring で使用可能な文字コード名と MIME 名、エイリアスの一覧です。文字コードを指定する場合、大文字、小文字は区別されません。
以下の表での MIME 名は、mbstring で MIME 名として扱われているものです。正式な MIME 名とは違うものが入っています(7bit、8bit、HTML-ENTITIES、Quoted-Printable など)。
MIME 名については、IANA の文書を参照してください(http://www.iana.org/assignments/character-sets)。
PHP の文字コード名 | MIME 名 | エイリアス | 備考 |
---|---|---|---|
7bit | 7bit | - | - |
8bit | 8bit | binary | - |
ArmSCII-8 | ArmSCII-8 | ArmSCII-8, ArmSCII8, ARMSCII-8, ARMSCII8 | PHP 4.4.2 / PHP 5.1.0 から使用可能 |
ASCII | US-ASCII | ANSI_X3.4-1968, iso-ir-6, ANSI_X3.4-1986, ISO_646.irv:1991, US-ASCII, ISO646-US, us, IBM367, IBM-367*, cp367, csASCII | IBM-367 は PHP 5.4.0 で追加されました。 |
auto | - | unknown | - |
BASE64 | BASE64 | - | - |
BIG-5 | BIG5 | CN-BIG5, BIG-FIVE, BIGFIVE, CP950* | CP950 は PHP 5.4.0 以降はエイリアスでなくなりました |
byte2be | - | - | - |
byte2le | - | - | - |
byte4be | - | - | - |
byte4le | - | - | - |
CP50220 | ISO-2022-JP | - | PHP 5.3.3 から使用可能 |
CP50220raw | ISO-2022-JP | - | PHP 5.3.3 から使用可能 |
CP50221 | ISO-2022-JP | - | PHP 5.3.3 から使用可能 |
CP50222 | ISO-2022-JP | - | PHP 5.3.3 から使用可能 |
CP51932 | CP51932 | cp51932 | PHP 5.2.1 から使用可能 |
CP850 | CP850 | CP850, CP-850, IBM850*, IBM-850 | PHP 5.3.0 から使用可能。IBM850 は PHP 5.4.0 から使用可能。 |
CP866 | CP866 | CP866, CP-866, IBM866*, IBM-866 | IBM866 は PHP 5.4.0 から使用可能。 |
CP932 | Shift_JIS | MS932, Windows-31J, MS_Kanji | PHP 5.3.3 から使用可能。PHP 5.3.2 までは SJIS-win のエイリアス。 |
CP936 | CP936 | CP-936, GBK | - |
CP950 | BIG5 | - | PHP 5.4.0 から使用可能。PHP 5.4.0より前は BIG-5のエイリアス。 |
EUC-CN | CN-GB | CN-GB, EUC_CN, eucCN, x-euc-cn, gb2312 | - |
EUC-JP | EUC-JP | EUC, EUC_JP, eucJP, x-euc-jp | - |
EUC-JP-2004 | EUC-JP | EUC_JP-2004 | PHP 5.4.0 から使用可能 |
EUC-KR | EUC-KR | EUC_KR, eucKR, x-euc-kr | - |
EUC-TW | EUC-TW | EUC_TW, eucTW, x-euc-tw | - |
eucJP-win | EUC-JP | eucJP-open, eucJP-ms | eucJP-ms は PHP 5.2.1 以降で使用可能 |
GB18030 | GB18030 | gb-18030, gb-18030-2000 | PHP 5.4.0 から使用可能 |
HTML-ENTITIES | HTML-ENTITIES | HTML, html | - |
HZ | HZ-GB-2312 | - | - |
ISO-2022-JP | ISO-2022-JP | - | - |
ISO-2022-JP-MS | ISO-2022-JP | ISO2022JPMS | PHP 5.2.1 から使用可能 |
ISO-2022-JP-2004 | ISO-2022-JP-2004 | - | PHP 5.4.0 から使用可能 |
ISO-2022-JP-MOBILE#KDDI | ISO-2022-JP | ISO-2022-JP-KDDI | PHP 5.4.0 から使用可能 |
ISO-2022-KR | ISO-2022-KR | - | - |
ISO-8859-1 | ISO-8859-1 | ISO_8859-1, latin1 | - |
ISO-8859-2 | ISO-8859-2 | ISO_8859-2, latin2 | - |
ISO-8859-3 | ISO-8859-3 | ISO_8859-3, latin3 | - |
ISO-8859-4 | ISO-8859-4 | ISO_8859-4, latin4 | - |
ISO-8859-5 | ISO-8859-5 | ISO_8859-5, cyrillic | - |
ISO-8859-6 | ISO-8859-6 | ISO_8859-6, arabic | - |
ISO-8859-7 | ISO-8859-7 | ISO_8859-7, greek | - |
ISO-8859-8 | ISO-8859-8 | ISO_8859-8, hebrew | - |
ISO-8859-9 | ISO-8859-9 | ISO_8859-9, latin5 | - |
ISO-8859-10 | ISO-8859-10 | ISO_8859-10, latin6 | - |
ISO-8859-13 | ISO-8859-13 | ISO_8859-13 | - |
ISO-8859-14 | ISO-8859-14 | ISO_8859-14, latin8 | - |
ISO-8859-15 | ISO-8859-15 | ISO_8859-15 | - |
ISO-8859-16 | ISO-8859-16 | ISO_8859-16 | - |
JIS | ISO-2022-JP | - | - |
JIS-ms | ISO-2022-JP | - | - |
KOI8-R | KOI8-R | KOI8-R, KOI8R | - |
KOI8-U | KOI8-U | KOI8-U, KOI8U | PHP 5.3.0 から使用可能 |
pass | - | none | - |
Quoted-Printable | Quoted-Printable | qprint | - |
SJIS | Shift_JIS | x-sjis, SHIFT-JIS | - |
SJIS-2004 | Shift_JIS | SJIS2004, Shift_JIS-2004 | PHP 5.4.0 から使用可能 |
SJIS-mac | Shift_JIS | MacJapanese, x-Mac-Japanese | PHP 5.4.0 から使用可能 |
SJIS-win | Shift_JIS | SJIS-open, SJIS-ms, CP932*, Windows-31J*, MS_Kanji* | SJIS-ms は PHP 5.3.3 から使用可能。CP932, Windows-31J, MS_Kanji は PHP 5.3.3 から SJIS-win のエイリアスでなくなりました。 |
SJIS-Mobile#DOCOMO | Shift_JIS | SJIS-DOCOMO, shift_jis-imode, x-sjis-emoji-docomo | PHP 5.4.0 から使用可能 |
SJIS-Mobile#KDDI | Shift_JIS | SJIS-KDDI, shift_jis-kddi, x-sjis-emoji-kddi | PHP 5.4.0 から使用可能 |
SJIS-Mobile#SOFTBANK | Shift_JIS | SJIS-SOFTBANK, shift_jis-softbank, x-sjis-emoji-softbank | PHP 5.4.0 から使用可能 |
UCS-2 | UCS-2 | ISO-10646-UCS-2, UCS2, UNICODE | - |
UCS-2BE | UCS-2BE | - | - |
UCS-2LE | UCS-2LE | - | - |
UCS-4 | UCS-4 | ISO-10646-UCS-4, UCS4 | - |
UCS-4BE | UCS-4BE | - | - |
UCS-4LE | UCS-4LE | - | - |
UHC | UHC | CP949 | - |
UTF-16 | UTF-16 | utf16 | - |
UTF-16BE | UTF-16BE | - | - |
UTF-16LE | UTF-16LE | - | - |
UTF-32 | UTF-32 | utf32 | - |
UTF-32BE | UTF-32BE | - | - |
UTF-32LE | UTF-32LE | - | - |
UTF-7 | UTF-7 | utf7 | - |
UTF7-IMAP | - | - | - |
UTF-8 | UTF-8 | utf8 | - |
UTF-8-Mobile#DOCOMO | UTF-8 | UTF-8-DOCOMO, UTF8-DOCOMO | PHP 5.4.0 から使用可能 |
UTF-8-Mobile#KDDI-A | UTF-8 | - | PHP 5.4.0 から使用可能 |
UTF-8-Mobile#KDDI-B | UTF-8 | UTF-8-Mobile#KDDI, UTF-8-KDDI, UTF8-KDDI | PHP 5.4.0 から使用可能 |
UTF-8-Mobile#SOFTBANK | UTF-8 | UTF-8-SOFTBANK, UTF8-SOFTBANK | PHP 5.4.0 から使用可能 |
UUENCODE | x-uuencode | - | - |
wchar | - | - | - |
Windows-1251 | Windows-1251 | CP1251, CP-1251, WINDOWS-1251 | - |
Windows-1252 | Windows-1252 | cp1252 | - |
Windows-1254 | Windows-1254 | CP1254, CP-1254, WINDOWS-1254 | PHP 5.3.0 から使用可能 |
PHP 5.2.1 以降では、PHP マニュアル : マルチバイト文字列関数 (mbstring) : サポートされるエンコーディング に記載されている文字コードの他に、CP51932
と ISO-2022-JP-MS
が文字コードとして使用できるようになりました。([PHP-dev 1345] PHP への CP932 系エンコーディングの追加パッチ)
CP51932 (Windows Codepage 51932) -------------------------------- o Windows で使用されている日本語EUC。 o EUC-JP の JIS X 0212 およびユーザー定義文字には対応していない。 o 機種依存文字のコード割り当てが eucJP-win とは異なる。 ISO-2022-JP-MS -------------- o ISO-2022-JP に次の文字集合を追加 文字集合 エスケープシーケンス NEC特殊文字 (JIS X 0208 13区) ESC $ B NEC選定IBM拡張文字 (JIS X 0208 89区〜92区) ESC $ B JIS X 0201 片仮名 ESC ( I ユーザー定義文字 ESC $ ( ? o Windows Codepage 50221 (Windows の 7ビットJISコード) とは、ユーザー 定義文字を除いて互換性あり。
これらの文字コードの詳細については、以下の情報を参照してください。
CP51932 (Legacy Encoding Project)
ISO-2022-JP-MS (Legacy Encoding Project)
PHP 5.4.0 から、mbstring で携帯各社(DoCoMo, KDDI, Softbank)の絵文字が追加されました。
絵文字変換に関連して追加されたエンコーディングは以下の通りです。
ISO-2022-JP-MOBILE#KDDI
(エイリアス: ISO-2022-JP-KDDI
)
SJIS-Mobile#DOCOMO
(エイリアス: SJIS-DOCOMO
, shift_jis-imode
, x-sjis-emoji-docomo
)
SJIS-Mobile#KDDI
(エイリアス: SJIS-KDDI
, shift_jis-kddi
, x-sjis-emoji-kddi
)
SJIS-Mobile#SOFTBANK
(エイリアス: SJIS-SOFTBANK
, shift_jis-softbank
, x-sjis-emoji-softbank
)
UTF-8-Mobile#DOCOMO
(エイリアス: UTF-8-DOCOMO
, UTF8-DOCOMO
)
UTF-8-Mobile#KDDI-A
UTF-8-Mobile#KDDI-B
(エイリアス: UTF-8-Mobile#KDDI
, UTF-8-KDDI
, UTF8-KDDI
)
UTF-8-Mobile#SOFTBANK
(エイリアス: UTF-8-SOFTBANK
, UTF8-SOFTBANK
)
入力文字列を UTF-8 としてデータベースやファイルに格納することが想定されています。出力時は、必要に応じて機種を判別して各社に対応した Shift_JIS に変換します。
DoCoMo の絵文字を含む Shift_JIS の文字列を UTF-8 に変換する場合の例は以下の通りです。
$utf8 = mb_convert_encoding( $sjis, "UTF-8-Mobile#DoCoMo", "SJIS-Mobile#DoCoMo" );
また、UTF-8-Mobile#KDDI-B
は KDDI の非公式マッピングですが、UTF-8-Mobile#KDDI
がエイリアスになっており、実際にはこちらを使用した方が良さそうです(KDDI (AU)携帯電話のUTF-8絵文字の仕様)。
以下のページに実装を担当された廣川さんの解説があります。
libmlfl(mbstring)への絵文字のサポート追加 (Opensource days)
mbstring/libmbfl モバイル用UTF-8 (Opensource days)
KDDI (AU)携帯電話のUTF-8絵文字の仕様 (Opensource days)
mbstring(libmbfl)で携帯絵文字サポート:Unicode 6.0に含まれない文字 (Opensource days)
マルチバイト正規表現は PHP 4.2.0 から導入されました。PHP 4.x は全て Ruby と同じ正規表現エンジンを使用しています。
PHP 4.x で使用できる正規表現演算子については、PHP で使用できる正規表現演算子(mb_ereg)(正規表現メモ)が分かりやすいと思います。
PHP 5.0.0 から正規表現エンジンに onigruma を使用しています。PHP 5.0.0 以降で使用できる正規表現演算子については、鬼車 正規表現を参照してください。onigruma のバージョンは PHP 5.0.x は 3.7.0、PHP 5.1.x は 3.7.1、PHP 5.2.x は 4.4.4、PHP 5.3.x と PHP 5.4.x は 4.7.1 が使用されています。
マルチバイト正規表現で使用する文字コードは mb_regex_encoding()
を使用して変更することができます。設定していない場合、内部文字コード(internal_encoding
)が使用されます。mb_regex_encoding()
で指定可能な文字コード一覧を、マルチバイト正規表現で指定可能な文字コードにまとめました。
マルチバイト正規表現関数は以下の通りです。各関数は PHP マニュアルの関数説明にリンクしています。
返り値の型 | 関数名 | 引数 | 使用可能バージョン |
---|---|---|---|
bool | mb_ereg_match() |
string $pattern , string $string [, string $option = "msr" ] |
4.2.0 以降 |
string | mb_ereg_replace() |
string $pattern , string $replacement , string $string [, string $option = "msr" ] |
4.2.0 以降 |
string | mb_ereg_replace_callback() |
string $pattern , callable $callback , string $string [, string $option = "msr" ] |
5.4.1 以降 |
int | mb_ereg_search_getpos() |
void | 4.2.0 以降 |
array | mb_ereg_search_getregs() |
void | 4.2.0 以降 |
bool | mb_ereg_search_init() |
string $string [, string $pattern [, string $option = "msr" ]] |
4.2.0 以降 |
array | mb_ereg_search_pos() |
[ string $pattern [, string $option = "ms" ]] |
4.2.0 以降 |
array | mb_ereg_search_regs() |
[ string $pattern [, string $option = "ms" ]] |
4.2.0 以降 |
bool | mb_ereg_search_setpos() |
int $position |
4.2.0 以降 |
bool | mb_ereg_search() |
[ string $pattern [, string $option = "ms" ]] |
4.2.0 以降 |
int | mb_ereg() |
string $pattern , string $string [, array $regs ] |
4.2.0 以降 |
string | mb_eregi_replace() |
string $pattern , string $replace , string $string [, string $option = "msri" ] |
4.2.0 以降 |
int | mb_eregi() |
string $pattern , string $string [, array $regs ]] |
4.2.0 以降 |
mixed | mb_regex_encoding() |
[ string $encoding ] |
4.2.0 以降 |
string | mb_regex_set_options() |
[ string $options ] |
4.3.0 以降 |
array | mb_split() |
string $pattern , string $string [, int $limit = -1 ] |
4.2.0 以降 |
mb_regex_set_options()
は PHP 4.3.0 で追加されました。詳細は、マルチバイト正規表現のオプションを参照してください。
mb_ereg_replace_callback()
は PHP 5.4.1 で追加されました。詳細は、mb_ereg_replace_callback() 関数を参照してください。
mb_regex_encoding()
で指定可能な文字コードは以下の通りです(PHP 5.3.0 の時点)。問題となることはほぼないと思いますが、mbstring の内部文字コード(internal_encoding
)で指定可能な文字コードと一部違いがあります。PHP 5.0.0 以降、以下の指定可能な文字コードに変更はありません。
文字コード名 | エイリアス |
---|---|
ASCII | US-ASCII, US_ASCII, ISO646 |
BIG5 | BIG-5, BIGFIVE, CN-BIG5, BIG-FIVE |
EUC-CN | EUCCN, EUC_CN, GB-2312, GB2312 |
EUC-JP | EUCJP, X-EUC-JP, UJIS, EUCJP, EUCJP-WIN |
EUC-KR | EUCKR, EUC_KR |
EUC-TW | EUCTW, EUC_TW |
ISO-8859-1 | ISO8859-1, ISO_8859_1, ISO8859_1 |
ISO-8859-2 | ISO8859-2, ISO_8859_2, ISO8859_2 |
ISO-8859-3 | ISO8859-3, ISO_8859_3, ISO8859_3 |
ISO-8859-4 | ISO8859-4, ISO_8859_4, ISO8859_4 |
ISO-8859-5 | ISO8859-5, ISO_8859_5, ISO8859_5 |
ISO-8859-6 | ISO8859-6, ISO_8859_6, ISO8859_6 |
ISO-8859-7 | ISO8859-7, ISO_8859_7, ISO8859_7 |
ISO-8859-8 | ISO8859-8, ISO_8859_8, ISO8859_8 |
ISO-8859-9 | ISO8859-9, ISO_8859_9, ISO8859_9 |
ISO-8859-10 | ISO8859-10, ISO_8859_10, ISO8859_10 |
ISO-8859-11 | ISO8859-11, ISO_8859_11, ISO8859_11 |
ISO-8859-13 | ISO8859-13, ISO_8859_13, ISO8859_13 |
ISO-8859-14 | ISO8859-14, ISO_8859_14, ISO8859_14 |
ISO-8859-15 | ISO8859-15, ISO_8859_15, ISO8859_15 |
ISO-8859-16 | ISO8859-16, ISO_8859_16, ISO8859_16 |
KOI8 | KOI-8 |
KOI8R | KOI8-R, KOI-8R |
SJIS | CP932, MS932, SHIFT_JIS, SJIS-WIN, WINDOWS-31J |
UCS-4 | UTF-32, UTF-32BE |
UCS-4LE | UTF-32LE |
UTF-16 | UTF-16BE |
UTF-16LE | - |
UTF-8 | UTF8 |
mb_regex_set_options()
と mb_ereg_replace()
の第4引数、mb_eregi_replace()
の第4引数、mb_ereg_replace_callback()
の第4引数で指定できる正規表現のオプションです。
mb_ereg()
, mb_eregi()
, mb_split()
はオプションが設定できません。これらの関数は mb_regex_set_options()
を使用することでオプションを設定できます。
mb_ereg_replace()
の説明にオプションに関して少しだけ説明がありますが、説明されていないオプションもあります。以下に PHP で使用可能なマルチバイト正規表現関数で使用可能なオプションをまとめました。
以下の表のオプションが使用できます。何も設定していない場合は "p"
(POSIX 互換モード) になります。
オプション | 説明 |
---|---|
i | 大文字・小文字を区別しない |
x | 空白を無視 |
m | マルチラインモード(改行文字も ". " に含まれる) |
s | シングルラインモード('^ ' を '\A ', '$ ' を '\Z ' とみなす) |
p | POSIX モード(m と s の両方指定と同等) |
l | 最長一致(Longest Match) |
e | 文字列 $replacement を PHP の式として評価(mb_ereg_replace() , mb_eregi_replace() で使用可能) |
以下の表のオプションが使用できます。何も設定していない場合は "pr"
(POSIX 互換モード(シングルラインモードおよび、マルチラインモード)、Ruby 形式の正規表現パターン文法)になります。
オプション | 説明 |
---|---|
i | 大文字・小文字を区別しない |
x | 空白を無視 |
m | マルチラインモード(改行文字も ". " に含まれる) |
s | シングルラインモード('^ ' を '\A ', '$ ' と '\Z ' とみなす) |
p | POSIX モード(m と s を両方指定と同等 ) |
l | 最長一致(Longest Match) |
n | 空マッチを無視 |
e | 文字列 $replacement を PHP の式として評価(mb_ereg_replace() , mb_eregi_replace() で使用可能) |
r | Ruby 形式の正規表現パターン文法 |
j | Java 形式の正規表現パターン文法 |
u | GNU 形式の正規表現パターン文法 |
g | Grep 形式の正規表現パターン文法 |
c | Emacs 形式の正規表現パターン文法 |
z | Perl 形式の正規表現パターン文法 |
b | 基本 POSIX 形式の正規表現パターン文法 |
d | 拡張 POSIX 形式の正規表現パターン文法 |
mb_ereg_replace_callback()
は PHP 5.4.1 で追加された関数です。正規表現にマッチした文字列に対してコールバック関数による置換ができます。
以前から、mb_ereg_replace()
のオプションに "e
" を渡すことで同様のことは可能でしたが、信頼できない入力に使用するとリモートからコード実行される可能性がありました。
信頼できない入力に対しては、絶対に e 修正子を使用してはいけません。 (preg_replace() と同様)自動的なエスケープは行いません。このことを忘れていると、自分の書いたアプリケーションにリモートコード実行の脆弱性を作りこんでしまうことになります。
PHP 5.4.1 からは、mb_ereg_replace_callback()
を使用することで、置換にコールバック関数を使用できるようになります。
置換文字列の文字コードが UTF-8 の場合は、preg_replace_callback()
も使用できます。u
修飾子を使用すると、UTF-8 の文字列を扱えます。こちらは PHP 4.1.0(Win32 では、PHP 4.2.3)からサポートされています。
PHP の mbstring には多くのバグが報告され、修正されています。備忘録として以下にまとめました。PHP のバージョンは php.net でダウンロードできるソースのバージョンです。
概要
PHP 4.3.11、PHP 5.0.0 で混入したバグにより、mb_detect_encoding()
が正しく文字コード検出をできないことがありまます。
これが原因で mb_convert_encoding()
が失敗することがあり、文字化けを起こすことがあります。
バグを含んでいるバージョン
PHP 4.3.11, PHP 5.0.0 - PHP 5.0.4
修正されたバージョン
PHP 4.4.0, PHP 5.0.5
参考リンク
概要
PHP 4.4.0 で混入したバグにより、mb_encode_mimeheader()
でマルチバイト文字を含む文字列のエンコードが失敗する事があります。
これが原因で、mb_encode_mimeheader()
でエンコードした文字列や mb_send_mail()
を使用して送信したメールの Subject が文字化けする事があります。
バグを含んでいるバージョン
PHP 4.4.0 - PHP 4.4.1, PHP 5.0.4 - PHP 5.0.5
修正されたバージョン
PHP 4.4.2, PHP 5.1.0
参考リンク
概要
PHP 4.4.1 では、mb_send_mail()
の第 5 引数に指定できる sendmail
コマンドに対するオプションが無視されるという問題がありました。
これが原因で、Envelope From の情報を設定できずに、メールを送信できなくなるなどの問題が発生しました。
バグを含んでいるバージョン
PHP 4.4.1
修正されたバージョン
PHP 4.4.2
参考リンク
概要
メールヘッダの Subject: の行で、文字数が RFC で規定されている 76 文字未満を満たさないことがありました。mb_encode_mimeheader()
の第5引数でユーザが indent を指定してから、mb_send_mail()
に渡すことで、この問題を回避できるようになりました。
詳しくは、上記のmb_encode_mimeheader() 関数の第5引数(indent) を参照してください。
バグを含んでいるバージョン
PHP 4.4.1 以前, PHP 5.0.5 以前
修正されたバージョン
PHP 4.4.2, PHP 5.1.1
参考リンク
概要
mail()
では To: に改行コードが入っていた場合は改行コード除去する処理が行われるようになっていましたが、PHP 4.4.1 以前、PHP 5.0.5 以前では、mb_send_mail()
では改行コードの除去が行われていませんでした。
このため、To: に改行コードが含まれていた場合、任意のメールヘッダを追加される危険性がありました。
バグを含んでいるバージョン
PHP 4.4.1 以前, PHP 5.0.5 以前
修正されたバージョン
PHP 4.4.2, PHP 5.1.0
参考リンク
概要
mb_parse_str()
が一つの引数のみで呼び出された場合、memory_limit 制限などで割り込まれると内部的に有効化された register_globals 設定が無効化されません。このため、この Apache のプロセスは PHP コードからは検出不可能な状態で register_globals 設定が有効にされてしまうという問題があります。
バグを含んでいるバージョン
PHP 4.4.6 以前, PHP 5.2.1 以前
修正されたバージョン
PHP 4.4.7, PHP 5.2.2
参考リンク
MOPB-26-2007:PHP mb_parse_str() register_globals Activation Vulnerability (php-security.org)
MOPB-26-2007:PHP mb_parse_str() register_globals Activation Vulnerability 日本語訳 (yohgaki's blog)
PHP Mb_Parse_Str Function Register_Globals Activation Weakness (SecurityFocus)
概要
Apache モジュール版の PHP の問題です。
PHP の内部の文字コードリストがリクエストごとに初期化されないという問題があります。mb_language()
で文字コードリストが変更されると、次のリクエストでも引き継がれてしまい、php.ini で設定した文字コードリストに戻りません。
スクリプト側で毎回 mb_language()
を実行していれば問題は出ませんが、php.ini の設定に頼っていると、意図しない動作になる可能性があります。
再現コードは次の通りです。試す場合は、php.ini の mbstring の設定を mbstring.language = Japanese
にしてください。
<?phpmb_detect_order
('auto'
); echoimplode
(', '
,mb_get_info
('detect_order'
) ); // (*1)mb_language
('Russian'
);mb_detect_order
('auto'
); echoimplode
(', '
,mb_get_info
('detect_order'
) ); // (*2)
初回のアクセス時(Apache 起動後)には、以下のようになります。
(*1) : ASCII, JIS, UTF-8, EUC-JP, SJIS (*2) : ASCII, UTF-8, KOI8-R, Windows-1251, CP866
何度かアクセスすると、結果が以下のように変わります。
(*1) : ASCII, UTF-8, KOI8-R, Windows-1251, CP866 (*2) : ASCII, UTF-8, KOI8-R, Windows-1251, CP866
バグを含んでいるバージョン
PHP 5.2.5 以前
修正されたバージョン
PHP 5.2.6
参考リンク
PHP 5.2.6 では、以下のバグ修正が行われています。
mb_strtoupper(), mb_strtolower()
で不正な文字コードを第2引数に指定すると不要なエラーメッセージまで出力される
PHP 5.2.5 以前では、mb_strtoupper()
と mb_strtolower()
において、第2引数(文字コード)に不正な文字コード名を指定すると、不要なエラーメッセージまで出力されます。
PHP 5.2.6 からは、不要な場合は、エラーメッセージが出力されないように修正されました。
参考: PHP Bugs: #43998: Two error messages returned for incorrect encoding for mb_strto[upper|lower] (bugs.php.net)
mb_ereg()
の第1引数に不正な値を入力しても成功する
PHP 5.2.5 以前では、mb_ereg()
の第1引数(検索パターン文字列)に不正な値(NULL, FALSE, 空文字列)を入力しても、int(1) が返ります。
PHP 5.2.6 からは、第1引数に不正な値が入力されると、エラーになるように修正されました。
参考: PHP Bugs: #43994: mb_ereg 'successfully' matching incorrectly (bugs.php.net)
mb_strrpos()
の第3引数に負の値を指定するとバイト単位で扱われる
PHP 5.2.5 以前では、mb_strrpos()
の第3引数(検索オフセット)に負の値を指定すると、文字数単位ではなく、バイト単位で扱われていました。
このため、マルチバイト文字列が含まれている文字列の場合、正しい結果が得られない可能性がありました。
PHP 5.2.6 からは、文字列単位で扱われるように修正されました。
参考: PHP Bugs: #43841: mb_strrpos offset is byte count for negative values (bugs.php.net)
mb_strpos()
の文字列境界チェックが文字数単位ではなくバイト単位で行われる
mb_strpos()
では、第3引数(検索オフセット)の数値を設定すると、指定した文字数から検索を開始します。第3引数で第1引数(調べたい文字列)より大きな文字数を指定すると、エラーになるはずでしたが、PHP 5.2.5 以前では、この限界値の算出をバイト単位で行っていました。
PHP 5.2.6 からは、第1引数(調べたい文字列)よりも第1引数(調べたい文字列)に大きな数値を指定すると、エラーになるように修正されました。
参考: PHP Bugs: #43840: mb_strpos bounds check is byte count rather than a character count (bugs.php.net)
mb_ereg*_replace()
の PHP 式評価時の第2引数に不正な値を入力すると PHP がクラッシュする可能性がある
mb_ereg_replace()
と mb_eregi_replace()
では、第4引数(オプション)に 'e
' を指定した場合、第2引数(置換文字列) が PHP の式として評価されます。
この 'e
' オプションを使用した場合、ある文字列('<
', '>
', '?
' など)を直接第2引数に指定すると PHP がクラッシュする可能性があります。
再現コードは以下のようになります。
php -r "mb_ereg_replace( 'str', '<', 'sample str', 'e' );"
参考: PHP Bugs: #43301: mb_ereg*_replace() crashes when replacement string is invalid PHP expression (bugs.php.net)
概要
以下の条件の場合、mb_check_encoding()
の第1引数に NULL バイトから始まる文字列を入力すると、不正な文字コードでも TRUE
を返します。
substitute_character
(内部変数) が none
の場合
第2引数(文字コード)にUTF-8を指定した場合
再現コード:
<?php // mb_substitute_character( 'none' ); $str = "\0\xFF\xFF\xFF"; //不正な文字列 $result = mb_check_encoding( $str, "UTF-8" ); var_dump( $result );
結果は以下のようになります。
PHP 5.2.6
bool(true)
PHP 5.2.7
bool(false)
回避方法として、mb_check_encoding()
の代わりに以下のような代替関数を使用すれば正しい結果を得ることが可能です。
function check_encoding( $val, $enc ) { return $val === mb_convert_encoding( $val, $enc, $enc ); }
バグを含んでいるバージョン
PHP 5.2.6 以前
修正されたバージョン
PHP 5.2.7
参考リンク
mb_check_encoding()
で第2引数に SJIS または SJIS-win を指定し、第1引数に SJIS としては不正な文字列 "0x81 0x3a" などを指定すると TRUE を返します。
どの範囲を TRUE と誤判定するのかは mb_check_encodingは何をチェックするのか(その1 SJIS編) を参照してください。
バグを含んでいるバージョン
PHP 5.2.8 以前
修正されたバージョン
PHP 5.2.9
参考リンク
PHP 5.1.2 〜 PHP 5.2.8 では、以下の条件において、無限ループが発生することがあります。
php.ini が以下の設定で、クライアントからのリクエスト時に不正な文字列を受け取った場合
mbstring.http_input
= auto
mbstring.encoding_translation
= On
mbstring.strict_detection
= On
php.ini が上記と同様の設定で、mb_parse_str()
を使用して文字列を変換した場合(この場合、mbstring.encoding_translation
= Off
でも再現します)
再現コードは以下の通りです。
mb_parse_str( "a=%fc", $dummy );
内部変数 strict_detection
を有効にして mb_convert_variables()
で不正な文字列を変換した場合
再現コードは以下の通りです。
ini_set( 'mbstring.strict_detection', 1 ); mb_convert_variables( 'UTF-8', 'auto', $a = array( "\xfc" ) );
PHP 5.0.5 以前のバージョンでは、内部変数 strict_detection
が存在しないため、この問題は発生しません。
バグを含んでいるバージョン
PHP 5.1.2 - PHP 5.2.8
修正されたバージョン
PHP 5.2.9
参考リンク
Bug #45239 Hang (99.9%CPU) when encoding_translation is on (bugs.php.net)
PHP 5.2.9 では、以下のバグ修正が行われています。
mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する(PHP 5.2.8 以前)
内部変数 strict_detection が有効な状態で、不正な文字列の文字コード検出を行うと無限ループになる(PHP 5.1.2 - PHP 5.2.8)
ユーロ記号(€: U+20AC)をユニコードから CP936(中国語簡体字) に変換すると不正な文字列になる
PHP 5.2.8 以前でユーロ記号をユニコード文字列(U+20AC)から CP936 に変換すると、不正な文字列(0x00 0x80)になります。
php-5.2.8 -r 'var_dump( bin2hex( mb_convert_encoding( "\x20\xac", "CP936", "Unicode" ) ) );' string(4) "0080"
PHP 5.2.9 で以下のように、正しく変換するように修正されました。
php-5.2.9 -r 'var_dump( bin2hex( mb_convert_encoding( "\x20\xac", "CP936", "Unicode" ) ) );' string(2) "80"
参考: Bug #46843 Broken Euro symbol conversion into CP936 (bugs.php.net)
mb_stripos()
, mb_strripos()
で offset(第3引数) の扱いが不正
PHP 5.2.8 以前のmb_stripos()
, mb_strripos()
では、offset(第3引数)を使用した場合、stripos()
や strripos()
の結果と違う場合がありました。
PHP 5.2.9 では、stripos()
と strripos()
と同等になるように修正されました。
参考: PHP Bugs: #45923: mb_st[r]ripos() offset not handled correctly (bugs.php.net)
mb_detect_encoding()
で不正な文字コードを指定して検出を行うと、その後で Segmentation Fault を起こすことがある
PHP 5.2.8 以前では、存在しない文字コードを指定して文字コードを検出した後、mb_convert_encoding()
などを使用すると、メモリの二重解放が発生します。このため、Segmentation Fault が発生することがあります。
PHP 5.2.9 では、メモリの二重解放を回避するように修正されました。
参考: Bug #47245 crash following mb_detect_encoding (bugs.php.net)
mb_strpos()
の offset(第3引数) に負の値を渡すとバイト単位でカウントされる
mb_strpos()
は offset(第3引数) に負の値を入力できますが、その場合、文字数ではなく、バイト単位で値を返します。
この問題は PHP 5.2.9 で修正されました。
参考: PHP Bugs: #43841: mb_strrpos offset is byte count for negative values (bugs.php.net)
.htaccess で mbstring.internal_encoding
を 指定し、さらに、encoding_translation
が On
の場合に発生する問題です。
上記の環境で、リクエストで受け取った文字列が、.htaccess で設定した mbstring.internel_encoding
に変換されないことがあります。
バグを含んでいるバージョン
PHP 5.2.9 以前
修正されたバージョン
PHP 5.2.10, PHP 5.3.0
参考リンク
PHP 5.3.0 より前のバージョンの mb_check_encoding() では、サロゲートペア領域(U+D800 - U+DFFF)の UTF-8 による表現を判定すると TRUE を返します。
UTF-8 でサロゲートペア領域の文字は直接符号化してはならないことになっています(RFC3629)。
$ php-5.2.10 -r 'var_dump( mb_check_encoding( "\xed\xa0\x80","UTF-8" ) );' bool(true)
"\xed\xa0\x80"
は UTF-8 で U+D800 を表現したものです。
PHP 5.3.0 で、この判定が FALSE になるように修正されました。
バグを含んでいるバージョン
PHP 5.3.0 より前のバージョン
修正されたバージョン
PHP 5.3.0
参考リンク
mb_check_encodingは何をチェックするのか(その3 UTF-8編) (hnw の日記)
UTF-8: サロゲートペアの扱い (Wikipedia)
PHP 5.2.10 および、PHP 5.3.0 では、parse_str()
を使用すると、内部エンコーディングが php.ini または、.htaccess で設定した値に戻るという問題があります。
バグを含んでいるバージョン
PHP 5.2.10, PHP 5.3.0
修正されたバージョン
PHP 5.2.11, PHP 5.3.1
参考リンク
BOM 付き UTF-16 の文字列(Little Endian)を mb_convert_encoding() で別の文字コードに変換すると文字列が壊れる問題が修正されました。
以下のようなテストコードで確認可能です。
<?php // テスト(UTF-16: BOM 付き Little Endian の文字列) $str = "\xFF\xFE\xC6\x30\xB9\x30\xC8\x30"; var_dump( bin2hex( mb_convert_encoding( $str, "UTF-16", "UTF-16" ) ) ); var_dump( mb_convert_encoding( $str, "UTF-8", "UTF-16" ) );
PHP 5.2.12 以降、PHP 5.3.2 以降では、以下のように正常に変換されます。
string(12) "30c630b930c8" string(9) "テスト"
バグを含んでいるバージョン
PHP 5.2.11 以前, PHP 5.3.1 以前
修正されたバージョン
PHP 5.2.12, PHP 5.3.2
参考リンク
mb_detect_encoding() は、strict_mode が On の場合、先行バイトのみ正しく、後続バイトが不正な場合、false を返します。しかし、最後に先行バイトのみの文字列であった場合、true を返していた問題が修正されました。
以下のようなテストコードで確認可能です。
<?php var_dump( "SJIS" === mb_detect_encoding( "\x81", "SJIS", TRUE ) );
PHP 5.2.12 以降、PHP 5.3.2 以降では、以下のように判定されます。
bool(false)
バグを含んでいるバージョン
PHP 5.2.11 以前, PHP 5.3.1 以前
修正されたバージョン
PHP 5.2.12, PHP 5.3.2
参考リンク
2008.08.08 に PHP4 系の最終版である PHP 4.4.9 がリリースされました。これ以降は、PHP 4.x はリリースされることはありません。PHP 5.x に移行することが推奨されていますが、まだ PHP 4.x を使い続ける必要がある場合も多いかもしれません。参考までに、PHP 4.4.9 で残っていて、比較的重要だと思われる mbstring 関連のバグを知っている範囲でまとめてみました。
(2008.11.09 追記) PHP 5.2.6 で修正された mbstring 関係のバグ一覧 に書いた 5. 以外は PHP 4.4.9 でも影響を受けることを確認しました(ここには 2. の mb_ereg()
の件以外については大きなバグではないと思いますので、この章には書いていません)。5. については正規表現エンジンが違う影響だと思いますが、PHP 4.4.9 では影響は確認できませんでした。
説明
PHP 4.4.9 で使用されているマルチバイト正規表現エンジンは古く、UTF-8 の文字列の扱いが不十分であるという問題があります。このため、mb_ereg()
等の関数で一部の文字がマッチしないなどの問題があります。PHP 5.x ではマルチバイト正規表現エンジンが変更されています。PHP 5.x では UTF-8 を正しく扱えますので、この問題はありません。
(2008.08.14): 記述の修正、追記、リンクの追加を行いました。
回避方法
PCRE ライブラリを利用する
UTF-8 の文字列を正規表現で扱う場合、preg_match()
や preg_replace()
を使用します。これらの Perl 互換の正規表現関数では u 修飾子(パターン修飾子を参照)を使用することで、UTF-8 の文字列を扱えます。
EUC-JP 等、他の文字コードに変換してから処理する
EUC-JP 等であれば、正しく処理できますので、先に他の文字コードに変換してから処理を行い、処理終了後に UTF-8 に戻すことでこの問題を回避することができます。
参考リンク
説明
php.ini で設定していない mbstring の内部変数がある場合、.htaccess や ini_set()
で設定した mbstring の内部変数が別のリクエストに反映されてしまうという問題です。internal_encoding
や script_encoding
、substitute_character
などが別のリクエストに反映されることが確認されています。この問題は PHP 5.2.7 で修正される予定ですが、PHP 4.4.9 では修正されていません。
回避方法
以下のどちらかを行うことで回避できます
php.ini や .htaccess で初期値を設定する
使用する mbstring の内部変数をスクリプトの最初で全て設定する
参考リンク
説明
上記で紹介した "auto" の内部で扱われる文字コードリストがリクエストごとに初期化されない(PHP 5.2.5 以前)と同じ問題です。mb_language()
で文字コードリストを変更すると、次のリクエストでも引き継がれてしまい、php.ini で設定した文字コードリストに戻りません。PHP 4.4.9 では修正されませんでした。
回避方法
PHP スクリプトの最初で mb_language()
を実行し、使用言語を指定すれば問題を回避できます。
参考リンク
説明
上記の PHP 5.2.6 で修正された mbstring 関係のバグ一覧 で紹介したものと同じ問題です。mb_ereg()
の第1引数(検索パターン文字列)に不正な値(NULL, FALSE, 空文字列)を入力しても、int(1) が返ってきます。この問題も PHP 4.4.9 では修正されませんでした。
回避方法
第1引数の入力文字列を確認してから使用すれば問題を回避できます。
参考リンク
説明
上記の mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す(PHP 5.2.6 以前)と同様の問題です。この問題は PHP 5.2.7 で修正されましたが、PHP 4.4.9 では残っています。
回避方法
第1引数の文字列の NULL バイトを先に削除する、または上記で紹介した代替関数を使用すれば問題は回避できます。
参考リンク
説明
上記の mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する(PHP 5.2.8 以前)と同様の問題です。この問題は PHP 5.2.9 で修正されましたが、PHP 4.4.9 では残っています。
回避方法
簡単な回避方法はありません。入力文字列が SJIS や SJIS-win として妥当かどうかを判定したい場合、以下のように preg_match()
と正規表現を使用する方法があります。
// SJIS の場合 (bool)preg_match( '/^(?:[\x00-\x7f\xa1-\xdf]|(?:[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc]))*$/', $str ); // SJIS-win の場合 (bool)preg_match( '/^(?:[\x00-\x7f\xa1-\xdf]|(?:[\x81-\x9f\xe0-\xfc][\x40-\x7e\x80-\xfc]))*$/', $str );
参考リンク
説明
上記の PHP 5.2.9 で修正された mbstring 関係のバグ一覧 で紹介したものと同じ問題です。この問題は PHP 5.2.9 で修正されましたが、PHP 4.4.9 では残っています。
回避方法
ありません。
参考リンク
説明
上記の PHP 5.2.9 で修正された mbstring 関係のバグ一覧 で紹介したものと同じ問題です。この問題は PHP 5.2.9 で修正されましたが、PHP 4.4.9 では残っています。
回避方法
mb_detect_encoding()
に不正な文字コードを指定されないようにすれば回避できます。
参考リンク
説明
上記の mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す(PHP 5.3.0 より前) で紹介したものと同様の問題です。
回避方法
直接の回避方法はありません。UTF-8 の文字コード判定に、mb_check_encoding()
ではなく、正規表現を使用することで、厳密なチェックを行うことが可能です。
説明
上記の mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある(PHP 5.2.12 以前, PHP 5.3.0) で紹介したものと同様の問題です。
回避方法
直接の回避方法はありません。正規表現を使用して厳密な文字コード検出を行うことは可能です。
説明
上記の BOM 付き UTF-16 の文字列の変換で不正な結果になることがある(PHP 5.2.12 以前, PHP 5.3.0) で紹介したものと同様の問題です。
回避方法
直接の回避方法はありません。UTF-16 の文字列を扱う場合は、外部コマンド(iconv, nkf など)で変換することは可能です。
PHP による日本語の文字コード判定スクリプト (2004.11.21 の過去ログ)に書いたものです。
文字列の文字コードを判定する場合、PHP には mb_detect_encoding()
が用意されていますが、文字列が短い場合、判定に失敗することが多くなりますので、もう少し判定率の良いスクリプトを作成してみました。
mbstring(マルチバイト文字列関数) と mbregex(マルチバイト正規表現関数) が必要です。文字コード範囲が重なるような場合は、SJIS-win を優先させています。
各文字コードの範囲については、Perl メモ:文字の正規表現を参考にしました。
(2008.02.03 修正)
以前のスクリプトでは、日本語を含まない長い文字列を入力すると、セグメンテーションフォールトが発生することを確認しました。そういった問題が起こらないようにスクリプトの正規表現を修正しました。
/** * 日本語文字列の文字コード判定(ASCII/JIS/eucJP-win/SJIS-win/UTF-8 のみ) */ function detect_encoding_ja( $str ) { $enc = @mb_detect_encoding( $str, 'ASCII,JIS,eucJP-win,SJIS-win,UTF-8' ); switch ( $enc ) { case FALSE : case 'ASCII' : case 'JIS' : case 'UTF-8' : break; case 'eucJP-win' : // ここで eucJP-win を検出した場合、eucJP-win として判定 if ( @mb_detect_encoding( $str, 'SJIS-win,UTF-8,eucJP-win' ) === 'eucJP-win' ) { break; } $_hint = "\xbf\xfd" . $str; // "\xbf\xfd" : EUC-JP "雀" // EUC-JP -> UTF-8 変換時にマッピングが変更される文字を削除( ≒ ≡ ∫ など) mb_regex_encoding( 'EUC-JP' ); $_hint = mb_ereg_replace( "\xad(?:\xe2|\xf5|\xf6|\xf7|\xfa|\xfb|\xfc|\xf0|\xf1|\xf2)", '', $_hint ); $_tmp = mb_convert_encoding( $_hint, 'UTF-8', 'eucJP-win' ); $_tmp2 = mb_convert_encoding( $_tmp, 'eucJP-win', 'UTF-8' ); if ( $_tmp2 === $_hint ) { // 例外処理( EUC-JP 以外と認識する範囲 ) if ( // SJIS と重なる範囲(2バイト|3バイト|iモード絵文字|1バイト文字) ! preg_match( '/^(?:' . '(?:[\x8E\xE0-\xE9][\x80-\xFC])+|' . '(?:\xEA[\x80-\xA4])+|' . '(?:\x8F[\xB0-\xEF][\xE0-\xEF][\x40-\x7F])+|' . '(?:\xF8[\x9F-\xFC])+|' . '(?:\xF9[\x40-\x49\x50-\x52\x55-\x57\x5B-\x5E\x72-\x7E\x80-\xB0\xB1-\xFC])+|' . '[\x00-\x7E]+' . ')+$/', $str ) && // UTF-8 と重なる範囲(全角英数字|漢字|1バイト文字) ! preg_match( '/^(?:' . '(?:\xEF\xBC[\xA1-\xBA])+|' . '(?:[\xE4-\xE9][\x8E-\x8F\xA1-\xBF][\x8F\xA0-\xEF])+|' . '[\x00-\x7E]+' . ')+$/', $str ) ) { // 条件式の範囲に入らなかった場合は、eucJP-win として検出 break; } // 例外処理2(一部の頻度の多そうな熟語を eucJP-win として判定) // (珈琲|琥珀|瑪瑙|癇癪|碼碯|耄碌|膀胱|蒟蒻|薔薇|蜻蛉) if ( mb_ereg( '^(?:' . '\xE0\xDD\xE0\xEA|\xE0\xE8\xE0\xE1|\xE0\xF5\xE0\xEF|\xE1\xF2\xE1\xFB|' . '\xE2\xFB\xE2\xF5|\xE6\xCE\xE2\xF1|\xE7\xAF\xE6\xF9|\xE8\xE7\xE8\xEA|' . '\xE9\xAC\xE9\xAF|\xE9\xF1\xE9\xD9|[\x00-\x7E]+' . ')+$', $str ) ) { break; } } default : // ここで SJIS-win と判断された場合は、文字コードは SJIS-win として判定 $enc = @mb_detect_encoding( $str, 'UTF-8,SJIS-win' ); if ( $enc === 'SJIS-win' ) { break; } // デフォルトとして SJIS-win を設定 $enc = 'SJIS-win'; $_hint = "\xe9\x9b\x80" . $str; // "\xe9\x9b\x80" : UTF-8 "雀" // 変換時にマッピングが変更される文字を調整 mb_regex_encoding( 'UTF-8' ); $_hint = mb_ereg_replace( "\xe3\x80\x9c", "\xef\xbd\x9e", $_hint ); $_hint = mb_ereg_replace( "\xe2\x88\x92", "\xe3\x83\xbc", $_hint ); $_hint = mb_ereg_replace( "\xe2\x80\x96", "\xe2\x88\xa5", $_hint ); $_tmp = mb_convert_encoding( $_hint, 'SJIS-win', 'UTF-8' ); $_tmp2 = mb_convert_encoding( $_tmp, 'UTF-8', 'SJIS-win' ); if ( $_tmp2 === $_hint ) { $enc = 'UTF-8'; } // UTF-8 と SJIS 2文字が重なる範囲への対処(SJIS を優先) if ( preg_match( '/^(?:[\xE4-\xE9][\x80-\xBF][\x80-\x9F][\x00-\x7F])+/', $str ) ) { $enc = 'SJIS-win'; } } return $enc; }
文字コード判定の考え方としては、以下のようになっています。
EUC-JP -> UTF-8 への変換してから、UTF-8 -> EUC-JP を行った場合、逆変換に成功することは少ない
まず、文字コードがあいまいな文字列を EUC-JP として検出させ、1. より、EUC-JP で無いことを確認した場合は SJIS か UTF-8 を判定
EUC-JP での逆変換に成功した場合、SJIS と文字コードが重なる部分は SJIS を優先
EUC-JP でない場合、UTF-8 と仮定して逆変換に成功した場合のみ UTF-8 として検出
例外は個別対応
この文字コード判定スクリプトには以下の問題があります。
少し重くなる
PHP の mbstring に依存している(汎用性がない)
EUC-JP のいわゆる半角カタカナ(\x8e[\xa1-\xfc])や、EUC-JP の一部の文字([\xe0-\xea][\xa1-\xfc])のみで構成される文字列は SJIS-win として判定される
UTF-8 の一部の記号(数学記号とギリシア文字の一部)が含まれる文字列が eucJP-win として判定される
使用方法としては、以下のように文字コードが分からない日本語文字列を UTF-8 に変換するような関数を作成する場合に便利です。
function convert_utf8( $str ) { $encoding = detect_encoding_ja( $str ); return mb_convert_encoding( $str, 'UTF-8', $encoding ); }
最近は、不正な文字(マルチバイトの上位バイトのみ入力するなど)を攻撃に使用されるなど、Web アプリケーションでもいろいろと考慮することが多くなっています。
個人的に、文字コードに可憐する問題への対処を含め、できる限り環境を統一するようなフィルタを作って、最初にインクルードして使用しています。何かの参考になるかもしれませんので、公開しておきます。
このスクリプトを使用することで、以下の環境にあわせる目的としています。
register_globals = Off
magic_quotes_gpc = Off
改行コードを "\n" に統一
文字列に NULL バイトが含まれていない
入力された変数( $_GET
, $_POST
, $_SERVER
など)の文字コードが内部文字コードと同じ
function __input_filter() { // php.ini の register_globals が有効になっていた場合は、登録された変数を削除 if ( ini_get( 'register_globals' ) ) { if ( isset( $_REQUEST['GLOBALS'] ) ) { trigger_error( 'GLOBALS overwrite attempt detected', E_USER_ERROR ); } $no_unset = array( 'GLOBALS' => '', '_GET' => '', '_POST' => '', '_COOKIE' => '', '_REQUEST' => '', '_SERVER' => '', '_ENV' => '', '_FILES' => '' ); $input = array_merge( $_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset( $_SESSION ) ? (array)$_SESSION : array() ); foreach ( array_keys( $input ) as $k ) { if ( ! isset( $no_unset[$k] ) && isset( $GLOBALS[$k] ) ) { unset( $GLOBALS[$k] ); } } } // $HTTP_*_VARS を削除 foreach ( array_keys( $GLOBALS ) as $k ) { if ( strncmp( $k, 'HTTP_', 5 ) === 0 ) { unset( $GLOBALS[$k] ); } } // php.ini で magic_quotes_gpc が有効になっていた場合、エスケープを削除 if ( get_magic_quotes_gpc() ) { function __stripslashes_r( $a ) { if ( is_array( $a ) ) { return array_map( __FUNCTION__, $a ); } return stripslashes( $a ); } if ( $_SERVER ) { $_SERVER = __stripslashes_r( $_SERVER ); } if ( $_GET ) { $_GET = __stripslashes_r( $_GET ); } if ( $_POST ) { $_POST = __stripslashes_r( $_POST ); } if ( $_COOKIE ) { $_COOKIE = __stripslashes_r( $_COOKIE ); } if ( $_REQUEST ) { $_REQUEST = __stripslashes_r( $_REQUEST ); } } // 改行コードを統一し、NULL バイトを削除 function __unify_filter( $a ) { if ( is_array( $a ) ) { return array_map( __FUNCTION__, $a ); } return str_replace( "\0", "", strtr( str_replace( "\x0D\x0A", "\n", $a ), "\x0D\x0A", "\n\n" ) ); } if ( $_GET ) { $_GET = __unify_filter( $_GET ); } if ( $_POST ) { $_POST = __unify_filter( $_POST ); } if ( $_COOKIE ) { $_COOKIE = __unify_filter( $_COOKIE ); } if ( $_REQUEST ) { $_REQUEST = __unify_filter( $_REQUEST ); } if ( $_SERVER ) { $_SERVER = __unify_filter( $_SERVER ); } if ( $_ENV ) { $_ENV = __unify_filter( $_ENV ); } if ( $_FILES ) { $_FILES = __unify_filter( $_FILES ); } // 入力された文字コードが内部文字コードと違った場合、エラーとして検出 if ( extension_loaded( 'mbstring' ) ) { function __check_encoding( $val, $key, $encoding ) { if ( is_array( $val ) ) { array_walk( $val, __FUNCTION__, $encoding ); } else if ( (string)$val !== mb_convert_encoding( $val, $encoding, $encoding ) ) { trigger_error( 'Invalid character encoding', E_USER_ERROR ); exit; } if ( (string)$key !== mb_convert_encoding( $key, $encoding, $encoding ) ) { trigger_error( 'Invalid character encoding', E_USER_ERROR ); exit; } } $enc = mb_internal_encoding(); if ( $_GET ) { array_walk( $_GET, '__check_encoding', $enc ); } if ( $_POST ) { array_walk( $_POST, '__check_encoding', $enc ); } if ( $_COOKIE ) { array_walk( $_COOKIE , '__check_encoding', $enc ); } if ( $_REQUEST ) { array_walk( $_REQUEST, '__check_encoding', $enc ); } if ( $_SERVER ) { array_walk( $_SERVER, '__check_encoding', $enc ); } } } // フィルタを実行 __input_filter();
このスクリプトの処理としては、以下の通りです。
register_globals
が有効になっていた場合、登録されたグローバル領域の変数を削除(スーパーグローバル変数を除く)
$GLOBALS
を上書きするような要求があった場合、エラーとしてスクリプトを終了
magic_quotes_gpc
が有効になっていた場合、エスケープを解除
入力文字列の 改行コードを \n
に統一
入力文字列の NULL バイトを削除
入力文字列のキーと値が内部文字コード(internal_encoding
)と違う場合は、エラーとしてスクリプトを終了
使い方は、上のスクリプトを適当なファイル(inputfilter.inc.php など)に保存して、最初に実行するスクリプト(index.php など)の最初の方で以下のように実行します。有効な内部文字コードを設定していないとエラーになりますので、注意してください。
<?php mb_language( 'Japanese' ); mb_internal_encoding( 'UTF-8' ); // スクリプトの内部エンコード include( 'inputfilter.inc.php' );
上記のスクリプトは、以下に掲載されていたコードなどを参考にさせていただきました。
私はどのように register_globals を扱うべきですか? (PHP マニュアル)
これからのプログラムの作り方 - 文字エンコーディング検証は必須 (yohgaki's blog)
その他、誤字・脱字の修正
PHP 5.3.2 〜 PHP 5.4.0 で追加された機能の説明や補足事項などを追加。
使用可能な文字コード名と MIME 名、エイリアスの一覧に PHP 5.3.1 以降で追加された文字コード名、エイリアス名などを追加。
mbstring 関数一覧を再作成。現在の PHP マニュアルに合わせました。
マルチバイト正規表現関数一覧を再作成。現在の PHP マニュアルに合わせました。
バグ・セキュリティ問題に以下を追加
その他、誤字・脱字の修正
バグ・セキュリティ問題に特定の環境で mbstring.internel_encoding の値が反映されない場合がある(PHP 5.2.9 以前)を追加。
PHP 5.3.0 で追加された機能の説明などを追加。
使用可能な文字コード名と MIME 名、エイリアスの一覧を再作成。PHP 5.3.0 で追加された文字コード名を追加。
mbstring 関数一覧に、PHP 5.3.0 で追加された mb_encoding_aliases()
を追加。
mb_language() 関数の引数に設定可能な言語一覧に、PHP 5.3.0 で追加されたウクライナ語を追加。
その他、全体的な文章の修正、リンク修正など。
バグ・セキュリティ問題に以下の項目を追加。
PHP 4.x で残っている mbstring 関連のバグに以下の項目を追加。
その他、全体的な文章の修正、リンク修正など。
PHP 4.x で残っている mbstring 関連のバグのmb_ereg()、mb_ereg_replace() で UTF-8 の一部の文字がマッチしない に追記。枡形さん、補足情報どうもありがとうございました。
その他、誤字・脱字、リンク切れの修正。
php.ini の mbstring 設定オプションに mbstring.http_output_conv_mimetypes
の情報を追加。
zend_multibyte についての項目を追加。その他、zend_multibyte 関連の情報を追加。
レガシーエンコーディングの追加でバージョンを間違えていたので修正(5.2.0 から 5.2.1)。
その他、誤字・脱字の修正。
"auto" の内部で扱われる文字コードリストがリクエストごとに初期化されない(PHP 5.2.5 以前)の再現コードのインデントが崩れていたので修正
全体的に文書を見直して追記、表現の修正、誤字・脱字の修正などを行いました。
バグ・セキュリティ問題に追加しました。
mb_send_mail() 関数の第5引数の項目を追加しました。
入力時フィルタの項目を追加しました。
PHP による日本語の文字コード判定スクリプトで、日本語を含まない長い文字列を入力すると、セグメンテーションフォールトが稀に発生することを確認しました。問題が起こらないように正規表現を修正しました。
その他、細かい追記、誤字・脱字の修正などを行いました。
初版作成。公開。