객체 속성에 직접 할당 된 클로저 호출


109

클로저를 변수에 다시 할당 한 다음 호출하지 않고 객체의 속성에 직접 할당 한 클로저를 호출 할 수 있기를 원합니다. 이게 가능해?

아래 코드는 작동하지 않으며 Fatal error: Call to undefined method stdClass::callback().

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback();

1
이것이 바로 당신이 필요로하는 것입니다 : github.com/ptrofimov/jslikeobject 더 많은 것 : $ this를 클로저 내부에 사용하고 상속을 사용할 수 있습니다. PHP> = 5.4 만!
Renato Cuccinotto 2013 년

답변:


106

PHP7부터 다음을 수행 할 수 있습니다.

$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');

또는 Closure :: call () 을 사용하지만 StdClass.


PHP7 이전 __call에는 호출을 가로 채서 콜백을 호출 하는 매직 메서드 를 구현해야했습니다 ( StdClass물론 __call메서드를 추가 할 수 없기 때문에 가능하지 않습니다 ).

class Foo
{
    public function __call($method, $args)
    {
        if(is_callable(array($this, $method))) {
            return call_user_func_array($this->$method, $args);
        }
        // else throw exception
    }
}

$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');

당신은 할 수 없습니다

return call_user_func_array(array($this, $method), $args);

에서 __call몸이 트리거 때문에 __call무한 루프.


2
때때로이 구문이 유용하다는 것을 알게 될 것입니다.-call_user_func_array ($ this-> $ property, $ args); 메서드가 아닌 호출 가능한 클래스 속성에 관한 것입니다.
니키타 Gopkalo

104

클로저에서 __invoke를 호출하여이를 수행 할 수 있습니다. 이는 객체가 함수처럼 동작하는 데 사용하는 마법 메서드이기 때문입니다.

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback->__invoke();

물론 콜백이 배열이나 문자열 (PHP에서도 유효한 콜백 일 수 있음) 인 경우에는 작동하지 않습니다. 클로저 및 __invoke 동작이있는 다른 객체에만 해당됩니다.


3
@marcioAlmada 매우 못생긴.
Mahn 2014-07-06

1
@Mahn 받아 들인 대답보다 더 명시 적이라고 생각합니다. 이 경우 명시적인 것이 더 좋습니다. "귀여운"솔루션을 정말로 염려한다면 call_user_func($obj->callback)그렇게 나쁘지 않습니다.
marcio

그러나 call_user_func너무 문자열와 함께 작동하고 항상 convinient 아니다
Gherman

2
@cerebriform 의미 론적으로 $obj->callback->__invoke();예상 할 때 수행해야하는 것은 의미가 없습니다 $obj->callback(). 일관성의 문제 일뿐입니다.
Mahn

3
@Mahn : 맞습니다. 일관성이 없습니다. 하지만 일관성이 실제로 PHP의 강력한 제품은 아닙니다. :) 그러나 나는 그것이 사물의 본질을 고려할 때 의미가 있다고 생각합니다 $obj->callback instanceof \Closure.
bishop

24

현재 PHP 7 다음을 수행 할 수 있습니다 :

($obj->callback)();

그런 상식이지만 순전히 나쁜 것입니다. PHP7의 강력한 힘!
tfont

10

PHP 7 부터는 다음 call()메소드를 사용하여 클로저를 호출 할 수 있습니다 .

$obj->callback->call($obj);

PHP 7 은 임의의 (...)표현식에 대해서도 연산을 실행할 수 있기 때문에 ( Korikulum에서 설명한 대로 ) :

($obj->callback)();

다른 일반적인 PHP 5 접근 방식은 다음과 같습니다.

  • 매직 방법 사용 __invoke()( Brilliand 에서 설명 )

    $obj->callback->__invoke();
  • 은 USING call_user_func()함수

    call_user_func($obj->callback);
  • 표현식에서 중간 변수 사용

    ($_ = $obj->callback) && $_();

각 방식마다 장단점이 있지만 가장 급진적이고 확실한 솔루션은 여전히 Gordon이 제시 한 솔루션 입니다.

class stdKlass
{
    public function __call($method, $arguments)
    {
        // is_callable([$this, $method])
        //   returns always true when __call() is defined.

        // is_callable($this->$method)
        //   triggers a "PHP Notice: Undefined property" in case of missing property.

        if (isset($this->$method) && is_callable($this->$method)) {
            return call_user_func($this->$method, ...$arguments);
        }

        // throw exception
    }
}

