PHP の mbstring に関するメモ

HOME | メモ一覧 | LastUpdate: 2013-02-03

このページについての説明・注意など

PHP の mbstring 拡張モジュールについて調べてみました。mbstring 拡張モジュールは、PHP マニュアル : マルチバイト文字列関数 (mbstring) にも書かれていない機能や制約が多くあります。そのあたりをソースコードを確認しながらまとめてみました。

更新内容については、更新履歴を参照してください。

ソースコードの確認は主に PHP 5.2.5 〜 PHP 5.3.0 で行いました。PHP 4.x に対する記述もありますが、細かいバージョン等については十分に確認できていないものもあります。このページを読んで、間違い、誤字、脱字などがを見つけましたら、はてなの日記、または、メールなどで教えてください。


目次

  1. このページについての説明・注意など
  2. mbstring を使用する方法
    1. 静的ライブラリとして組み込む方法
    2. 共有ライブラリとして使用する方法
    3. Windows 環境の場合
  3. php.ini の設定(mbsting 関係)
    1. php.ini の mbstring 設定オプション
    2. mbstring.internal_encoding を 指定しなかった場合のデフォルト設定
    3. zend_multibyte について
  4. mbstring の内部変数
    1. mbstring の内部変数一覧
  5. マルチバイト文字列(mbstring)関数
    1. mbstring 関数について
    2. mbstring 関数一覧
    3. mb_language() 関数
      1. mb_language() 関数の第1引数(language)
      2. "auto" の展開と文字コードリスト
      3. mb_language() 関数の引数に設定可能な言語一覧
    4. mb_check_encoding() 関数
    5. mb_detect_encoding() 関数の第3引数(strict) と php.ini の mbstring.strict_detection
    6. mb_encode_mimeheader() 関数の第5引数(indent)
    7. mb_send_mail() 関数
      1. mb_send_mail() 関数によくある間違った使い方
      2. mb_send_mail() 関数の第5引数(additional_parameter)
      3. mb_send_mail() 関数の使用例
    8. 文字コード変換関数 mb_convert_encoding(), mb_convert_variables()
      1. mbstring が提供する文字コード変換関数
      2. 文字コード変換で不正文字列を変換した場合の代替文字
  6. 文字コード
    1. 使用可能な文字コード名と MIME 名、エイリアスの一覧
    2. レガシーエンコーディングの追加
    3. 絵文字変換用のエンコーディング追加
  7. マルチバイト正規表現
    1. マルチバイト正規表現関数について
    2. マルチバイト正規表現関数一覧
    3. マルチバイト正規表現で指定可能な文字コード
    4. マルチバイト正規表現のオプション
      1. PHP 4.x のオプション
      2. PHP 5.0.0 以降のオプション
    5. mb_ereg_replace_callback() 関数
  8. バグ・セキュリティ問題
    1. mb_detect_encoding() が失敗する(PHP 4.3.11, PHP 5.0.0 - PHP 5.0.4)
    2. mb_encode_mimeheader() 関数が正常に動作しない(PHP 4.4.0 - PHP 4.4.1, PHP 5.0.4 - PHP 5.0.5)
    3. mb_send_mail() 関数の第 5 引数が無視される(PHP 4.4.1)
    4. mb_send_mail() 関数の作成する Subject: 行の文字数が RFC 違反(PHP 4.4.1 以前, PHP 5.0.5 以前)
    5. mb_send_mail() 関数の To: (第1引数) にメールヘッダの埋め込みが可能(PHP 4.4.1 以前, PHP 5.0.5 以前)
    6. MOPB-26-2007:PHP mb_parse_str() register_globals Activation Vulnerability(PHP 4.4.6 以前、PHP 5.2.1 以前)
    7. "auto" の内部で扱われる文字コードがリクエストごとに初期化されない(PHP 5.2.5 以前)
    8. PHP 5.2.6 で修正された mbstring 関係のバグ一覧
    9. mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す(PHP 5.2.6 以前)
    10. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する(PHP 5.2.8 以前)
    11. 内部変数 strict_detection が有効な状態で、不正な文字列の文字コード検出を行うと無限ループが発生する(PHP 5.1.2 - PHP 5.2.8)
    12. PHP 5.2.9 で修正された mbstring 関係のバグ一覧
    13. 特定の環境で mbstring.internel_encoding の値が反映されない場合がある(PHP 5.2.9 以前)
    14. mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す(PHP 5.3.0 より前)
    15. parse_str() を使用すると、mbstring の内部エンコーディングがリセットされる(PHP 5.2.10, PHP 5.3.0)
    16. BOM 付き UTF-16 の文字列の変換で不正な結果になることがある(PHP 5.2.11 以前, PHP 5.3.1 以前)
    17. mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある(PHP 5.2.11 以前, PHP 5.3.1 以前)
  9. PHP 4.x で残っている mbstring 関連のバグ
    1. mb_ereg()、mb_ereg_replace() で UTF-8 の一部の文字がマッチしない
    2. Apache モジュール版を使用している場合、.htaccess や ini_set() で設定した mbstring の内部変数が別のリクエストに影響する
    3. Apache モジュール版を使用している場合、"auto" の内部で扱われる文字コードリストが別のリクエストに影響する
    4. mb_ereg() の第1引数に不正な値(NULL, FALSE, 空文字列)を入力しても成功する
    5. mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す
    6. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する
    7. ユーロ記号(€: U+20AC)をユニコードから CP936(中国語簡体字) に変換すると不正な文字列になる
    8. mb_detect_encoding() で不正な文字コードを指定して検出を行うと、その後で Segmentation Fault を起こすことがある
    9. mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す
    10. mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある
    11. BOM 付き UTF-16 の文字列の変換で不正な結果になることがある
  10. 付録
    1. PHP による日本語の文字コード判定スクリプト
    2. 入力時フィルタ
  11. 更新履歴

