技術約15分で読めます

PHP 5.3から8.6で後方互換性が壊れた関数・引数・名前空間の変更点

いけさん目次

古いPHPのコードを新しい版へ移行するときに困るのは、構文エラーで止まる箇所より、組み込み関数の戻り値や比較結果がエラーも出さずに変わってしまう箇所だ。たとえば 0 == "foo" は、PHP 7系までは true だったが、PHP 8.0からは false を返す。構文チェックを通り、警告すら出ないまま処理結果だけが静かにすり替わる。

PHP 8.6は2026年10月22日にリリース候補第4版(RC4)が出て、正式版は2026年11月19日に予定されている。PHP 5.3から8.6までのあいだに入った後方互換性のない変更のうち、組み込み関数の挙動、引数の受け取り方、名前空間に関わる変更を版ごとに整理した。

非推奨と挙動変更における壊れ方の違い

仕様変更には大きく分けて2種類の壊れ方がある。

1つは非推奨(deprecation)で、利用時に E_DEPRECATED の通知が出るだけで処理自体はそのまま継続する。非推奨になった機能は、次のメジャーバージョン(PHP 8.x で非推奨になったものならPHP 9.0)で削除されるかエラーになる方針が取られる。通知を表示しない設定のサーバーでは、削除される版に上げた時点で初めて動かなくなる。

もう1つは挙動の変更で、その版に上げた時点で戻り値や比較結果が変わる。例外を投げるようになったものは処理が止まるため検知しやすいが、比較結果や並び順が変わる変更はエラーも出ずに結果だけが違ってくる。

PHP 4から5でオブジェクトの代入が参照渡しに変わった

PHP 4では、$b = $a と書くとオブジェクトがまるごとコピーされていた。PHP 5からは変数がオブジェクト本体を直接保持せず識別子を持つようになり、代入すると同じオブジェクトを2つの変数が指すようになった。$b のプロパティを書き換えると $a 側も変わるため、PHP 4時代のコードは意図どおり動かなくなる。複製が必要な箇所は clone で明示する。

PHP 5.3〜5.6での名前空間導入と危険な機能の削除

PHP 5.3で名前空間が追加され、5.4では長く残っていた設定や古い関数がまとめて削除された。

版変更壊れ方
5.3ereg()、eregi()、split() などの ereg 系関数を非推奨にした非推奨。7.0で削除
5.3名前空間を導入した。区切り文字は開発中に :: から \ へ変更名前空間を使わないコードには影響しない
5.4register_globals、magic_quotes_gpc、safe_mode などの設定を削除した設定自体がなくなり、依存していたコードの前提が成り立たなくなる
5.4htmlspecialchars() と htmlentities() の既定の文字コードを、ISO-8859-1からUTF-8に変えた即時。Shift_JISやEUC-JPの文字列を文字コード指定なしで渡すと、空文字になる場合がある
5.6htmlspecialchars() と htmlentities() の既定の文字コードが、php.ini の default_charset に従うようになった即時。サーバーの設定で結果が変わる

PHP 7.0での関数削除と式の評価順の変更

7.0では、5.xで非推奨になった機能をまとめて削除し、式の評価順も見直された。

変更壊れ方
mysql_* 関数(PHP拡張の ext/mysql)を削除した即時。関数がなくなる
ereg 系関数と、preg_replace() の /e 修飾子を削除した即時
可変変数の評価順を左から右に統一した。$$foo['bar'] は5.xでは ${$foo['bar']}、7.0からは ($$foo)['bar'] と解釈される即時。エラーにならず別の変数を参照する
list() が右の要素からの逆順代入をやめ、左からの順序代入に統一された即時。同じ変数へ複数回代入するコードの結果が変わる
参照なしの foreach が、配列の内部ポインタを変更しなくなった即時。ループ中に current() を呼ぶコードの結果が変わる
"0x1A" のような16進数の文字列を数値として扱わなくなった即時。is_numeric("0x1A") が false になる

PHP 7.1〜7.4で増えた警告と非推奨

7.x後半は、それまで警告なしで通していた使い方に警告や非推奨を付け、8.0でエラーにする前段階にあたる。

