PHP 7.2.0から未定義定数を使用した配列アクセス ($foo[bar]) がE_WARNINGを出力

導入

PHPで配列要素へのアクセスとして,以下の記法が存在していた。

$foo[bar]

しかし,この記法がどうやらPHPのバージョンによって,エラー出力レベルが変わるようで,新しいPHPだと問題になるようだ。

気になったので,この記法について調べた。

原因

この$foo[bar]の記法については,PHP公式マニュアルで解説されている。

PHPの変数は$の前置が必須であり,リテラル値以外で$が前置されていない場合,定数か関数とみなされる。そのため,$foo[bar]barは丸括弧が欠落しているため,定数とみなされる。

ここで,barが未定義の場合,配列の添字が空となり,エラーが予想される。しかし,PHPでは未定義の定数は定数名の文字列 ('bar')とみなされる。

If you use an undefined constant, PHP assumes that you mean the name of the constant itself, just as if you called it as a string (CONSTANT vs “CONSTANT”). This fallback is deprecated as of PHP 7.2.0, and an error of level E_WARNING is issued when it happens (previously, an error of level E_NOTICE has been issued instead.) See also the manual entry on why $foo[bar] is wrong (unless you first define() bar as a constant). This does not apply to (fully) qualified constants, which will raise a fatal error if undefined. If you simply want to check if a constant is set, use the defined() function.

PHP: Syntax – Manual

そのため,$foo[bar]の記法が成立していた。ただし,$foo[bar]で排列要素にアクセスする場合,PHP 7.2.0未満まではE_NOTICEレベルのエラーが出力されていた。

これが,PHP 7.2.0からエラーレベルがE_WARNINGに格上げとなった。このことは,上記の他にPHPの7.2での変更点としても明記されている。

Unquoted strings

Unquoted strings that are non-existent global constants are taken to be strings of themselves. This behaviour used to emit an E_NOTICE, but will now emit an E_WARNING. In the next major version of PHP, an Error exception will be thrown instead.
Unqouted strings – PHP: Deprecated features in PHP 7.2.x – Manual

PHP 7.2.0以上でこのエラーを抑制したい場合,以下のように既定のエラーレベルに加えて,E_WARNINGのエラーをphp.iniのerror_reportingプロパティやerror_reporting関数で設定すればいい。

<?php
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED & ~E_WARNING);
$foo = array("bar" => "bar"); echo $foo[bar] . "\n";

ただし,前述のPHP 7.2.0での変更点の文書で,PHP 8.0.0からはエラー例外を出すことが予告されている。

したがって,未定義定数を使った$foo[bar]の記法はやめ,素直にリテラル文字列を使った$foo['bar']$foo["bar"]の記法を採用すべきだろう。

再現

この問題を再現・検証するコードを以下に掲載する。GitHubにも掲載している。

<?php
## \file array-access-with-unquoted-strings.php
## \author SENOO, Ken
## \copyright CC0
## \date Created date: 2019-07-13
## \sa https://senooken.jp/post/2019/07/13/
## \brief `$foo[bar]` の記法によるエラー出力の違いを記す。
##
## `$foo[bar]` の記法 (未定義定数barの使用) はPHP 7.2.0未満では,E_NOTICEのエラーが出力され,PHP 7.2.0以上ではE_WARNINGのエラーが出力される。
##
## E_WARNINGは既定のエラーレベルに存在するため,今まで問題なかったPHPコードが,PHPのバージョンアップにより,問題を出すようになった。

$foo = array("bar" => "bar");
echo $foo["bar"] . "\n";

echo $foo[bar] . "\n";
## PHP 7.2.0以上の場合,既定のエラーレベルに存在するE_WARNINGにより,ここで以下のエラーが出力される。
##
## ```
## Warning: Use of undefined constant bar - assumed 'bar' in /home/senooken/project/example/PHP/array-access-without-quotation.php on line 16
## ```

error_reporting(E_NOTICE);
echo $foo[bar] . "\n";
## PHP 7.2.0未満の場合,E_NOTICEの有効により,ここで以下のエラーが出力される。
##
## ```
## Notice: Use of undefined constant bar - assumed 'bar' in /home/senooken/project/example/PHP/array-access-without-quotation.php on line 24
## ```

## 以下のようにE_WARNINGをオフにすれば,PHP 7.2.0以上で `$foo[bar]` によるエラー出力を抑制できる。
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED & ~E_WARNING);
echo $foo[bar] . "\n";

## ただし,PHP 8.0.0以上では `$foo[bar]` の記法はエラー扱いになることが予告されているので,この対応は悪い。
##
## 素直に,`$foo[bar]` を `$foo['bar']` のように,配列添字を一重引用符または二重引用符で囲んだリテラル値にすべきだろう。
結論

未定義定数を使用した配列要素へのアクセス ($foo[bar]) によるエラー出力の違いを検証した。

PHPは勉強を初めたばかりで,今回のような挙動がなぜ発生するのか,知らなかった。疑問に思い調べたことで,PHPの未定義定数の使用という原因がわかった。

疑問を解消できて勉強になった。PHPはいろんなところで使われているので,バージョン間の違いが問題になりやすい。今後も注意したい。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です