52

別の質問で、PHP 関数呼び出しの結果を括弧で囲むと、次のように結果を本格的な式に変換できることが指摘されました。

<?php
error_reporting(E_ALL | E_STRICT);

function get_array() {
   return array();
}

function foo() {
   // return reset(get_array());
   //              ^ error: "Only variables should be passed by reference"

   return reset((get_array()));
   //           ^ OK
}

foo();

ここで何が起こっているのかを明確かつ明確に説明するために、ドキュメントで何かを見つけようとしています。C++ とは異なり、私は PHP の文法とそのステートメント/式の扱いについて、自分で派生させるのに十分な知識がありません。

この動作に関するドキュメントに隠されているものはありますか? そうでない場合、他の誰かが推測に頼らずに説明できますか?


アップデート

私は最初、この EBNFが PHP の文法を表していると主張していることに気づき、スクリプトを自分でデコードしようとしましたが、結局あきらめました。

次に、を使用phcして 2 つのバリアントの.dotファイルを生成し、次のコマンドを使用して両方のスクリプトの AST イメージを生成しました。foo()

$ yum install phc graphviz
$ phc --dump-ast-dot test1.php > test1.dot
$ dot -Tpng test1.dot > test1.png
$ phc --dump-ast-dot test2.php > test2.dot
$ dot -Tpng test2.dot > test2.png

どちらの場合も、結果はまったく同じでした。

スニペット 1 と 2 の構文木

4

2 に答える 2

32

この動作はバグに分類される可能性があるため、絶対に依存しないでください。

関数呼び出しでメッセージがスローされない(簡略化された) 条件は次のとおりです ( opcode の定義をZEND_SEND_VAR_NO_REF参照してください)。

  • 引数が関数呼び出しではない (または関数呼び出しである場合は、参照によって返される)、および
  • 引数が参照であるか、参照カウントが 1 の場合 (参照カウントが 1 の場合は参照になります)。

これらをさらに詳しく分析してみましょう。

最初のポイントは真です (関数呼び出しではありません)

追加の括弧により、PHP は引数が関数呼び出しであることを検出しなくなりました。

空でない関数の引数リストを解析する場合、PHP には 3 つの可能性があります。

  • アンexpr_without_variable
  • variable
  • (削除されたコールタイム パス バイ リファレンス機能の場合は、&その後に , が続きます)variable

get_array()PHPだけを記述する場合、これはvariable.

(get_array())一方、 としての資格はありませんvariable。ですexpr_without_variable

これは最終的にコードのコンパイル方法に影響します。つまり、オペコードの拡張値にSEND_VAR_NO_REFは flag が含まれなくなりますZEND_ARG_SEND_FUNCTION。これは、オペコードの実装で関数呼び出しが検出される方法です。

2 番目のポイントは true (参照カウントは 1)

いくつかの点で、Zend Engine は、参照が予期される場所で参照カウント 1 の非参照を許可します。これらの詳細はユーザーに公開されるべきではありませんが、残念ながらここにあります。

あなたの例では、他のどこからも参照されていない配列を返しています。そうであったとしても、メッセージは表示されます。つまり、この 2 番目の点は当てはまりません。

したがって、次の非常によく似た例は機能しません

<?php

$a = array();
function get_array() {
   return $GLOBALS['a'];
}

return reset((get_array()));
于 2011-07-18T12:30:40.503 に答える
1

A)ここで何が起こっているのかを理解するには、PHP の値/変数と参照の扱いを理解する必要があります(PDF、1.2MB)。ドキュメント全体で述べられているように、「参照はポインターではありません」。また、関数からの参照によってのみ変数を返すことができます。それ以外は何もありません。

私の意見では、つまり、PHP のすべての関数は参照を返します。ただし、一部の関数 (PHP に組み込まれている) は、引数として値/変数を必要とします。ここで、関数呼び出しをネストしている場合、内側のものは参照を返し、外側のものは値を期待します。これは、「有名な」 E_STRICT-error "Only variables should be passed by reference" につながります。

$fileName = 'example.txt';
$fileExtension = array_pop(explode('.', $fileName));
// will result in Error 2048: Only variables should be passed by reference in…

B)質問にリンクされている PHP 構文の説明の行を見つけました。

expr_without_variable = "(" expr ")"

ドキュメントの次の文と組み合わせると、「PHP では、ほとんどすべてのものが式になります。式を定義する最も簡単で最も正確な方法は、「値を持つもの」です」という結論に至ります。(5)値 5 の整数に評価される PHP の式です。

(As$a = 5は代入だけでなく、5 に評価される式でもあります。)

結論

expression への参照を渡すと(...)、この式は値を返し、それを引数として外部関数に渡すことができます。それ(私の考え)が本当なら、次の2行は同等に機能するはずです:

// what I've used over years: (spaces only added for readability)
$fileExtension = array_pop( ( explode('.', $fileName) ) );
// vs
$fileExtension = array_pop( $tmp = explode('.', $fileName) );

PHP 5.0.5も参照してください: 致命的なエラー: 参照によって渡すことができるのは変数のみです。2005.09.13

于 2011-07-17T22:43:43.103 に答える