Smarty3でassignした日本語が文字化けする原因と対策

PHP

個人開発用にSmartyというレガシーなテンプレートエンジンを使用していますが、
ローカルでは文字化けしないのに、AWSのEC2にデプロイすると、tpl拡張子(Smartyのテンプレート側ファイル)にassignした日本語の文字だけ文字化けする現象が起きていました。

ほんとは、「Wifi情報」と出力される箇所が文字化けしている


最初はphp.iniのエンコーディングが上手く設定されていないのかな?と思い、default_charsetや internal_encoding辺りのパラメータをUTF-8にするも反応なし。

というわけで、問題を切り分けて対策を考えてみることにしました。

環境情報

ローカル開発環境:OS Mac OS
バージョン:XAMPP 7.0.1-0(文字化けが起きてない環境)

本番環境: OS Amazon Linux AMI release 2018.03(文字化けが起きた環境)
バージョン:PHP 7.0.33 smarty 3.1.34

保存時の文字コードは問題ない?

調べていると、phpでファイルを保存する時に文字コードをSHIFT-JISにしていると、Smarty側で強制的にUTF-8に変換するから文字化けするという情報が出てきました。

いやでも、保存するときもUTF-8にしてるからこれは関係ない。

古いコンパイルファイルを読み込んでいる?

Smartyはテンプレートエンジンなので、tplで書いたファイルを自身が指定したディレクトリ先にコンパイルして、そのコンパイル済みファイルをSmartyが実行するという仕組みになっています。

そのコンパイル済ファイルが文字化けしている場合は、文字化けしたファイルを読み込んでしまうので、削除して確認する必要があります。


14e848f10f875f270a474f735bd7ccbe13bed4cb_0.file.○.tpl.php        3dda0a2799a5dc823884b53546a9d1c8e6e7867c_0.file.○.tpl.php          f6b7d163c0f74b42abfa9d36a63690198dd33987_0.file.○.tpl.php
1851b6564bd901cd9cacc7c4b25f9c61eaee4929_0.file.○.tpl.php      79d5f643bfef9b19d1d4310a3107d13b81359cdc_0.file.○.tpl.php    
30b6b247b9d27ddf464f817eb7f2cc038c589609_0.file.○.tpl.php  ae05516b1885914dd4923787600807cf745e69e0_0.file.○.tpl.php  

$ rm -rf view/templates_c/*    #上記コンパイル済ファイルを全て削除


しかし、コンパイル後のファイルを削除して確認しても全く変わらず。。

SmartyクラスでUTF-8を別の文字コードにエンコーディングしている?

再度調べ直すと、SmartyクラスでUTF-8をエンコーディングしている可能性があるんではないか、と思い、Smarty.class.phpの中身でUTF-8が書かれてる箇所を調べてみました。

すると、64行目にこんな記述が

# vendor/smarty/smarty/libs/Smarty.class.php

if (!defined('SMARTY_RESOURCE_CHAR_SET')) {
    // UTF-8 can only be done properly when mbstring is available!
    /**
     * @deprecated in favor of Smarty::$_CHARSET
     */
    define('SMARTY_RESOURCE_CHAR_SET', SMARTY_MBSTRING ? 'UTF-8' : 'ISO-8859-1');
}

SMARTY_RESOURCE_CHAR_SETに、SMARTY_MBSTRINGが設定されていない場合にISO-8859-1を設定してますね・・・

試しにここをUTF-8に修正すると、以下のように

正しく表示された

直っている。


なるほど。つまり、MB_STRINGが設定されていない、というかmbstring自体入っていないのでは?

以下記事に沿ってmbstringモジュールをインストール


Amazon Linuxでphpでmbstringを使う

$ yum list | grep "\-mbstring"    # インストール可能なモジュールを探索

php70-mbstring.x86_64                7.0.33-1.32.amzn1             @amzn-updates
php-mbstring.x86_64                  5.3.29-1.8.amzn1              amzn-main    
php54-mbstring.x86_64                5.4.45-1.75.amzn1             amzn-main    
php55-mbstring.x86_64                5.5.38-2.119.amzn1            amzn-main    
php56-mbstring.x86_64                5.6.40-1.143.amzn1            amzn-updates 
php71-mbstring.x86_64                7.1.33-1.43.amzn1             amzn-updates 
php72-mbstring.x86_64                7.2.24-1.18.amzn1             amzn-updates 
php73-mbstring.x86_64                7.3.11-1.21.amzn1             amzn-updates

