白米と納豆と梅干しがあれば生きていける、かーさん (id:Kaa_saan) です。
主にPHP案件担当してます。
今回は、痛いお話
CakePHP3.x系のお話です。
去年の話になりますが、CakePHPのアップデートを試みて、 盛大に失敗した ときの覚書です。
今後の戒めとして残しておきます。
アップデートの経緯・・・?
1年前のことなので、詳細な経緯は忘れてしまいました。 :)
・・・が、おそらく単純に、「そろそろ新しくしようぜ~♫」的な軽い気持ちだったような・・・。
すでに稼働しているサービスだったので、とりあえず、CakePHPとPHPUnitのアップデートが目標でした。
ちなみに、サービスは本番・検証・開発環境全て、AWS(Amazon Linux AMI release 2015.09)で稼働しています。
アップデート検証 CakePHP+PHPUnit
検証のシナリオ
アップデートを試す順番は、①~③で考えました。
②が目標ですが、あわよくば③まで行きたいなと。
① CakePHPのみアップデート
② CakePHP&PHPUnit アップデート
③ 全プラグインアップデート
事前準備
- アップデート 検証用ディレクトリを作成して、developソースを
git clone
①~③各手順共通事項
- 作業ログを取る(テキストエディタに、実行コマンドなど、行った作業すべてメモする)
- アップデート後は、ユニットテスト(以下、UT)を走らせる
それでは、順に試していきます。
① CakePHPのみアップデート(CakePHP:3.2.13 → 3.3.13)
$ composer update "cakephp/cakephp" Loading composer repositories with package information Updating dependencies (including require-dev) - Installing zendframework/zend-diactoros (1.3.10) Downloading: 100% - Removing cakephp/cakephp (3.2.13) - Installing cakephp/cakephp (3.3.13) Downloading: 100% Writing lock file Generating autoload files > Cake\Composer\Installer\PluginInstaller::postAutoloadDump
補足 :composer update [プラグイン名] で、アップデートするプラグインを限定できます。
- CakePHPが 3.2.13 から 3.3.13 になった
- 新しく、
zendframework/zend-diactoros (1.3.10)
が追加 - UTもOK
①がうまく行ったので、次は②を試します。
② CakePHP&PHPUnit アップデート(PHPUnit:5.5.0→ 5.6.4)
$ composer update cakephp/cakephp phpunit/phpunit Loading composer repositories with package information Updating dependencies (including require-dev) - Removing phpunit/phpunit (5.5.0) - Installing phpunit/phpunit (5.6.4) Loading from cache Writing lock file Generating autoload files > Cake\Composer\Installer\PluginInstaller::postAutoloadDump
補足:CakePHPは①でアップデート済みですので、ログにでてきません。
- PHPUnitが 5.5.0 から 5.6.4 になった
- UTも、エラー無しでOK
②も問題なくOK でした。ので、③へ進みます。(この辺でだいぶ調子に乗っている)
③ 全プラグインアップデート ・・・ダメでした
$ composer update
補足:プラグイン指定なしの
composer update
は、全プラグインを最新バージョンで更新します。
- UTで大量のエラー(´;ω;`)
- 全プラグインアップデートは断念する
エラーの原因 ・・・使ってるプラグインが無くなっていた
MissingComponentException: Component class AuthUserComponent could not be found.
が大量に出た(Controllerテストで主にでてる)- AuthUserComponent(dereuromark/cakephp-tools*1)は 後から入れたプラグイン
- cakephp-toolsの 検証時、既存ソースはv1.1.3、最新バージョンは2.x
- 2.x系になって、AuthUserComponent は cakephp-toolsから 抹殺された → TinyAuthプラグイン*2として 独り立ち!→現行ソースの書き換え必須!*3
検証の結果
- ③の全プラグインアップデートは、変更の入ったプラグインも存在していて、コードの書き換えが必須なので断念
- アップデートの対象は、当初のCakePHPとPHPUnitに絞る(他のプラグインは、現行バージョンのままで行く)
- ②の内容の、
composer.json
とcomposer.lock
をバージョン管理コミット
検証環境で、アプリ動作確認をする
検証の結果、CakePHPとPHPUnitは問題が出なかったので、次は検証環境でアップデートしてみて、実際にアプリの動作確認を行います。
ここで、一連の機能が問題なく使えれば、本番へ反映ということになります。
が、、、、、、事はすんなり行きませんでした。
検証環境でのアプリ動作確認中に、不具合発生
JSON出力のDateデータに不具合?
アプリ動作確認しているチームから、非同期処理部分で機能が動かなくなった、というような報告が上がってきました・・・。
調査してみたら、以下のような不具合が確認できました。
- AjaxでJSONデータを利用して処理している機能で、一部データの不整合が発生していた
- 具体的に言うと、JSONで取得している Date(日付)の戻り値に、3.2系と3.3系で差分が発生していた
- 3.2:2017-02-22T18:17:15+09:00
- 3.3:2017-02-22T18:17:15
+09:00
が 付いてこない
- DBから取得したデータをdumpしてみると、ちゃんと
+09:00
が付いている状態なのは確認できた - どうやら、JSONに加工された時になにかが起こっているのでは?と当たりをつける
#DBデータのダンプ [ 'update_date' => object(Cake\I18n\Time) { 'time' => '2017-02-22T18:17:15+09:00', 'timezone' => 'Asia/Tokyo', 'fixedNowTime' => false }, ],
Cakeのコアソースを調べる
3.3.13のdateのフォーマット部分のソースは以下になります。
protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH:mm:ssxxx"; *4
3.2.13では以下の通り、フォーマットが違いました。
protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH:mm:ssZ"; *5
どうやら、3.x系でフォーマットが変更されたようです。
それでも、なぜ、検証環境でこのフォーマットが効かないのか原因がさっぱりわかりませんでした。
最後の手段で、CakePHP(JP)のslackチャンネル*6で相談したところ、
「サーバのICU*7のバージョンが古いのでは?」という助言をいただくことが出来ました。
(Cakeチャンネルの皆様、あのときは大変お世話になりました。m(_ _)m 感謝)
Cakeチャンネルの @chinpei215 さんにいろいろ確認手順を教えていただいて、以下のことがわかりました。
調査結果
- ICUが古くて、
xxx
フォーマットに対応していない可能性 - ICUバージョン確認
php -i | grep ICU
$ php -i | grep ICU ICU version => 50.1.2 ICU Data version => 50.1 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- 正常に動く人の環境は バージョン
57.1
- CentOS 6.5の バージョン
50.1.2
で確認してもらったら、同じ現象になった - おまけ:CakePHPソースコード上での、動作確認コード
echo (new \IntlDateFormatter('ja_JP', null, null, null, null, "yyyy-MM-dd'T'HH:mm:ssxxx"))->format(new \DateTime());
対応案
- ① src/Controller/AppController.php::initialize() に 以下を追加
Time::setJsonEncodeFormat('YYYY-MM-dd HH:mm:ssZ');
- なんか、付け焼き刃みたいな対応で嫌だ
- ② ICUのバージョンを上げる
- 本当はこっちが正解なんだと思う
- ICUの詳細については後述
今回の反省点
結局、CakePHPのアップデートは見送りに
今回は、PHPの拡張モジュールである intl
*8のバージョンを上げないとならないという、問題にぶち当たってしまいました。
CakePHPの動作環境は、公式のページ*9にも記載されていて、PDO
始め主要なものはちゃんとチェックしていたつもりでいました。
が、まさか intl
に落とし穴があるとは・・・。
簡単にPHP拡張モジュールのアップデートも行えないので、今回のアップデートは、見送ることになりました。
もうちょっとちゃんとUTすればよかった
非同期でデータを取得してくる機能のUTは、戻り値の項目数を全てテストはしていませんでした。
任意の何項目かをピックアップしてテストしていて、肝心の日付(Date)部分は、テストしていませんでした。
今回の事で、日付項目もテストに追加したいと思います。
あと、コアのCakePHP自体のUTも、開発環境で流してみたいところです。
以上、めちゃ長くなりましたが、次回のCakePHPでの案件の課題にしたいと思います。
おまけ PHPとICUとintl
国際化用拡張モジュール(Intl)
- http://php.net/manual/ja/intro.intl.php
intlは » ICU ライブラリのラッパーです
問題発覚時の環境メモ
- PHPは、PHP 7.0.15
- intl のバージョンは以下
- version 1.1.0
- ICU version 50.1.2
- ICU Data version 50.1
phpinfo();
でも確認できる
ICUおまけ
- ICUの脆弱性
- JVNVU#97322697: ICU4C ライブラリに複数の脆弱性 https://jvn.jp/vu/JVNVU97322697/
*1:https://github.com/dereuromark/cakephp-tools
*2:https://github.com/dereuromark/cakephp-tinyauth
*3:https://github.com/dereuromark/cakephp-tools/blob/master/docs/Upgrade.md
*4:https://github.com/cakephp/cakephp/blob/3.3.13/src/I18n/DateFormatTrait.php#L58
*5:https://github.com/cakephp/cakephp/blob/3.2.10/src/I18n/DateFormatTrait.php#L58
*6:CakePHP公式の日本語話者向けSlackチャンネル開設のご案内 - Qiita https://qiita.com/chinpei215/items/3c116171c5308365c314
*7:International Components for Unicode という Unicode 関連のオープンソースライブラリ