版変更壊れ方
7.1ユーザー定義関数を引数が足りないまま呼ぶと、警告にとどまらず ArgumentCountError を投げるようになった即時。処理が止まる
7.1mt_rand() のアルゴリズムの誤りを修正した即時。同じシード(乱数の初期値)を与えても出てくる乱数の列が変わり、テストデータ生成などで再現できなくなる
7.2配列でも Countable でもない値を count() に指定すると警告が出るようになった警告。8.0で TypeError になる
7.2each() と create_function() を非推奨にした非推奨。8.0で削除
7.3ヒアドキュメント(複数行文字列構文)の終端マーカーをインデント可能にし、直後に改行がなくても許可した即時。本文中に終端マーカーと同じ文字列が行頭近くにあると、そこでヒアドキュメントが終わって構文エラーになる
7.4括弧なしでネストした三項演算子と、$str{0} のような波括弧での文字アクセスを非推奨にした非推奨。8.0でエラーになる
7.4implode() に区切り文字と配列を逆順で指定する書き方を非推奨にした非推奨。8.0で削除
7.4配列以外の値(null や int など)に $x['key'] で配列アクセスすると通知が出るようになった通知。Trying to access array offset on value of type null がログに出る

PHP 8.0での比較と型エラーの刷新

8.0は比較や型の挙動が大きく刷新され、エラーなしで結果が変わる変更が目立つ。

変更壊れ方
数値と非数値文字列を == で比較するとき、数値側を文字列に変換して評価するようになった。0 == "foo" は7.xまで true、8.0から false即時、エラーなし。in_array()、switch、array_search() の結果がエラーなしで変わる
組み込み関数に型の合わない引数を渡すと TypeError、範囲外の引数を渡すと ValueError を投げるようになった即時。警告を出して null や false を返していた箇所で処理が止まる
cURL、GD、OpenSSL、XMLパーサー、ソケットなどの値がリソース型からオブジェクト型に変わった即時。is_resource() で成功を判定していたコードが常に失敗と判定される。戻り値は false かどうかで判定する
名前付き引数を導入した即時。引数名がAPIの一部になった。組み込み関数の引数名も整理されている
文字列連結 . と、+・-・ビットシフトの優先順位を変えた。"合計: " . $a + $b は7.xまでは ("合計: " . $a) + $b と解釈され、8.0からは $a + $b が先に評価される即時、エラーなし
@ 演算子で致命的エラーを防げなくなった即時。隠れていたエラーで止まるようになる
クラス名と同じ名前のメソッドをコンストラクタとして扱わなくなった(PHP 4形式)即時。初期化処理が呼ばれなくなる
並べ替え関数が安定ソートになった即時、エラーなし。比較結果が等しい要素同士の並び順が変わり得る
strpos() などに整数の検索対象(needle)を渡しても文字コード解釈をやめ、文字列として探すようになった即時、エラーなし。strpos($s, 65) は "A" ではなく "65" を探す

PHP 8.1〜8.2のnullの扱いとDB接続の既定値

8.1では組み込み関数への null 渡しに非推奨が入り、ログの件数が一気に増えた。

版変更壊れ方
8.1null を受け付けない組み込み関数の引数に null を渡すことを非推奨にした(strlen(null)、trim(null)、str_replace() など)非推奨。9.0では TypeError になる予定
8.1mysqli 拡張の既定のエラー報告モードを、例外を投げる設定に変えた即時。if (!$result) で失敗を判定していたコードが例外で止まる
8.1PDOのMySQLとSQLiteで、エミュレートプリペア(PHP側でSQLを組み立てる仕組み)時に文字列型だった整数や浮動小数点数を、PHPの数値型で返すようになった即時、エラーなし。$row['id'] === "1" のような厳密比較が false になる
8.1htmlspecialchars() の既定のフラグを ENT_COMPAT から ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 に変えた即時。シングルクォートも ' に変換される
8.1組み込みクラスを継承したメソッドに戻り値の型がないと非推奨通知が出るようになった非推奨。#[\ReturnTypeWillChange] 属性で回避できる
8.1strftime()、gmstrftime()、FILTER_SANITIZE_STRING を非推奨にした非推奨
8.2宣言していないプロパティへの代入(動的プロパティ)を非推奨にした非推奨。古いフレームワークや自作クラスで大量に出る
8.2文字列内の "${var}" 記法と、utf8_encode()・utf8_decode() を非推奨にした非推奨
8.2strtolower()、strtoupper() などがロケール設定(地域や言語の設定)の影響を受けなくなった即時、エラーなし。ロケールに頼って変換していた文字が変わらなくなる
8.2"self::method" のように一部の文脈でしか呼べない形の callable を非推奨にした非推奨
8.2str_split("") が空の配列 [] を返すようになった即時、エラーなし

PHP 8.3〜8.4の引数の型宣言と丸め処理