mbstring を使用する方法

  1. 静的ライブラリとして組み込む方法
  2. 共有ライブラリとして使用する方法
  3. Windows 環境の場合

PHP の mbstring 拡張モジュールは、ソースコードから構築する場合、コンパイル時に静的ライブラリとして組み込む方法と、共有ライブラリとして動的に使用する方法の2通りがあります。

Windows 環境の場合は、mbstring.dll がダウンロードした PHP パッケージの中に含まれています。コンパイルする必要はありません。

a. 静的ライブラリとして組み込む方法

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

章の最初へ | 目次へ

b. 共有ライブラリとして使用する方法

まず、共有ライブラリを作成する必要があります。開発環境を準備し、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

章の最初へ | 目次へ

c. Windows 環境の場合

ダウンロードした PHP のパッケージの中に、php_mbstring.dll が含まれています。

php.ini で extension_dirextension を設定します。

extension_dir = "C:/php/ext"
extension=php_mbstring.dll

章の最初へ | 目次へ

▲ 目次へ


php.ini の設定(mbsting 関係)

  1. php.ini の mbstring 設定オプション
  2. mbstring.internal_encoding を 指定しなかった場合のデフォルト設定
  3. zend_multibyte について

a. php.ini の mbstring 設定オプション

php.ini には、以下のような mbstring の設定があります。詳しくは php.ini の mbstring 設定オプション (PHP マニュアル) を参照してください。

日本語を使用するのであれば、mbstring.language を "Japanese" に、mbstring.internal_encoding を PHP スクリプトを書いている文字コード(UTF-8、または、EUC-JP など)と合わせておくと良いと思います。

php.ini の mbstring 設定オプション
オプション名 デフォルト値 使用可能バージョン 備考
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)。

章の最初へ | 目次へ

b. mbstring.internal_encoding を 指定しなかった場合のデフォルト設定

php.ini で mbstring.internal_encoding を指定しなかった場合、mbstring.language の設定に対応した mbstring の内部文字コードが設定されます。

実際には以下のようになります。

内部文字コードのデフォルト値
mbstring.language 内部文字コード
neutral(未設定) ISO-8859-1
English ISO-8859-1
Japanese EUC-JP
Uni UTF-8

