はじめに
PHPはバージョンごとに仕様やエラーハンドリングの挙動が変わることがあります。
特に可変変数(変数変数)の扱いは、PHP7以降で改善されている部分があり、古いバージョンで動いていたコードがPHP8でエラーになったり、逆にPHP5.6で動かなくなったりするケースがあります。
今回は「可変変数を使った配列アクセス」の具体例を挙げ、PHP8とPHP5.6で挙動が異なる理由を解説します。
目次
問題のコード
<?php
// declare(strict_types=1); // PHP5系は対応していないのでコメントアウト推奨
error_reporting(-1);
$test = 'hello';
$s = 'test';
// 可変変数
echo $$s . '<br>'; // $test と展開され 'hello' が表示される
echo ${$s} . '<br>'; // 上と同じく 'hello' が表示される(こちらの書き方が推奨)
$awk['bar'] = 'hoge';
$foo = 'awk';
// 可変変数配列アクセス
var_dump($$foo['bar']); //hogeが出力される
実行結果の違い
PHPバージョン | 結果・挙動 |
---|---|
PHP8.0 | hello hello string(4) “hoge” |
PHP5.6 | Warning: Unsupported declare ‘strict_types’(※)hellohelloWarning: Illegal string offset ‘bar’Notice: Undefined variable: aNULL |
※PHP5.6はdeclare(strict_types=1)
がサポートされていないため警告が出ます。
なぜ結果が違うのか?
declare(strict_types=1);
の警告
- PHP5.6では
strict_types
宣言が存在しないため、実行時に警告が出ます。 - PHP8.0では問題なく動作します。
可変変数の配列アクセス部分の違い
var_dump($$foo['bar']);
この式は解釈が2通り考えられます:
- ($$foo)[‘bar’] と解釈する(PHP8の挙動)
→ $fooの値は’awk’、よって$$fooは$awkとなり、配列$awk[‘bar’]の値 ‘hoge’ が返る。 - $$foo[‘bar’]と文字通り右から左に評価し、まず$foo[‘bar’]を評価しようとする(PHP5.6の挙動)
→ $fooは文字列 ‘awk’ のため、文字列に配列アクセスしようとして警告発生。
→ さらに$$foo[‘bar’]の展開で未定義変数$aを参照しようとしてNotice発生。
PHP5.6の評価順序の問題
PHP5.6では複雑な可変変数+配列アクセスの解釈で曖昧さがあり、配列アクセス部分を先に評価してしまい文字列に対して配列アクセスしようとしてしまいます。
正しい書き方と対策
このような可変変数と配列アクセスを組み合わせる場合は、必ず {}
で明示的に括るべきです。
var_dump( ${$foo}['bar'] );
これにより、$foo
の変数変数を先に評価し、そこから配列の'bar'
キーを取得することが確実になります。PHP5.6でもPHP8でも同じ結果になります。
まとめ
- PHP5.6とPHP8では可変変数+配列アクセスの評価順序が異なるため挙動が変わる
declare(strict_types=1)
はPHP5.6で使わない(コメントアウト推奨)- 複雑な可変変数の操作は必ず
{}
で括って明示的に評価順序を指定する - 上記方法でPHPのバージョン差によるトラブルを回避できる