版変更壊れ方
8.3引数なしの get_class()、get_parent_class() を非推奨にした非推奨
8.3range() の挙動を見直し、不正な引数で例外を投げるなどの変更を加えた即時
8.4function f(Foo $x = null) のように既定値 null で型をnull許容にする書き方(暗黙のnull許容型)を非推奨にした非推奨。?Foo $x = null と書く必要がある。古いライブラリの多くで通知が出る
8.4E_STRICT 定数と、trigger_error() に E_USER_ERROR を指定することを非推奨にした非推奨
8.4round() の内部で行っていた事前の丸めをやめた。0.285(内部では0.28499999999999998)のような値を10進数として扱うための処理だったが、一部の数を誤って丸めていた即時、エラーなし。境界付近の値で丸めの結果が変わることがある
8.4round() に不正な丸めモードを指定すると ValueError を投げるようになった即時。処理が止まる
8.4password_hash() の bcrypt の既定コストを10から12に引き上げた即時。ハッシュの計算時間が延びる。既存ハッシュの検証は成功する
8.4IMAP、OCI8、PDO_OCI、pspellの拡張をコアからPECL(外部拡張リポジトリ)へ移した即時。追加インストールしないと関数が使えない

PHP 8.5のキャスト名や配列キーのnull

PHP 8.5は2025年11月にリリースされた。

変更壊れ方
(integer)、(boolean)、(double)、(binary) という別名キャストを非推奨にした非推奨。(int)、(bool)、(float)、(string) を使う
バッククォートでシェルコマンドを実行する記法と、case 'x'; のようにcaseの後ろにセミコロンを置く書き方を非推奨にした非推奨
null を配列のキーに使うこと、array_key_exists() に null のキーを指定することを非推奨にした非推奨
数値ではない文字列をインクリメント($s = "a"; $s++; で "b" になる挙動)することを非推奨にした非推奨。str_increment() を使う
ord() に1バイトでない文字列を指定すること、chr() に0〜255の範囲外の整数を指定することを非推奨にした非推奨。マルチバイト文字列の先頭を ord() で取得するコードで通知が出る
curl_close()、imagedestroy()、finfo_close()、xml_parser_free() を非推奨にした非推奨。8.0でオブジェクトになってから何もしない関数になっていた
HTTPラッパーが自動で作る変数 $http_response_header を非推奨にした非推奨。http_get_last_response_headers() を使う
int の範囲を超える浮動小数点数を int にキャストすると警告が出るようになった警告
NAN(非数)をほかの型に変換すると警告が出るようになった警告
配列でない値を分割代入すると警告が出るようになった警告
PDO::FETCH_GROUP、PDO::FETCH_UNIQUE など5つの定数の値そのものを変更した即時。数値をハードコードしていたコードが動かなくなる
disable_classes 設定を削除した即時
__sleep() と __wakeup() の利用通知を出さないソフト非推奨(将来の移行を促す扱い)

名前空間の仕様の変遷

区切り文字はPHP 5.3の開発中は :: だったが、2008年10月のRFCで候補を比較し \ に変更された。

名前空間の中で関数名や定数名を書いたとき、その名前空間に定義がなければグローバルの同名関数・定数が使われるフォールバックがある。しかしクラス名にはこのフォールバックがない。名前空間内で strlen() と書くと組み込み関数が呼ばれるのに、Exception と書くと \Exception にならずエラーになるのはこの仕様の違いによるもので、5.3から一貫している。

PHP 8.0では Foo\Bar のような名前空間付き識別子が1つのトークンとして解釈されるようになり、fn や match のような予約語を名前空間の一部に使えるようになった。一方で Foo \ Bar のように区切りの前後に空白を入れる書き方は構文エラーになった。

8.2以降は新しい組み込みクラスが名前空間付きで追加されている(8.2の Random\Randomizer、8.4の Dom\HTMLDocument や BcMath\Number、8.5の Uri\Rfc3986\Uri など)。自作コードで同じ名前空間を使っていると衝突する可能性がある。

PHP 8.6のmb_ereg系非推奨とセッション設定の既定値

PHP 8.6の非推奨RFCでは35項目が投票にかけられ、否決された4項目(list() 構文、gettextの別名 _()、in/out/inout の識別子利用、dechunkフィルター)を除く31項目が可決された。

8.6で非推奨になる関数と構文

日本語を扱う環境で影響が大きいのは、mbregex関数(mb_ereg 系関数)の非推奨だ。mb_ereg()、mb_ereg_replace()、mb_split()、mb_regex_encoding() などの16関数が非推奨になり、PHP 9.0で削除される予定となっている。内部で使っている正規表現ライブラリOnigurumaのメンテナンスが2025年4月に終了したためだ。コア関数だけで代替する場合は preg_* 関数に u 修飾子を付けて書き直すことになるが、OnigurumaとPCRE(正規表現エンジン)では文字クラスや名前付きキャプチャなどの正規表現構文が一部異なるため、パターンごとに動作確認が要る。