章の最初へ | 目次へ

c. zend_multibyte について

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 の内部変数

  1. mbstring の内部変数一覧

a. mbstring の内部変数一覧

mbstring では、以下の内部変数が定義されています。mb_get_info() を使用することで、確認できます。

mbstring の内部変数一覧
内部変数名 説明 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)

章の最初へ | 目次へ

▲ 目次へ


マルチバイト文字列(mbstring)関数

  1. mbstring 関数について
  2. mbstring 関数一覧
  3. mb_language() 関数
    1. mb_language() 関数の第1引数(language)
    2. "auto" の展開と文字コードリスト
    3. mb_language() 関数の引数に設定可能な言語一覧
  4. mb_check_encoding() 関数
  5. mb_detect_encoding() 関数の第3引数(strict) と php.ini の mbstring.strict_detection
  6. mb_encode_mimeheader() 関数の第5引数(indent)
  7. mb_send_mail() 関数
    1. mb_send_mail() 関数によくある間違った使い方
    2. mb_send_mail() 関数の第5引数(additional_parameter)
    3. mb_send_mail() 関数の使用例
  8. 文字コード変換関数 mb_convert_encoding(), mb_convert_variables()
    1. mbstring が提供する文字コード変換関数
    2. 文字コード変換で不正文字列を変換した場合の代替文字

a. mbstring 関数について

mbstring の関数を使用する場合、内部文字コード(internal_encoding)と変数の文字コードが合っていないと正しく動作しない関数があります。また、PHP スクリプトが書かれたファイルの文字コードについても、意識しておく必要があります。zend_multibyte を有効にしていない場合、PHP ファイルの文字コードが変数の文字コードになります。

多くの mbstring の関数では、引数に文字コードを指定できますが、省略した場合は internal_encoding が使用されます。また、文字コードは指定できませんが、internal_encoding 使用される関数もあります。

例えば mb_encode_mimeheader()mb_send_mail() では、文字コードを設定する引数はありませんが、internal_encoding を使用します。もし、引数として渡す文字列の文字コードが internal_encoding と違う場合、文字化けの原因になります。

章の最初へ | 目次へ

b. mbstring 関数一覧

mbstring 関数は以下の通りです(マルチバイト正規表現関数を除く)。各関数は PHP マニュアルの関数説明にリンクしています。

mbstring 関数一覧
返り値の型 関数名 引数 使用可能バージョン 備考
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 以降

章の最初へ | 目次へ

c. mb_language() 関数

i. mb_language() 関数の第1引数(language)

mb_language() は、第1引数(language)に "Japanese", "ja", "English", "en", "uni" などが設定可能です(PHP マニュアル : mb_language)。引数の大文字、小文字は区別されません("JA" と "ja" は同じです)。また、php.ini の mbstring.language で設定した場合も機能的にはほぼ同等です。

mb_language() は、主に以下の2つの機能があります。

ii. "auto" の展開と文字コードリスト

いくつかのマルチバイト文字列関数では、文字コードを指定する引数に "auto" を設定できます。"auto" を設定した場合、内部変数 language(mbstring.language または、mb_language() で設定した言語)によって、展開される文字コードリストが変化します。

"auto" は、以下の関数の引数に設定すると有効に機能します。

"auto" は以下の表のように展開されます。

内部変数 language の値に対応する文字コードリストの値
language 文字コードリスト
English ASCII, UTF-8
Uni ASCII, UTF-8
Japanese ASCII, JIS, UTF-8, EUC-JP, SJIS

日本語の文字コード検出をしたいのであれば、多くの場合、"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 として文字コードを検出させるようにすることで、誤検出する可能性が低くなります。

iii. mb_language() 関数の引数に設定可能な言語一覧

mbstring は他にも様々な言語で使用されることが想定されています。以下は、PHP 5.3.0 で mb_language() の引数に設定可能な言語一覧です。

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 ユニコード

章の最初へ | 目次へ

d. mb_check_encoding() 関数

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() は引数なしの場合のみ、特殊な挙動になります。

