web-dev-qa-db-ja.com

クラス変数であるクロージャーを呼び出す方法は?

class MyClass {
  var $lambda;
  function __construct() {
    $this->lambda = function() {echo 'hello world';};
    // no errors here, so I assume that this is legal
  }
}

$myInstance = new MyClass();
$myInstance->lambda();
//Fatal error: Call to undefined method MyClass::lambda()

それでは、クラス変数に到達するための正しい構文は何ですか?

74
rsk82

PHPでは、メソッドとプロパティは別のネームスペースにあり(同じ名前のメソッドとプロパティを持つことができます)、プロパティまたはメソッドにアクセスするかどうかは、使用する構文によって異なります。

$expr->something()はメソッド呼び出しであるため、PHPはクラスのメソッドのリストでsomethingを検索します。

_$expr->something_はプロパティフェッチであるため、PHPはクラスのプロパティリストでsomethingを検索します。

$myInstance->lambda();はメソッド呼び出しとして解析されるため、PHPはクラスでlambdaという名前のメソッドを検索しますが、そのようなメソッドはありません(したがって- 未定義のメソッドの呼び出しエラー)。

そのため、fetch property構文を使用してラムダをフェッチしてから呼び出す必要があります。

  • PHP 7.0なので、これは_($obj->lambda)()_でできます:

    _($obj->lambda)();
    _

    括弧は、PHPが_($obj->lambda)_をlambdaという名前のプロパティを取得するとして解析することを確認します。その後、_()_はプロパティの取得結果を呼び出します。

  • または、->lambda->__invoke()でこれを行うことができます:

    _$myInstance = new MyClass();
    $myInstance->lambda->__invoke();
    _

    ___invoke_PHPの魔法のメソッド の1つです。オブジェクトがこのメソッドを実装すると、呼び出し可能になります。$var()構文を使用して呼び出すことができます。無名関数は Closure のインスタンスで、___invoke_を実装します。

  • または、ローカル変数に割り当てます:

    _$lambda = $myInstance->lambda;
    $lambda();
    _
  • または、call_user_funcを使用して呼び出します。

    _call_user_func($myInstance->lambda);
    _

    _call_user_func_ は、任意の callable (匿名関数を含む)を呼び出すことができます。

  • あるいは、これがコードの一般的なパターンである場合は、___call_メソッドを設定して、呼び出しをラムダに転送できます。

    _class MyClass
    {
        private $lambda;
    
        public function __construct()
        {
            $this->lambda = function() {
                echo "Hello world!\n";
            };
        }
    
        public function __call($name, $args)
        {
            return call_user_func_array($this->$name, $args);
        }
    }
    _

    今、これは動作します:

    _$myInstance = new MyClass();
    $myInstance->lambda();
    _

    PHP 5.4なので、トレイトでそれを行うこともできます。

    _trait LambdasAsMethods
    {
        public function __call($name, $args)
        {
            return call_user_func_array($this->$name, $args);
        }
    }
    
    class MyClass
    {
        use LambdasAsMethods;
    
        private $lambda;
    
        public function __construct()
        {
            $this->lambda = function() {
                echo "Hello World!\n";
            };
        }
    }
    
    $myInstance = new MyClass();
    $myInstance->lambda();
    _
185
Arnaud Le Blanc

ReflectionFunctionを使用して、クラスを変更せずにラムダ関数を呼び出すこともできます。

$myInstance = new MyClass();
$lambda = new ReflectionFunction($myInstance->lambda);
$lambda->invoke();

または引数を渡す必要がある場合

$args = array('arg'=>'value');
$lambda->invokeArgs($args);
2
akDeveloper