$ yum install php70-mbstring.x86_64    # mbstringをインストール



以下コマンドでインストールされているか確認

$ php -m | grep mb
mbstring


よし。あとは、php.iniでmbstringの該当パラメータの値を設定すればOK

$ /etc/php.ini
1498 mbstring.encoding_translation = On    ←    ;を削除
1499 
1500 ; automatic encoding detection order.
1501 ; "auto" detect order is changed according to mbstring.language
1502 ; http://php.net/mbstring.detect-order
1503  mbstring.detect_order = UTF-8,SJIS,EUC-JP,JIS,ASCII    ←    ;を削除


Apacheを再起動して、コンパイルファイルも削除して、再度確認。


よーし、完全に直った。

MBSTRINGとは?何故Smartyで必要なの?

簡単に言うとPHPで日本語を扱うための拡張モジュールです。

XAMPPやMAMPなどのパッケージ管理ソフトを使用している場合、最初からインストールされている可能性がありますが、PHPを単体でインストールした場合は、別途mbstringモジュールをインストールする必要があります。
(mbstringなど、後から追加するモジュールのことを「拡張モジュール」と言います。)

そもそも何故、Smartyで日本語を扱う為にmbstringが必要なのか。

まず、半角文字は(例えばabcなど)は、シングルバイト文字と呼ばれます。(1文字1バイト(8bit)で符号化されます)
大文字はマルチバイト文字と呼ばれます(2バイト以上(16bit以上)で符号化されます。)

phpでは、マルチバイト文字を扱う場合に、文字コードをUTF-8に初期設定するのが望ましいとされています。(それ以外の文字コードだと正しく処理出来ない可能性があるからです。)

Smartyではmbstringモジュールがない場合、文字コードをISO-8859-1に自動変換するような対策が施されています。(ISO-8859-1はphp5.6バージョン以前の、phpデフォルトの文字コードです)
しかし、ISO-8859-1という文字コードは日本語には対応していません。

「ISO-8859-1」のキャラクタセットであるとして処理されます。「ISO-8859-1」はLatin-1とも呼ばれ、ラテンアルファベットのキャラクタセットであり、日本語は含まれません。

出典:もしも文字化けで困ったら


つまり、文字コードがISO-8859-1に自動変換された結果、Smartyがコンパイルした時に、ISO-8859-1に対応していない日本語だけが文字化けした、ということです。

なので、マルチバイト文字をエンコーディングしてくれる拡張モジュール「mbstring」をインストールしてすれば、Smarty側でUTF-8に自動変換してくれて、日本語も英語も文字化けせずにコンパイルしてくれる、ってことです。

他参考

原因はXAMPPとAmazon Linuxの環境の違いだった

そもそも自分がローカル開発環境で使っているXAMPPでは、最初からmbstringモジュールが入っていたので、Smartyの文字化けが起きないのでした。


環境依存の問題を探るのは大変ですが、地道に一個一個原因を潰していくしかないですね。

勉強会で聞いた!評判のプログラミングスクール3選
ポテパンキャンプ

【特長】
3ヶ月のスクール代を全額キャッシュバック!
実質無料で学べるプログラミングスクールです。

【こんな人にオススメ】
・Railsチュートリアルをやり切った
・お金はないがやる気はある
・Ruby/Railsを中心に学びたい
・初心者の域を脱したい

TECH::EXPERT

【特長】
600時間の学習!
スパルタカリキュラムで高収入な転職を実現させるプログラミングスクールです。

【こんな人にオススメ】
・最短最速でエンジニア転職したい
・今の環境をいますぐ変えたい
・お金があってやる気もある
・もう挫折したくない

DMM WEBCAMP

【特長】
転職成功率98%
基礎からプロダクト制作まで徹底したカリキュラムで学べるプログラミングスクールです。

【こんな人にオススメ】
・Web系企業に就職・転職したい
・インフラ系からスキルチェンジしたい
・短期間で集中して学びたい
・チーム開発を学びたい

PHP
この記事を書いた人

元専業アフィリエイター・ブロガー。
現在はWebエンジニアの27歳。
運営メディアは月間150万pvを超えたことも。

YUUKIをフォローする
シェアする
YUUKIをフォローする
YUUKIのWebエンジニア道