章の最初へ | 目次へ

e. mb_detect_encoding() 関数の第3引数(strict) と php.ini の mbstring.strict_detection

mb_detect_encoding() の第3引数に TRUE を指定すると厳密に文字コードを検出するようになります。第3引数を省略すると、php.ini の mbstring.strict_detection の設定に従います。

実際の処理としては、ある文字コードの検査中に、マルチバイトの有効な上位バイトが存在しているのに、有効な下位バイトが存在しない文字があった場合、検査中の文字コードは正しいと判定されません。これは、正しくない文字コードで検出しようとした場合に起きやすいものです。文字コードの検出率が高くなりますが、その分、処理は重くなります。

PHP のソースコードを見ると、ext/mbstring/libmbfl/mbfl/mbfilter.c で、mbfl_identify_filterflag フィールドと status フィールドを使用して処理しているようです。

章の最初へ | 目次へ

f. mb_encode_mimeheader() 関数の第5引数(indent)

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 ドライバーやメーラー
      などで問題が出る場合があるようです.

[PHP-dev 1186] Re: PHP4.4.1でmb_send_mailの第5引数が利かなくなってる

どのような場合に使用すべきかについては、以下のように説明されています。

使い方は単純で
$head = mb_encode_mimeheader($s, 'ISO-2022-JP', 'B', "\r\n", strlen("Subject: "));
のように5番目の引数にオフセットを指定できるようにしただけです。

[PHP-dev 1197] Re: PHP4.4.1でmb_send_mailの第5引数が利かなくなってる

PHP マニュアル: mb_encode_mimeheader には、indent は PHP 5 で追加されたことになっていますが、正確には、PHP 4.4.2 と PHP 5.1.1 から使用可能です。

章の最初へ | 目次へ

g. mb_send_mail() 関数

i. mb_send_mail() 関数によくある間違った使い方

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 でメールを送信する場合です。languageUni にして、先に ISO-2022-JP に変換するような処理を入れた場合、文字化けしたメールが送信されます。

Subject(第2引数:メールの題名)も同様です。変数の文字コードと internal_encoding が一致していれば、mb_send_mail() がメールを送信する際に必要な文字コードに変換します。

ii. mb_send_mail() 関数の第5引数(additional_parameter)

PHP マニュアルでは、mb_send_mail() 関数の第5引数は、以下のように説明されています。

additional_parameter(mb_send_mail() の第5引数)は、MTA へ渡す コマンドライン引数です。sendmail を利用する際に正しい Return-Path を設定するためなどに利用すると便利です。

PHP マニュアル: mb_send_mail

実際には、sendmail コマンドで Return-Path を設定するには、-f mailaddress@example.com のように、実際の送信元のメールアドレスを指定します。

php.ini の mail.force_extra_parameters を設定している場合、こちらが優先されます。その場合、mb_send_mail() の第5引数は無視されます。

iii. mb_send_mail() 関数の使用例

以下に、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 );
}

章の最初へ | 目次へ

h. 文字コード変換関数 mb_convert_encoding(), mb_convert_variables()

i. mbstring が提供する文字コード変換関数

mbstring が提供している文字コード変換関数は以下の2つです。

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つ以上) 指定された複数の文字コードから自動判別

自動判別の場合、前から順に指定された文字コードが変換対象の文字列に一致するかどうかを判定します。判定の結果が一致した場合、その文字コードを変換元の文字コードとして使用します。

ii. 文字コード変換で不正文字列を変換した場合の代替文字

PHP にも、文字コード変換で入力文字列の文字コードが不正、または、出力文字列の文字コードに文字が存在しない場合の代替文字を設定する機能があります。

php.ini では、mbstring.substitute_character、PHP スクリプトでは、mb_substitute_character() で設定可能です。

設定方法は以下の通りです。

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' );

章の最初へ | 目次へ

▲ 目次へ


文字コード

  1. 使用可能な文字コード名と MIME 名、エイリアスの一覧
  2. レガシーエンコーディングの追加
  3. 絵文字変換用のエンコーディング追加

