「サイボウズ・アドベントカレンダー」の2日目です(これまでの記事一覧)。
こんにちは。Garoonのメンテナンスチームの横田です。
JavaScriptだなんだと言っていますが、今日はPHPのバージョンアップの話です。
以前話題になっていた記事やコメントをみると、恐ろしすぎてついていけないです。しかし、いつかはPHPのサポートが切れて、PHP5.4への移行をしなくてはいけません。gkbrモノですよね!特段すごいことでないと前置きしておきますが、できるだけコードを触らずにPHP5.4で動かすためには、どうしたらいいのかをちょちょいと説明しちゃいます!
PHP5.4対策
まずは、php.iniファイルのE_STRICTを切ります。PHP5.4ではデフォルトでONになりますので、明示的に切りましょう。
Call-time pass-by-reference(関数呼び出し時の参照渡し)がPHPの機能として削除されてしまっているので、オプションを削除してください。ちなみに関数呼び出し時の参照渡しとは、以下のようなコードです。
/** * @param $a $aは参照渡し * @param $b $bは値渡し * @see http://php.net/manual/ja/language.references.pass.php * @return bool * この関数だけでは、Fatal Errorは発生しないことに注意してください **/ function somethingMethod( &$a, $b ) { return true; } $a = 1; $b = 2; /** ここでエラーが発生 */ somethingMethod(&$a, $b);
php.iniの設定
error_reporting = E_ALL & ~E_STRICT ;; 削除する ;;allow_call_time_pass_reference = On
ほかにも削除されているディレクティブがありますので、ここで確認してみてください。
call_time_referenceに対応する
PHP5.4では Call-time pass-by-referenceが php -l コマンドで見つけることができるので、実行せずとも問題のコードを見つけることができます。素晴らしい!これとE_STRICT切りで、PHP5.4は動いたも同然です!
php -l trunk/code/something.php Fatal error: Call-time pass-by-reference has been removed in trunk/code/something.php on line 112 ...
ちなみに社内のコードでは150箇所ほどでした(´・ω・`)ヒィ。ここを対応したら、だいたいは動くようになりましたー。
あと、php -lでFatal Errorが発生してしまうと、後のコードは、見てくれないので、修正とリントを繰り返してくださいね。
文字列offset問題
上の2つでPHPそのものは動きますが、文字列のOffset問題は、非常に辛い問題でして、品質保証という点に関しては回帰試験が必要でしょう。
(´・ω・`)キツイ…
$str = 'abc'; var_dump($str['1']); var_dump(isset($str['1'])); var_dump($str['1.0']); var_dump(isset($str['1.0'])); var_dump($str['x']); var_dump(isset($str['x'])); var_dump($str['1x']); var_dump(isset($str['1x']));
PHP5.3の場合
string(1) "b" bool(true) string(1) "b" bool(true) string(1) "a" bool(true) string(1) "b" bool(true)
PHP5.4の場合
string(1) "b" bool(true) Warning: Illegal string offset '1.0' in /tmp/t.php on line 7 string(1) "b" bool(false) Warning: Illegal string offset 'x' in /tmp/t.php on line 9 string(1) "a" bool(false) string(1) "b" bool(false)
- 真偽判定がひっくり返る
- 新たにエラーが発生するように
E_STRICTを見つけ出す方法
E_STRICT対応を逃げても今まで挙げた対応が出来れば、PHP5.4でも動きます。この方法はE_STRICTも対応したいという方向けです。
E_STRICTの中にはコンパイル時に発生するものがあります。なので、phpの実行時にエラーレベルをMAXにして、php -lをかけるとすこし幸せになれます。といっても実行時エラーも多く、継承関係がおかしいのぐらいしか警告してくれませんが、実際にコードを動かさなくても、出せるエラーがあります。
class ParentClass { public function methodA($a,$b){} //E_STRICT error } class ChildClass extends ParentClass { public function methodA($a){} }
エラーをMAXにしてphp -lをかけます。
./plain-php5.4.8/bin/php -d display_errors=1 -d html_errors=0 -d error_prepend_string=" " -d error_append_string=" " -d error_reporting=4095 -l sample.php Strict Standards: Declaration of ChildClass::methodA() should be compatible with ParentClass::methodA($a, $b) in sample.php on line 17
テストコードのtips
やっぱりどうしてもテストを書けないという時があります。そういう時は、レビューアーとQAとで協力して乗り切る他ありませんが、test_helperを使うと少し幸せになれるかもしれません。
関数の中にプロセスが終了関数があって、テストが途中終了する
クラス・メソッドならまだ、事態はマシです。テストコードを書くチャンスがあります。「レガシーコード改善ガイド」(マイケル・C・フェザーズ著) では、グローバル関数は、オブジェクト接合部で解決されますが、PHPはそんなに甘くないです。
たとえばこんなコードがあって、
function cybozu_throw_error($err_msg) { print_error($err_msg); die();//最悪(>_<;) }
こんなところに使われているとか
class ClassA { public function someMethod() { if( $this->isValid() ) { cybozu_throw_error("invalid in some Method"); } } }
ClassA::isValid()の返り値がtrueだと、PHPUnitが途中で終了して、テストもままなりません。
peclライブラリのtest_helperを利用する
- https://github.com/sebastianbergmann/php-test-helpers
- http://php.net/manual/ja/function.rename-function.php
こんなかんじで、グローバル関数の置き換えができたりします。
function fake_cybozu_throw_error() {/* do nothing */} rename_function( "cybozu_throw_error", "orig_cybozu_throw_error"); rename_function( "fake_cybozu_throw_error", "cybozu_throw_error");
Garoonを改善してくださるプログラマ募集
PHPマニュアルにのっていることがほとんどで、お茶を濁した感がすごいです(スイマセン)。レガシーコードを触っているとだんだん周りが見えなくなってくるので、「こういう方法もあるよー」的なバージョンアップ対応方法を紹介してみました。少しづつPHP5.4に向けて、準備出来るところもあるはずです。いきなり全部やろうとすると大やけどをするので、用法用量を守ってがんばりましょう。
僕が現在関わっているGaroonを改善してくださる同志を募集しております。くわしくは採用情報へ。
ほんとたすけてくだしあ・・・ 一緒にがんばりましょうー。
明日は「Webブラウザでフリック体験」をお送りします。お楽しみに。