LoginSignup
2
1

More than 5 years have passed since last update.

mbstring.internal_encodingがno valueの時のmb_substrの挙動について

Last updated at Posted at 2014-12-11

今日あったトラブルなんですがこんな感じのコードです。

tests/string.php
<?php
require "vendor/autoload.php";

class StringTest extends \PHPUnit_Framework_TestCase
{
  /**
   * @test
   */
  public function 文字数が50文字以上の場合は50文字で切ることができる()
  {
      $str = "犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬";
      $actual = mb_substr($str,0,50);
      $this->assertEquals(50, mb_strlen($actual),'文字数が正しくない');
  }
}

まあこれはテスト通るんですが、実は出力してみるとおかしくなるんですよね。

test.php
<?php

$str = "犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬";
echo mb_substr($str,0,50)."\n";

これの出力結果が以下の通りです

$php test.php
犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬�

文字がぶっ壊れてしまいましたね。
で、phpマニュアルにると、mb_substrの4番目の引数にエンコーディングの設定があります。
デフォルト値がmb_internal_encoding()となっていたので、mb_internal_encodingの値を確認してみましょう。

$ php -i | grep mbstring.internal_encoding
mbstring.internal_encoding => no value => no value

どうやらno valueになっていたのが原因みたいです。
UTF-8として認識されていなかったのぽいので、文字がぶっ壊れたと思います。

試しにecho mb_internal_encoding();ってやってみたらISO-8859-1って出てきました。
これmacな環境だからですかね?

コメントで教えていただきましたが、どうやらOS依存ではなくて、PHP5.5だとデフォルトがISO-8859-1になるようです。

対応策

まあどの環境でもUTF-8でコードを書いてたならUTF-8で動くようにしなきゃいけませんよね。
考えられる対応策は3つありますね。

  1. 毎回引数に文字コードを明示的にしていする
  2. スクリプトの中でmb_internal_encoding()を指定する
  3. php.iniで設定

大体こんな感じだと思います。
まあこの辺はプロジェクトとかスクリプトが使われる状況によって変わって来ると思います。

まあiniで設定するのが一番良さそうですが、どうなんすかね。
とりあえず今回は引数指定で、修正してみました。

test.php
<?php

$str = "犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬";
echo mb_substr($str,0,50,'utf-8')."\n";

しかしmb_strlenはなぜ50文字って取得できたのか。。。
まあmb_strlenもエンコード指定してなかったからですね。これがmb_strlenだけにutf-8指定してたらちゃんとエラーになりますね。

test/string2.php

<?php
require "vendor/autoload.php";

class StringTest extends \PHPUnit_Framework_TestCase
{
  /**
   * @test
   */
  public function 文字数が50文字以上の場合は50文字で切ることができる()
  {
      $str = "犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬犬";
      $actual = mb_substr($str,0,50);
      $this->assertEquals(50, mb_strlen($actual,'UTF-8'),'文字数が正しくない');
  }
}

まとめ

mb_~系のメソッドは大体文字コードの指定があるのでちゃんとinternal_encodingエンコーディングを設定しましょう。もしくは引数を必ず書きましょう。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1