a. 使用可能な文字コード名と MIME 名、エイリアスの一覧

以下が、PHP の mbstring で使用可能な文字コード名と MIME 名、エイリアスの一覧です。文字コードを指定する場合、大文字、小文字は区別されません。

以下の表での MIME 名は、mbstring で MIME 名として扱われているものです。正式な MIME 名とは違うものが入っています(7bit、8bit、HTML-ENTITIES、Quoted-Printable など)。

MIME 名については、IANA の文書を参照してください(http://www.iana.org/assignments/character-sets)。

mbstring で使用可能な文字コード一覧
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 から使用可能

章の最初へ | 目次へ

b. レガシーエンコーディングの追加

PHP 5.2.1 以降では、PHP マニュアル : マルチバイト文字列関数 (mbstring) : サポートされるエンコーディング に記載されている文字コードの他に、CP51932ISO-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コード) とは、ユーザー
  定義文字を除いて互換性あり。

[PHP-dev 1345] PHP への CP932 系エンコーディングの追加パッチ

これらの文字コードの詳細については、以下の情報を参照してください。

章の最初へ | 目次へ

c. 絵文字変換用のエンコーディング追加

PHP 5.4.0 から、mbstring で携帯各社(DoCoMo, KDDI, 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絵文字の仕様)。

以下のページに実装を担当された廣川さんの解説があります。

章の最初へ | 目次へ

▲ 目次へ


マルチバイト正規表現

  1. マルチバイト正規表現関数について
  2. マルチバイト正規表現関数一覧
  3. マルチバイト正規表現で指定可能な文字コード
  4. マルチバイト正規表現のオプション
    1. PHP 4.x のオプション
    2. PHP 5.0.0 以降のオプション
  5. mb_ereg_replace_callback() 関数

a. マルチバイト正規表現関数について

マルチバイト正規表現は 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() で指定可能な文字コード一覧を、マルチバイト正規表現で指定可能な文字コードにまとめました。

章の最初へ | 目次へ

b. マルチバイト正規表現関数一覧

マルチバイト正規表現関数は以下の通りです。各関数は 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 以降

章の最初へ | 目次へ

c. マルチバイト正規表現で指定可能な文字コード

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

章の最初へ | 目次へ

d. マルチバイト正規表現のオプション

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 で使用可能なマルチバイト正規表現関数で使用可能なオプションをまとめました。

i. PHP 4.x のオプション

以下の表のオプションが使用できます。何も設定していない場合は "p"(POSIX 互換モード) になります。

PHP 4.x のマルチバイト正規表現のオプション
オプション 説明
i 大文字・小文字を区別しない
x 空白を無視
m マルチラインモード(改行文字も "." に含まれる)
s シングルラインモード('^' を '\A', '$' を '\Z' とみなす)
p POSIX モード(ms の両方指定と同等)
l 最長一致(Longest Match)
e 文字列 $replacement を PHP の式として評価(mb_ereg_replace(), mb_eregi_replace() で使用可能)
ii. PHP 5.0.0 以降のオプション

以下の表のオプションが使用できます。何も設定していない場合は "pr"(POSIX 互換モード(シングルラインモードおよび、マルチラインモード)、Ruby 形式の正規表現パターン文法)になります。

PHP 5.0.0 以降のマルチバイト正規表現のオプション
オプション 説明
i 大文字・小文字を区別しない
x 空白を無視
m マルチラインモード(改行文字も "." に含まれる)
s シングルラインモード('^' を '\A', '$' と '\Z' とみなす)
p POSIX モード(ms を両方指定と同等 )
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 形式の正規表現パターン文法

章の最初へ | 目次へ

e. mb_ereg_replace_callback() 関数

mb_ereg_replace_callback() は PHP 5.4.1 で追加された関数です。正規表現にマッチした文字列に対してコールバック関数による置換ができます。

以前から、mb_ereg_replace() のオプションに "e" を渡すことで同様のことは可能でしたが、信頼できない入力に使用するとリモートからコード実行される可能性がありました。

