【PHP】PHPのバージョンアップで起きやすい非互換エラーとその対処法

phpアイキャッチ PHP

こんにちは、かつコーチです。

前回はComposerの基本を解説しました。

Composerでパッケージを管理するようになると、避けて通れないのが「PHPのバージョンアップ」への対応です。

サーバーのPHPバージョンが上がった途端、昨日まで動いていたコードが急にエラーを吐き出すようになった、という経験は中級者になるとほぼ全員が通る道です。

今回は、PHPのバージョンアップで特に起きやすい非互換エラーと、その具体的な対処法をまとめます。

なぜバージョンアップでエラーが起きるのか

PHPは後方互換性を段階的に見直している

PHPは長い歴史を持つ言語で、その分「昔は動いていたけれど今は非推奨、あるいは廃止された書き方」が少なからず存在します。

開発チームは新しいバージョンを出すたびに、古い書き方に対して次のようなステップを踏むのが一般的です。

  1. まずは動くが警告(Deprecated)を出す
  2. しばらくして完全に廃止し、エラーになる

このプロセスがあるため、「PHP7では警告だけだったのに、PHP8では動かなくなった」という現象が起こります。

主要バージョンで何が変わったか

特に影響が大きいのが、PHP7系からPHP8系への移行です。

バージョン主な変更点
PHP 7.4 → 8.0型に関するエラーの厳格化、多くの警告レベルの引き上げ
PHP 8.0 → 8.1Enum追加、読み取り専用プロパティの追加など
PHP 8.1 → 8.2動的プロパティの非推奨化

バージョンアップの際は、公式のマイグレーションガイド(PHPマニュアルの「移行ガイド」)に必ず目を通す習慣をつけておくと安心です。

非互換エラーの具体例1:文字列と数値の比較

挙動が変わったポイント

PHP8では、文字列と数値を比較したときの挙動が一部変更されました。

<?php
// PHP7系では true と判定されるケースがあった
var_dump(0 == "abc");

PHP7系では、数値の0と数値に変換できない文字列"abc"を比較すると、"abc"0として扱われtrueになってしまうケースがありました。

PHP8ではこの挙動が見直され、0 == "abc"falseと判定されるようになっています。

かつコーチが実際につまずいたケース

私が担当していた古いシステムで、こんな認証チェックのコードがありました。

<?php
// ❌ Before:PHP7系の挙動に依存した危険な比較
$inputToken = "invalid_token_string";
$correctToken = 0; // 何らかの理由で数値の0が入ってしまっていた

if ($inputToken == $correctToken) {
    echo "認証成功"; // PHP7系ではここが実行されてしまうことがあった
}

サーバーをPHP7.4から8.1にアップグレードした直後、このコードの挙動が変わり、逆に「今まで通ってしまっていた不正な比較が通らなくなった」ことでテストが失敗し始めました。

原因を調べて初めて、このコード自体がPHP7系の危うい挙動に依存した書き方だったと気づき、冷や汗をかいた記憶があります。

<?php
// ✅ After:型まで厳密に比較する === を使う
$inputToken = "invalid_token_string";
$correctToken = 0;

if ($inputToken === $correctToken) {
    echo "認証成功";
} else {
    echo "認証失敗";
}
// 出力: 認証失敗(型が違うため正しく判定される)

このケースは結果的にPHP8での挙動の方が安全な方向に倒れましたが、==に依存したコードはバージョンによって結果が変わるリスクがあることを痛感した出来事でした。

普段から===を使う習慣があれば、そもそも起きなかったトラブルです。

非互換エラーの具体例2:null を渡せなくなった内部関数

DeprecatedからTypeErrorへの変化

PHP8.1以降、多くの組み込み関数で「引数にnullを渡すこと」が非推奨になり、8.1〜8.3にかけて段階的に警告が強化されています。

<?php
$name = null;

// ❌ Before:null になり得る変数をそのまま渡してしまう
echo strlen($name);
Deprecated: strlen(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/html/index.php on line 4

処理自体はまだ止まりませんが、Deprecated警告がログを埋め尽くし、将来のバージョンで完全にエラー化される可能性があります。

対処法:null合体演算子で事前にガードする

<?php
$name = null;

// ✅ After:null合体演算子でデフォルト値を用意しておく
echo strlen($name ?? "");
// 出力: 0

??(null合体演算子)で「nullだったら空文字を使う」という防御を1行入れるだけで、警告を防げます。

外部APIやDBから取得した値をそのまま組み込み関数に渡している箇所は、特に注意が必要です。

非互換エラーの具体例3:動的プロパティの非推奨化

PHP8.2で追加された変更

PHP8.2からは、クラスの定義時に宣言していないプロパティを後から追加する「動的プロパティ」が非推奨になりました。

<?php
class User
{
    public string $name;
}

$user = new User();
$user->name = "かつコーチ";

// ❌ Before:クラスに定義していないプロパティを追加している
$user->age = 30;
Deprecated: Creation of dynamic property User::$age is deprecated in /var/www/html/index.php on line 10
<?php
// ✅ After:クラスにプロパティをきちんと定義しておく
class User
{
    public string $name;
    public int $age;
}

$user = new User();
$user->name = "かつコーチ";
$user->age = 30; // 定義済みのプロパティなので警告は出ない

クラスを使い始めたばかりの頃はつい後からプロパティを追加しがちですが、PHP8.2以降を意識するなら、必要なプロパティは最初にすべて宣言しておくのが安全です。

バージョンアップ時の実践的な対応手順

手順1:現在使用中のバージョンを確認する

php -v

手順2:Composerパッケージの対応バージョンを確認する

composer.jsonに記載されている各パッケージが、アップグレード先のPHPバージョンに対応しているかを確認します。

composer outdated

このコマンドで、更新可能なパッケージの一覧を確認できます。

手順3:ローカル環境で新バージョンを試す

いきなり本番サーバーのPHPバージョンを上げるのではなく、ローカル環境やステージング環境で新バージョンを試し、Deprecated警告を洗い出しておくことが重要です。

<?php
// 開発環境ではすべてのエラー・警告を表示してチェックする
error_reporting(E_ALL);
ini_set("display_errors", "1");

前回・前々回で扱ったエラー表示の設定を使い、警告を1つずつ潰していくのが、バージョンアップ作業の基本的な流れです。

手順4:影響範囲の大きい箇所から優先的に対応する

認証まわり・決済まわりなど、影響範囲が大きい箇所から優先的にテストし、非互換エラーがないか確認していきましょう。

私は現在、バージョンアップ作業をするときは必ずこの4手順をチェックリスト化して進めるようにしています。

行き当たりばったりで進めると、思わぬ箇所で本番障害につながりかねないためです。

まとめ

この記事のポイント

  • PHPは新バージョンごとに「まず警告、その後廃止」という段階を踏んで後方互換性を見直している
  • PHP8では文字列と数値の比較(==)の挙動が変更され、===を使う習慣がリスク回避につながる
  • 組み込み関数へのnull渡しは非推奨化が進んでいるため、??で事前にガードする
  • PHP8.2以降は動的プロパティが非推奨。クラスのプロパティは最初にすべて宣言しておく
  • バージョンアップ時は「現状確認→パッケージ確認→ローカル検証→優先度順の対応」の手順で進める

次に読むべき記事

非互換エラーへの対処法が分かったところで、次はエラー調査に欠かせないvar_dumpprint_rによるデバッグ方法と、より本格的なデバッグツール「Xdebug」の導入を解説していきます。

→ 次の記事:var_dump・print_rでのデバッグ方法とXdebugの導入

タイトルとURLをコピーしました