PHP 7系から8系への移行で突然サイトが壊れるのはなぜ?後方互換性の破壊的変更と、安全に移行するための実践的テスト手順を解説します。
「昨日まで動いていたのに、なぜ?」
こんな経験はありませんか?
サーバー会社から「PHP 7.4のサポートが終了します。PHP 8.x への移行をお願いします」というメールが届き、「まあ大丈夫だろう」と気軽にバージョンを切り替えたら、翌朝サイトが真っ白になっていた——。
Fivenine Design にも、そういったご相談が年に数件届きます。先日も、神奈川県内の製造業のクライアントから「WordPressサイトが急に表示されなくなった。PHP を 8.1 に上げた直後から」とお問い合わせをいただきました。原因を調べると、長年使ってきたプラグインが PHP 8 で廃止された関数に依存していたことが判明。バックアップからの切り戻しと修正対応に丸1日かかりました。
PHPのバージョンアップは「アップデート」ではなく「移行」です。 同じ言語であっても、8系は7系と比べて数十箇所にわたる「後方互換性を壊す変更(Breaking Changes)」が含まれています。この記事では、なぜコードが壊れるのかという根本的な理由と、本番環境に手を触れる前に実施すべき事前テストの手順を、実際の現場経験をもとに解説します。
なぜ「動いていたコード」が壊れるのか
PHP のメジャーバージョンアップには、後方互換性のない変更(Breaking Changes) が必ず含まれています。これは「古い書き方を許容しなくなった」という意味であり、構文エラーや型の扱いの厳格化が主な原因です。
PHP 8系の主要な破壊的変更
① 廃止された関数・クラス
PHP 8.0 で each() 関数が削除され、create_function() も使えなくなりました。PHP 8.1 では mysqli_connect() のデフォルト引数の扱いが変わり、古いWordPressプラグインで頻繁にエラーが発生します。
② 型の厳格化(Type Coercion の変更)
PHP 7 までは文字列 "1" を数値 1 として暗黙的に扱っていましたが、PHP 8 では内部的な型変換ルールが変更されました。特に、match 式の導入と厳格な比較(===)の推奨が、既存の switch 文依存のロジックを壊すことがあります。
③ エラーから例外への昇格
7系では「警告(Warning)」として素通りしていたものが、8系では TypeError や ValueError として例外を投げるようになりました。try-catch で囲んでいないコードは、そこで処理が止まります。
// PHP 7: 警告を出しながらも動作した
$result = 0 + "10 apples";
echo $result; // 10
// PHP 8.1+: Deprecation Notice が出るようになった
// 将来的には TypeError に昇格予定
$result = 0 + "10 apples";
// Deprecated: Implicit conversion from float-string "10 apples" to int
④ null の扱いの厳格化
PHP 8.1 から導入された never 型や、Fibers、Enum など新機能の追加と並行して、null を文字列関数に渡すことが非推奨になりました。
// PHP 8.1 で Deprecated Warning が出るコード
$str = null;
echo strlen($str); // Deprecated: Passing null to non-nullable
// 正しい書き方
echo strlen($str ?? '');
こういった変更が数十個積み重なると、大規模なプロジェクトほど影響箇所が多く、単純なバージョン切り替えでは必ず問題が発生します。
事前テストの実践手順
「本番で試して壊れたら戻せばいい」という発想は非常に危険です。切り戻しの間もサイトはダウンし続け、SEOへの悪影響やユーザーの離脱につながります。以下の手順で 本番を触る前に問題を洗い出す ことが鉄則です。
Step 1: 静的解析ツールで問題箇所を一覧化する
まず手を動かすより前に、PHP_CodeSniffer と Rector を使った静的解析を実施します。コードを実行せずに「PHP 8 で問題になる書き方」を検出できます。
# PHP_CodeSniffer のインストール
composer require --dev squizlabs/php_codesniffer
# PHP 8 互換性チェックプラグインの追加
composer require --dev phpcompatibility/php-compatibility
# プロジェクト全体をスキャン(PHP 8.1 を対象に)
./vendor/bin/phpcs --standard=PHPCompatibility \
--runtime-set testVersion 8.1 \
--extensions=php ./src
Rector の設定ファイルは以下のように記述します。
// rector.php
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/src',
__DIR__ . '/app',
]);
// PHP 8.0 → 8.1 への移行ルールを適用
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_81,
]);
};
Step 2: ローカル環境またはステージング環境でPHPバージョンを切り替える
Dockerを使っている場合は docker-compose.yml の PHP イメージを変更するだけで切り替え可能です。本番と同じデータを使ったステージング環境があるなら、そちらで先に試します。
# docker-compose.yml
services:
app:
image: php:8.1-fpm # 7.4 から変更
volumes:
- ./src:/var/www/html
Step 3: エラーログを有効にしてすべての Warning を可視化する
本番では通常エラーを非表示にしていますが、テスト環境では すべての警告を表示 させます。PHP 8 への移行では、Fatal Error だけでなく Deprecated や Warning も将来の破壊的変更の予兆です。
// php.ini または .htaccess で設定
error_reporting(E_ALL);
ini_set('display_errors', '1');
ini_set('log_errors', '1');
ini_set('error_log', '/var/log/php_errors.log');
Step 4: 自動テストで動作確認(PHPUnit)
テストコードがある場合は、PHP バージョンを変更した状態でテストスイートをすべて実行します。テストが存在しない場合でも、最低限クリティカルな処理(決済、フォーム送信、ログインなど)は手動で確認します。
# PHPUnit でテスト実行
./vendor/bin/phpunit --stop-on-failure
# Laravel プロジェクトの場合
php artisan test --parallel
flowchart TD
A[PHP 8移行開始] --> B[静的解析\nPHP_CodeSniffer / Rector]
B --> C{問題箇所あり?}
C -->|Yes| D[コード修正・\nRectorで自動修正]
D --> B
C -->|No| E[ステージング環境で\nPHPバージョン切替]
E --> F[エラーログ全開で\n動作確認]
F --> G{エラー・警告あり?}
G -->|Yes| H[修正対応]
H --> F
G -->|No| I[自動テスト実行]
I --> J{テスト通過?}
J -->|Yes| K[本番移行]
J -->|No| Hよくある失敗パターンと対処法
失敗1: プラグイン・ライブラリの確認を怠る
自社コードは問題なくても、使用しているライブラリやWordPressプラグインが PHP 8 未対応 というケースが非常に多いです。冒頭のクライアント事例もこれでした。
対処法として、composer outdated コマンドで依存ライブラリの最新状況を確認し、各パッケージの GitHub リリースノートで「PHP 8.x」への対応を明示しているかをチェックします。WordPressの場合、プラグインのページに「テスト済みの最新バージョン」が表示されているので必ず確認してください。
失敗2: == と === の混在
PHP 8 では比較演算子の型処理が変わりました。0 == "foo" が PHP 7 では true を返していましたが、PHP 8 では false になります。認証ロジックや条件分岐に == を多用しているコードは要注意です。
// PHP 7: true(危険な挙動)
var_dump(0 == "foo"); // bool(true)
// PHP 8: false(正しい挙動)
var_dump(0 == "foo"); // bool(false)
// 型を明示した比較に書き換えることが推奨
var_dump(0 === "foo"); // bool(false) ← こちらに統一する
失敗3: 移行後にバックアップを取っていない
「バックアップは移行前に取ればいい」と思われがちですが、移行作業中にも定期的にスナップショットを残す ことが重要です。修正が複数回にわたる場合、どの時点まで戻すかが問題になります。本番移行の直前に最新バックアップを取得し、切り戻し手順を事前に文書化しておきましょう。
開発・運用でお困りなら
システム開発
設計から運用まで、堅牢なシステムを構築します
※ 通常1営業日以内にご返信します
まとめと次のステップ
PHP 8 への移行を安全に進めるには「とりあえず上げてみる」ではなく、静的解析 → ステージングでの検証 → 自動テスト という段階を踏むことが欠かせません。特に長く運用してきたサイトほど、誰も把握していない古いコードが潜んでいるものです。
Fivenine Design では、PHP バージョン移行の事前診断から修正対応・本番切替まで一貫してサポートしています。「自分でやってみたが、どうしても解消できないエラーがある」「移行してみたいが怖くて踏み出せない」という方は、お気軽にご相談ください。20年以上の実績で、安全な移行をお手伝いします。