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

最初は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の文字化けが起きないのでした。
環境依存の問題を探るのは大変ですが、地道に一個一個原因を潰していくしかないですね。