$obj = new stdKlass();
$obj->callback = function() { print "HelloWorld!"; };
$obj->callback();

7

을 사용하여 가능한 것 같습니다 call_user_func().

call_user_func($obj->callback);

우아하지는 않지만 .... @Gordon이 말하는 것은 아마도 유일한 길일 것입니다.


7

글쎄, 당신이 정말로 주장 한다면 . 또 다른 해결 방법은 다음과 같습니다.

$obj = new ArrayObject(array(),2);

$obj->callback = function() {
    print "HelloWorld!";
};

$obj['callback']();

그러나 그것은 가장 좋은 구문이 아닙니다.

그러나, PHP 파서는 항상 취급 T_OBJECT_OPERATOR, IDENTIFIER, (메서드 호출한다. ->메서드 테이블 을 우회하고 대신 속성에 액세스 할 수있는 해결 방법이없는 것 같습니다 .


7

나는 이것이 오래되었다는 것을 알고 있지만 PHP 5.4 이상을 사용하는 경우 Traits 가이 문제를 잘 처리한다고 생각합니다.

먼저 속성을 호출 가능하게 만드는 특성을 만듭니다.

trait CallableProperty {
    public function __call($method, $args) {
        if (property_exists($this, $method) && is_callable($this->$method)) {
            return call_user_func_array($this->$method, $args);
        }
    }
}

그런 다음 클래스에서 해당 특성을 사용할 수 있습니다.

class CallableStdClass extends stdClass {
    use CallableProperty;
}

이제 익명 함수를 통해 속성을 정의하고 직접 호출 할 수 있습니다.

$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4

와우 :) 제가하려고했던 것보다 훨씬 더 우아합니다. 내 유일한 질문은 영국식 핫 / 콜드 탭 커넥터 요소와 동일합니다. 왜 이미 내장되어 있지 않습니까 ??
dkellner 2015

감사 :). 그것이 만드는 모호성 때문에 아마도 내장되어 있지 않을 것입니다. 내 예에서 실제로 "add"라는 함수와 "add"라는 속성이 있다고 상상해보십시오. 괄호가 있으면 해당 이름을 가진 함수를 찾도록 PHP에 지시합니다.
SteveK 2015

2

글쎄, 변수에 클로저를 저장하고 변수를 호출하는 것이 실제로 (비밀하게) 더 빠르다는 것을 강조해야합니다. 호출 량에 따라 상당히 많이됩니다. 1,5 (인자, __invoke를 직접 호출하는 대신 변수를 사용하여. 대신 변수에 클로저를 저장하고 호출하십시오.


2

업데이트 :

$obj = new stdClass();
$obj->callback = function() {
     print "HelloWorld!";
};

PHP> = 7 :

($obj->callback)();

PHP> = 5.4 :

$callback = $obj->callback;  
$callback();

이거 해봤 어? 작동하지 않습니다. Call to undefined method AnyObject::callback()(물론 AnyObject 클래스가 존재합니다.)
Kontrollfreak

1

다음은 수락 된 답변을 기반으로하지만 stdClass를 직접 확장하는 또 다른 대안입니다.

class stdClassExt extends stdClass {
    public function __call($method, $args)
    {
        if (isset($this->$method)) {
            $func = $this->$method;
            return call_user_func_array($func, $args);
        }
    }
}

사용 예 :

$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();

call_user_func또는 __invoke그래도 사용하는 것이 좋습니다 .


0

PHP 5.4 이상을 사용하는 경우 콜 러블을 개체 범위에 바인딩하여 사용자 지정 동작을 호출 할 수 있습니다. 예를 들어 다음과 같은 설정을했다면 ..

function run_method($object, Closure $method)
{
    $prop = uniqid();
    $object->$prop = \Closure::bind($method, $object, $object);
    $object->$prop->__invoke();
    unset($object->$prop);
}

그리고 당신은 그렇게 수업을하고 있었어요 ..

class Foo
{
    private $value;
    public function getValue()
    {
        return $this->value;
    }
}

객체의 범위 내에서 작동하는 것처럼 자신의 논리를 실행할 수 있습니다.

$foo = new Foo();
run_method($foo, function(){
    $this->value = 'something else';
});

echo $foo->getValue(); // prints "something else"

0

이것은 PHP5.5에서 작동합니다.

$a = array();
$a['callback'] = function() {
    print "HelloWorld!";
};
$a['callback']();

클로저의 가상 객체 컬렉션을 만들 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.