信頼できない入力に対しては、絶対に e 修正子を使用してはいけません。 (preg_replace() と同様)自動的なエスケープは行いません。このことを忘れていると、自分の書いたアプリケーションにリモートコード実行の脆弱性を作りこんでしまうことになります。

PHP マニュアル: mb_ereg_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)からサポートされています。

章の最初へ | 目次へ

▲ 目次へ


バグ・セキュリティ問題

  1. mb_detect_encoding() が失敗する(PHP 4.3.11, PHP 5.0.0 - PHP 5.0.4)
  2. mb_encode_mimeheader() 関数が正常に動作しない(PHP 4.4.0 - PHP 4.4.1, PHP 5.0.4 - PHP 5.0.5)
  3. mb_send_mail() 関数の第 5 引数が無視される(PHP 4.4.1)
  4. mb_send_mail() 関数の作成する Subject: 行の文字数が RFC 違反(PHP 4.4.1 以前, PHP 5.0.5 以前)
  5. mb_send_mail() 関数の To: (第1引数) にメールヘッダの埋め込みが可能(PHP 4.4.1 以前, PHP 5.0.5 以前)
  6. MOPB-26-2007:PHP mb_parse_str() register_globals Activation Vulnerability(PHP 4.4.6 以前、PHP 5.2.1 以前)
  7. "auto" の内部で扱われる文字コードがリクエストごとに初期化されない(PHP 5.2.5 以前)
  8. PHP 5.2.6 で修正された mbstring 関係のバグ一覧
  9. mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す(PHP 5.2.6 以前)
  10. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する(PHP 5.2.8 以前)
  11. 内部変数 strict_detection が有効な状態で、不正な文字列の文字コード検出を行うと無限ループが発生する(PHP 5.1.2 - PHP 5.2.8)
  12. PHP 5.2.9 で修正された mbstring 関係のバグ一覧
  13. 特定の環境で mbstring.internel_encoding の値が反映されない場合がある(PHP 5.2.9 以前)
  14. mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す(PHP 5.3.0 より前)
  15. parse_str() を使用すると、mbstring の内部エンコーディングがリセットされる(PHP 5.2.10, PHP 5.3.0)
  16. BOM 付き UTF-16 の文字列の変換で不正な結果になることがある(PHP 5.2.11 以前, PHP 5.3.1 以前)
  17. mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある(PHP 5.2.11 以前, PHP 5.3.1 以前)

PHP の mbstring には多くのバグが報告され、修正されています。備忘録として以下にまとめました。PHP のバージョンは php.net でダウンロードできるソースのバージョンです。

a. mb_detect_encoding() が失敗する(PHP 4.3.11, PHP 5.0.0 - PHP 5.0.4)

章の最初へ | 目次へ

b. mb_encode_mimeheader() 関数が正常に動作しない(PHP 4.4.0 - PHP 4.4.1, PHP 5.0.4 - PHP 5.0.5)

章の最初へ | 目次へ

c. mb_send_mail() 関数の第 5 引数が無視される(PHP 4.4.1)

章の最初へ | 目次へ

d. mb_send_mail() 関数の作成する Subject: 行の文字数が RFC 違反(PHP 4.4.1 以前, PHP 5.0.5 以前)

章の最初へ | 目次へ

e. mb_send_mail() 関数の To: (第1引数) にメールヘッダの埋め込みが可能(PHP 4.4.1 以前, PHP 5.0.5 以前)

章の最初へ | 目次へ

f. MOPB-26-2007:PHP mb_parse_str() register_globals Activation Vulnerability(PHP 4.4.6 以前、PHP 5.2.1 以前)

章の最初へ | 目次へ

g. "auto" の内部で扱われる文字コードがリクエストごとに初期化されない(PHP 5.2.5 以前)

章の最初へ | 目次へ

h. PHP 5.2.6 で修正された mbstring 関係のバグ一覧