分類非推奨になるもの置き換え先・影響
mbregexmb_ereg()、mb_ereg_replace() など16関数非推奨(9.0削除)。preg_* 関数(u 修飾子)や外部拡張 mb_onig を使う
関数の別名is_double()、is_integer()、is_long()、doubleval()is_float()、is_int()、floatval() を使う
関数spl_object_hash()spl_object_id() を使う
ロケール比較strcoll()、ソート関数の SORT_LOCALE_STRINGintl拡張の Collator を使う
関数metaphone()、spl_classes()、mysqli_get_charset()ユーザー側ライブラリや ReflectionExtension を使う
引数define() の第3引数(大文字小文字を区別しない定数)指定しない。8.0から機能していない
引数$allow_string を false にした is_a()・is_subclass_of() にクラス名文字列を渡すことオブジェクトを渡すか、$allow_string を true にする
引数array_walk()、http_build_query()、mb_convert_variables()、zlibやbzip2の関数に配列の代わりにオブジェクトを渡すこと未対応関数では TypeError になるため get_object_vars() で配列化する
構文finally ブロックの中の return呼び出し元への戻り値が上書きされるためブロック外で返す
構文__construct()・__destruct() から値を返すこと、ジェネレーターにすること戻り値は無視されるため値なしの return; にする
識別子let・is・_ をクラス名や定数名に使うこと、readonly という名前の関数、namespace という名前のクラス定数将来の構文予約。同名定義があるとコンパイル時エラーになる予定
SPLSplFileObject のCSV系メソッド、ArrayIterator の asort() などfgetcsv() 関数などを使う
mysqlimysqli_stmt_init()、クエリなしの mysqli_stmt コンストラクタmysqli::prepare() を使う
セッションcreate_sid() と validateId() を持たないセッションハンドラー未実装のハンドラーを渡すと非推奨通知が出る

8.6へ移行した時点で動作が変わる変更

変更壊れ方
セッション設定の既定値を session.use_strict_mode=1、session.cookie_httponly=1、session.cookie_samesite=Lax に変更即時。JavaScriptからセッションIDを取得できなくなる。またSameSite=LaxによりクロスサイトPOST時にCookieが送信されなくなるため、外部連携のPOST遷移などでセッションが引き継がれなくなる場合がある。php.ini で明示設定していれば影響しない
trim()、ltrim()、rtrim() が既定でフォームフィード(\f)も取り除くようになった即時、エラーなし
file_exists()、is_file()、filesize() など18のファイル関数が、ヌルバイト(NUL文字)を含むパスで ValueError を投げるようになった即時。これまでは false が返っていた
setlocale() が配列を第1引数でしか受け付けなくなった即時。2番目以降に配列を渡すと TypeError
bindec()、hexdec()、octdec()、base_convert() が、正確に変換できない大きな数で通知を出すようになった通知。なおPHP 7.4からは基数に合わない文字(hexdec('z') など)で非推奨通知を出しており、8.7向けに例外を投げるRFCが進行している
PDOとmysqlnd(MySQLネイティブドライバ)でMySQLの永続接続を使い回すとき、COM_RESET_CONNECTION で接続状態を初期化するようになった即時。MySQL 5.7.3未満、MariaDB 10.2.4未満では永続接続が使えない。リクエスト間で接続状態が引き継がれる前提のコードは動作が変わる

おまけ:PHP 6が欠番になった経緯

PHP 5の次がPHP 7になっているが、PHP 6というバージョンは正式にはリリースされていない。

PHP 6は言語内部でUnicodeをネイティブサポートすることを主な目的として、2005年に開発が始まった。しかし内部文字列をUTF-16化することによるメモリ消費の増大や処理速度の低下、既存拡張モジュール移行の難航により、2010年に開発中止となった。Unicode対応以外で開発されていた機能(名前空間や遅延静的束縛など)の多くは、PHP 5.3と5.4に取り込まれた。

開発中にはカンファレンスで機能紹介が行われ、「PHP 6対応」と銘打った書籍も出版されていた。そのため2014年に次のメジャーバージョン名を決める投票が行われた際、旧PHP 6の混乱を避ける理由からPHP 7が支持され、PHP 7が58票、PHP 6が24票でPHP 7に決まった。

出典