関数の戻り値と定数値(リテラル)への参照

Computer, Development 7月 13, 2005 #PHP
(Last Updated On: 2018年8月13日)

追記:このエントリへのアクセスが多いので加筆修正しました。

Fatal error: Only variables can be passed by reference

直訳すると「致命的エラー:変数のみ参照渡しが可能です」となります。エラーメッセージの通りvariable(以外)の値は参照として渡せないのでエラーになっています。エラーメッセージが適切かどうかは微妙ですが、意訳すると「致命的エラー:ソースコード中に記述した定数値(リテラル)へのアクセスはできません」あたりが妥当と思います。当然ですがdefineで定義した定数値を返す事は可能です。PHP内部では定数は変更できない「変数」の様に実装されているからです。このエントリの「定数値」を正確に書くと「ソースコード中に記述された定数値」となります。

PHP 4.4, PHP 5.1では定数値への参照の仕様が変更されています。ここで言う定数値とはdefineで定義された定数とは違い、コンパイル時にバイトコード中の定数値となる値(リテラル)を指します。PHPスクリプトから定義する定数は変更不可能な変数のように実装されており、これらの定数値と全く異なります。

PHPのバグレポート33643から抜粋

<?php

class Foo {
    public static function bar() {
        return array('a' => '1234567890');
    }
}

$tmp = Foo::bar();
$var = array_pop($tmp);

?>

Seems to work, but it shouldn’t be down to the PHP user (imho) to
implement this as this change in PHP 5.1 breaks a lot of existing code.

Reproduce code:

<?php

class Foo {
    public static function bar() {
        return array('a' => '1234567890');
    }
}

$var = array_pop(Foo::bar());

?>

Expected result:

$var = '1234567890';
Foo::bar() gets placed in temporary variable

Actual result:

Fatal error: Only variables can be passed by reference

(引用終わり)

つまり、array()で定義した配列定数をreturn文で返し、戻り値を変数に格納せず直接使用すると定数値にアクセスしたこととなり致命的エラー(E_ERROR)が発生しました。

現在のPHPではE_ERRORはエラーは発生しません。PHP 5.1.0以降はソースコードに埋め込まれた値を次のように返すとE_STRICTエラーが発生します。

各PHPバージョンの動作: http://3v4l.org/8fISj

HHVMではリテラルの扱いが異るのでエラーになりません。しかし、現在のPHPでも引数が参照の場合(array_spliceの第一引数は参照)は Fatal error: Only variables can be passed by reference が発生します。

<?php
var_dump(array_splice(array("red", "green", "blue", "yellow"), 2));

各PHPバージョンの動作:http://3v4l.org/t79rF

古いPHPでは戻り値がリテラルだとZendエンジンの仕様上、修正が困難でした。現在のPHPでは、Zendエンジンのバージョンも上がりこのようなコードも処理できるようになっています。E_STRICTエラーが発生するのはやり過ぎだと思うので変更提案をしてみます。

現状:http://3v4l.org/d51DQ

現在のPHPでもキャストしたり、配列に変換するとFatal error(E_ERROR)が発生します。

Fatal errorの方は修正/仕様変更が難しいと思います。関数からの戻り値でE_NOTICEエラーになる仕様の変更は、PHP7ならいけるかもしれません。

 

参考:

投稿者: yohgaki