PHP 5.2.6 では、以下のバグ修正が行われています。

  1. "auto" の内部で扱われる文字コードリストがリクエストごとに初期化されない(PHP 5.2.5 以前)

  2. mb_strtoupper(), mb_strtolower() で不正な文字コードを第2引数に指定すると不要なエラーメッセージまで出力される

  3. 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)

  4. 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)

  5. 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)

  6. 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)

章の最初へ | 目次へ

i. mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す(PHP 5.2.6 以前)

章の最初へ | 目次へ

j. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する(PHP 5.2.8 以前)

mb_check_encoding() で第2引数に SJIS または SJIS-win を指定し、第1引数に SJIS としては不正な文字列 "0x81 0x3a" などを指定すると TRUE を返します。

どの範囲を TRUE と誤判定するのかは mb_check_encodingは何をチェックするのか(その1 SJIS編) を参照してください。

章の最初へ | 目次へ

k. 内部変数 strict_detection が有効な状態で、不正な文字列の文字コード検出を行うと無限ループが発生する(PHP 5.1.2 - PHP 5.2.8)

PHP 5.1.2 〜 PHP 5.2.8 では、以下の条件において、無限ループが発生することがあります。

PHP 5.0.5 以前のバージョンでは、内部変数 strict_detection が存在しないため、この問題は発生しません。

章の最初へ | 目次へ

l. PHP 5.2.9 で修正された mbstring 関係のバグ一覧

PHP 5.2.9 では、以下のバグ修正が行われています。

  1. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する(PHP 5.2.8 以前)

  2. 内部変数 strict_detection が有効な状態で、不正な文字列の文字コード検出を行うと無限ループになる(PHP 5.1.2 - PHP 5.2.8)

  3. ユーロ記号(€: 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)

  4. 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)

  5. 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)

  6. mb_strpos() の offset(第3引数) に負の値を渡すとバイト単位でカウントされる

章の最初へ | 目次へ

m. 特定の環境で mbstring.internel_encoding の値が反映されない場合がある(PHP 5.2.9 以前)

.htaccess で mbstring.internal_encoding を 指定し、さらに、encoding_translationOn の場合に発生する問題です。

上記の環境で、リクエストで受け取った文字列が、.htaccess で設定した mbstring.internel_encoding に変換されないことがあります。

章の最初へ | 目次へ

n. mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す(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 になるように修正されました。

章の最初へ | 目次へ

o. parse_str() を使用すると、mbstring の内部エンコーディングがリセットされる(PHP 5.2.10, PHP 5.3.0)

PHP 5.2.10 および、PHP 5.3.0 では、parse_str() を使用すると、内部エンコーディングが php.ini または、.htaccess で設定した値に戻るという問題があります。

章の最初へ | 目次へ

p. BOM 付き UTF-16 の文字列の変換で不正な結果になることがある(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) "テスト"

章の最初へ | 目次へ

q. mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある(PHP 5.2.11 以前, PHP 5.3.1 以前)

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 4.x で残っている mbstring 関連のバグ

  1. mb_ereg()、mb_ereg_replace() で UTF-8 の一部の文字がマッチしない
  2. Apache モジュール版を使用している場合、.htaccess や ini_set() で設定した mbstring の内部変数が別のリクエストに影響する
  3. Apache モジュール版を使用している場合、"auto" の内部で扱われる文字コードリストが別のリクエストに影響する
  4. mb_ereg() の第1引数に不正な値(NULL, FALSE, 空文字列)を入力しても成功する
  5. mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す
  6. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する
  7. ユーロ記号(€: U+20AC)をユニコードから CP936(中国語簡体字) に変換すると不正な文字列になる
  8. mb_detect_encoding() で不正な文字コードを指定して検出を行うと、その後で Segmentation Fault を起こすことがある
  9. mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す
  10. mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある
  11. BOM 付き UTF-16 の文字列の変換で不正な結果になることがある

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 では影響は確認できませんでした。

a. mb_ereg()、mb_ereg_replace() で UTF-8 の一部の文字がマッチしない

章の最初へ | 目次へ

b. Apache モジュール版を使用している場合、.htaccess や ini_set() で設定した mbstring の内部変数が別のリクエストに影響する

章の最初へ | 目次へ

c. Apache モジュール版を使用している場合、"auto" の内部で扱われる文字コードリストが別のリクエストに影響する

章の最初へ | 目次へ

d. mb_ereg() の第1引数に不正な値(NULL, FALSE, 空文字列)を入力しても成功する

章の最初へ | 目次へ

e. mb_check_encoding() の第1引数に NULL バイトから始まる文字列を入力すると TRUE を返す

章の最初へ | 目次へ

f. mb_check_encoding() で一部の不正な SJIS(Shift_JIS) の文字列を TRUE と判定する

章の最初へ | 目次へ

g. ユーロ記号(€: U+20AC)をユニコードから CP936(中国語簡体字) に変換すると不正な文字列になる

章の最初へ | 目次へ

h. mb_detect_encoding() で不正な文字コードを指定して検出を行うと、その後で Segmentation Fault を起こすことがある

章の最初へ | 目次へ

i. mb_check_encoding() が UTF-8 のユニコードのサロゲートペア領域を判定すると TRUE を返す

章の最初へ | 目次へ

j. mbstring.strict_mode が On の場合、mb_detect_encoding() が不正な結果を返すことがある

章の最初へ | 目次へ

k. BOM 付き UTF-16 の文字列の変換で不正な結果になることがある

章の最初へ | 目次へ

▲ 目次へ


付録

  1. PHP による日本語の文字コード判定スクリプト
  2. 入力時フィルタ

a. PHP による日本語の文字コード判定スクリプト

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;
}

