古いPHPから現代のPHPへ
あるところにPHP 5.4で構築されたWEBアプリケーションがありました。
このアプリケーションが載っているOS、Amazon Linux 2がEOLを迎えることを受けて、Amazon Linux 2023で再構築することになりましたが、さすがにPHP 5系はおろか、7系もサポートされていません。
そこで、5系(5.4)から8系(8.3)へ一気にジャンプアップを試みることとなりました。
果たして需要がある記事なのかは分かりませんが、備忘録的に…
公式の差分情報
特に下位互換性のない変更について
- PHP 5.5.xからPHP5.6.xへの移行 - 下位互換性のない変更点
- PHP 5.6.xからPHP7.0.xへの移行 - 下位互換性のない変更点
- PHP 7.0.xからPHP7.1.xへの移行 - 下位互換性のない変更点
- PHP 7.1.xからPHP7.2.xへの移行 - 下位互換性のない変更点
- PHP 7.2.xからPHP7.3.xへの移行 - 下位互換性のない変更点
- PHP 7.3.xからPHP7.4.xへの移行 - 下位互換性のない変更点
- PHP 7.4.xからPHP8.0.xへの移行 - 下位互換性のない変更点
- PHP 8.0.xからPHP8.1.xへの移行 - 下位互換性のない変更点
- PHP 8.1.xからPHP8.2.xへの移行 - 下位互換性のない変更点
- PHP 8.2.xからPHP8.3.xへの移行 - 下位互換性のない変更点
今回出くわしたモノたち
あくまで今回、自分が対応したものを抜粋したものです。
型の概念が厳密になった
-
count()の引数は配列
# 古いコード
if (count($hoge)) {
...
}
値の存在チェックを兼ねてこういう書き方をしていたところは、array以外のパラメータ(Nullだったり空の文字列だったり)が渡されるとUncaught TypeError: count(): Argument #1 ($value) must be of type Countable|arrayが発生するようになりました。
事前にis_array()で型チェックをするか、配列かNullの二択が明示的ならisset()で存在チェックをする必要があります。
- それは配列ではありません
# 古いコード
$hoge = "";
...
$hoge[] = "val";
最初に文字列として初期化していたのにしれっと配列として使おうとしていたところは、Uncaught Error: [] operator not supported for stringsが発生するようになりました。
きちんと配列なら配列として初期化しなければいけません。
- そんな定数はありません
# 古いコード
$arr[hoge]
連想配列の参照の際、インデックス文字列をうっかり''や""で囲み忘れていたところは、定数として認識されるのでUncaught Error: Undefined constantが発生するようになりました。
むしろ5系はよくこれで動いていたな…。
# 古いコード
if ($var == fuga) {
...
}
同様に、文字列との比較をするところでうっかり囲み忘れていたところも、同じくエラーが発生するようになりました。
むしろ5系はよく(略
- 空文字列とは計算できません
$str_num = "";
$int_num = 0;
$sub = $str_num - $int_num;
空文字列として初期化した変数は四則演算できません。Uncaught TypeError: Unsupported operand typesとなります。
ただ、文字列の数値は継続して計算できますが。
$str_num = "100";
$int_num = 50;
$sub = $str_num - $int_num;
# 50
様々な関数・記法の廃止
-
ereg系 →preg系
ereg()やereg_replace()が廃止となりました。それぞれ、preg_match(),preg_replace()に置き換えます。
# 古いコード
if (ereg("fuga",$hoge,$matches)) {
...
}
$replaced_hoge = ereg_replace("foo","bar",$hoge);
# 修正後のコード
if (preg_match("/fuga/",$hoge,$matches)) {
...
}
$replaced_hoge = preg_replace("/foo/","bar",$hoge);
-
mcrypt系 →openssl系
mcrypt系がPHP 7.2で廃止されました。暗号化・復号関数はそれぞれ置き換えます。
mcrypt_encrypt()→openssl_encrypt()
mcrypt_decrypt()→openssl_decrypt()
ただし、例えばDBに保存済みの旧暗号化データがある場合は、PHPコードだけ入れ替えてしまうと復号できなくなってしまいます。
事前に旧バージョンの状態で、mcrypt_decrypt()で復号してopenssl_encrypt()で再暗号化して保存し直しておく必要があります。
-
implode()の引数順序
# A
implode($hogearr, ",")
# B
implode(",", $hogearr)
かつては引数が配列・セパレータどちらの順でも動いていたのが、前者(A)の記法は廃止されました。
古いシグネチャ(PHP 7.4.0 で非推奨となり、PHP 8.0.0 以降は削除されています
- new参照代入廃止
# 古いコード
$hoge =& new Hoge();
newで生成したインスタンスを参照代入することはできなくなりました。
基本、通常の代入で問題ないでしょう。
new 演算子の結果に対して同じ構文を使った場合もエラーになります。
関数の仕様変更
-
str_replace()の挙動変更
かつては二次元配列を渡しても置換を行ってくれていたのですが、PHP 8系より挙動が変わった模様です。
一次元配列(一次元の連想配列含む)は問題ないですが、二次元(以上の)配列を渡すと、子要素の配列は"Array"という文字列になってしまうのです。(Array to string conversionというWarningが出ます)
# 一次元配列
$arr_1 = ['hoge', 'hogehoge', 'foo'];
$replaced_arr = str_replace('hoge','fuga',$arr_1);
// ['fuga', 'fugafuga', 'foo']
# 二次元配列含む
$arr_2 = ['hoge', ['hogehoge'], ['foo','bar']];
$replaced_arr = str_replace('hoge','fuga',$arr_2);
// ['fuga', 'Array', 'Array']
In PHP 8.0, the inner arrays are cast to a string, aka Array and then, the replacements occurs.
Notice → Warning
多くのログレベル Notice だったものが、Warning になりました。
特に以下は多発している印象です。
Undefined variable ...
Undefined array key ...
Trying to access array offset ...
このあたりも事前に初期化や、値・キーの存在チェックをしていきましょう、ということでしょう。
あまりに多すぎて目を瞑るところも…
振り返り
自由すぎたPHPも、やはりTypeScriptのような型推論の方向性でしょうか。