[php] 즉석에서 PHP 개체에 새 메서드를 추가하는 방법은 무엇입니까?

“즉시”개체에 새 메서드를 추가하려면 어떻게합니까?

$me= new stdClass;
$me->doSomething=function ()
 {
    echo 'I\'ve done something';
 };
$me->doSomething();

//Fatal error: Call to undefined method stdClass::doSomething()



답변

__call이를 위해 활용할 수 있습니다 .

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

$foo = new Foo();
$foo->bar = function () { echo "Hello, this function is added at runtime"; };
$foo->bar();


답변

PHP 7을 사용하면 익명 클래스를 사용할 수 있습니다.

$myObject = new class {
    public function myFunction(){}
};

$myObject->myFunction();

PHP RFC : 익명 클래스


답변

런타임에 새 메서드를 추가 할 수 있도록 단순히 __call을 사용하면 해당 메서드가 $ this 인스턴스 참조를 사용할 수 없다는 주요 단점이 있습니다. 추가 된 메서드가 코드에서 $ this를 사용하지 않을 때까지 모든 것이 잘 작동합니다.

class AnObj extends stdClass
{
    public function __call($closure, $args)
    {
        return call_user_func_array($this->{$closure}, $args);
    }
 }
 $a=new AnObj();
 $a->color = "red";
 $a->sayhello = function(){ echo "hello!";};
 $a->printmycolor = function(){ echo $this->color;};
 $a->sayhello();//output: "hello!"
 $a->printmycolor();//ERROR: Undefined variable $this

이 문제를 해결하기 위해 다음과 같이 패턴을 다시 작성할 수 있습니다.

class AnObj extends stdClass
{
    public function __call($closure, $args)
    {
        return call_user_func_array($this->{$closure}->bindTo($this),$args);
    }

    public function __toString()
    {
        return call_user_func($this->{"__toString"}->bindTo($this));
    }
}

이런 식으로 인스턴스 참조를 사용할 수있는 새 메서드를 추가 할 수 있습니다.

$a=new AnObj();
$a->color="red";
$a->sayhello = function(){ echo "hello!";};
$a->printmycolor = function(){ echo $this->color;};
$a->sayhello();//output: "hello!"
$a->printmycolor();//output: "red"


답변

업데이트 : 여기에 표시된 접근 방식에는 큰 단점이 있습니다. 새 함수는 클래스의 정규화 된 멤버가 아닙니다. $this이 방식으로 호출 될 때 메소드에 존재하지 않습니다. 즉, 개체 인스턴스의 데이터 또는 함수로 작업하려면 개체를 매개 변수로 함수에 전달해야합니다! 또한 이러한 함수에서 클래스 private또는 protected클래스 멤버에 액세스 할 수 없습니다 .

새로운 익명 함수를 사용하는 좋은 질문과 영리한 아이디어!

흥미롭게도 이것은 작동합니다.

$me->doSomething();    // Doesn't work

함수 자체에 대한 call_user_func 의해 :

call_user_func($me->doSomething);    // Works!

작동하지 않는 것은 “올바른”방법입니다.

call_user_func(array($me, "doSomething"));   // Doesn't work

그런 식으로 호출되면 PHP는 클래스 정의에서 선언 할 메서드를 요구합니다.

이것은인가 private/ public/ protected가시성 문제?

업데이트 : 아니요. 클래스 내에서도 정상적인 방식으로 함수를 호출하는 것은 불가능하므로 가시성 문제가 아닙니다. 실제 함수를 전달하는 것이이 call_user_func()작업을 수행 할 수있는 유일한 방법입니다.


답변

함수를 배열에 저장할 수도 있습니다.

<?php
class Foo
{
    private $arrayFuncs=array();// array of functions
    //
    public function addFunc($name,$function){
        $this->arrayFuncs[$name] = $function;
    }
    //
    public function callFunc($namefunc,$params=false){
        if(!isset($this->arrayFuncs[$namefunc])){
            return 'no function exist';
        }
        if(is_callable($this->arrayFuncs[$namefunc])){
            return call_user_func($this->arrayFuncs[$namefunc],$params);
        }
    }

}
$foo = new Foo();

//Save function on array variable with params
$foo->addFunc('my_function_call',function($params){
    return array('data1'=>$params['num1'],'data2'=>'qwerty','action'=>'add');
});
//Save function on array variable
$foo->addFunc('my_function_call2',function(){
    return 'a simple function';
});
//call func 1
$data = $foo->callFunc('my_function_call',array('num1'=>1224343545));
var_dump($data);
//call func 2
$data = $foo->callFunc('my_function_call2');
var_dump($data);
?>


답변

eval을 사용하여이 작업을 수행하는 방법을 보려면 github에서 사용할 수있는 PHP 마이크로 프레임 워크 인 Halcyon을 살펴보십시오 . 문제없이 알아낼 수있을만큼 충분히 작습니다. HalcyonClassMunger 클래스에 집중하세요.


답변

__call솔루션이 없으면 bindTo(PHP> = 5.4)를 사용 $this하여 다음 $me과 같이 bound to 메서드를 호출 할 수 있습니다 .

call_user_func($me->doSomething->bindTo($me, null)); 

전체 스크립트는 다음과 같습니다.

$me = new stdClass;
// Property for proving that the method has access to the above object:
$me->message = "I\'ve done something";
$me->doSomething = function () {
    echo $this->message;
};
call_user_func($me->doSomething->bindTo($me)); // "I've done something"

또는 바인딩 된 함수를 변수에 할당 한 다음 다음없이 호출 할 수 있습니다 call_user_func.

$f = $me->doSomething->bindTo($me);
$f();