[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();
답변
런타임에 새 메서드를 추가 할 수 있도록 단순히 __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();