文字コード判定の考え方としては、以下のようになっています。

  1. EUC-JP -> UTF-8 への変換してから、UTF-8 -> EUC-JP を行った場合、逆変換に成功することは少ない

  2. まず、文字コードがあいまいな文字列を EUC-JP として検出させ、1. より、EUC-JP で無いことを確認した場合は SJIS か UTF-8 を判定

  3. EUC-JP での逆変換に成功した場合、SJIS と文字コードが重なる部分は SJIS を優先

  4. EUC-JP でない場合、UTF-8 と仮定して逆変換に成功した場合のみ UTF-8 として検出

  5. 例外は個別対応

この文字コード判定スクリプトには以下の問題があります。

使用方法としては、以下のように文字コードが分からない日本語文字列を UTF-8 に変換するような関数を作成する場合に便利です。

function convert_utf8( $str )
{
    $encoding = detect_encoding_ja( $str );
    return mb_convert_encoding( $str, 'UTF-8', $encoding );
}

章の最初へ | 目次へ

b. 入力時フィルタ

最近は、不正な文字(マルチバイトの上位バイトのみ入力するなど)を攻撃に使用されるなど、Web アプリケーションでもいろいろと考慮することが多くなっています。

個人的に、文字コードに可憐する問題への対処を含め、できる限り環境を統一するようなフィルタを作って、最初にインクルードして使用しています。何かの参考になるかもしれませんので、公開しておきます。

このスクリプトを使用することで、以下の環境にあわせる目的としています。

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();

このスクリプトの処理としては、以下の通りです。

使い方は、上のスクリプトを適当なファイル(inputfilter.inc.php など)に保存して、最初に実行するスクリプト(index.php など)の最初の方で以下のように実行します。有効な内部文字コードを設定していないとエラーになりますので、注意してください。

<?php
mb_language( 'Japanese' );
mb_internal_encoding( 'UTF-8' ); // スクリプトの内部エンコード
include( 'inputfilter.inc.php' );

上記のスクリプトは、以下に掲載されていたコードなどを参考にさせていただきました。

章の最初へ | 目次へ

▲ 目次へ


更新履歴

2013-02-03

2012-05-06

2009-09-23

2009-09-19

2009-08-16

2009-03-01

2008-11-09

2008-08-15

2008-08-11

2008-05-18

2008-05-11

2008-01-27

2008-01-27

  • 初版作成。公開。

▲ 目次へ戻る

LastUpdate: 2013-02-03 | Counter: counter | メモ一覧 | HOME