"즉시"개체에 새 메서드를 추가하려면 어떻게합니까?
$me= new stdClass;
$me->doSomething=function ()
{
echo 'I\'ve done something';
};
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
"즉시"개체에 새 메서드를 추가하려면 어떻게합니까?
$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();
array_unshift($args, $this);
메소드 호출 전에 수행 하여 함수가 명시 적으로 바인딩 할 필요없이 객체에 대한 참조를 얻도록합니다 ...
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();
특정 디자인 패턴의 구현을 통해서만 달성 할 수 있음을 명확히 하는 유사한 게시물이 stackoverflow 에 있습니다.
유일한 다른 방법은 실험적인 PHP 확장 인 classkit을 사용하는 것입니다 . (또한 게시물)
예, 정의 된 후에 PHP 클래스에 메소드를 추가 할 수 있습니다. "실험용"확장 인 classkit을 사용하려고합니다. 그러나이 확장은 기본적으로 활성화되지 않은 것으로 보이므로 Windows에서 사용자 지정 PHP 바이너리를 컴파일하거나 PHP DLL을로드 할 수 있는지 여부에 따라 다릅니다 (예 : Dreamhost는 사용자 지정 PHP 바이너리를 허용하며 설정이 매우 쉽습니다. ).
karim79 답변은 작동하지만 메서드 속성 내부에 익명 함수를 저장하며 그의 선언은 동일한 이름의 기존 속성을 덮어 쓸 수 있거나 기존 속성이 private
치명적인 오류 개체를 사용할 수없는 경우 작동하지 않습니다 . 별도의 배열에 저장하고 setter를 사용하는 것이 더 깨끗한 솔루션이라고 생각합니다. 방법 인젝터 세터가 자동으로 사용하는 모든 객체에 추가 할 수있는 특성을 .
추신 물론 이것은 SOLID의 개방 폐쇄 원칙에 위배되므로 사용해서는 안되는 해킹입니다.
class MyClass {
//create array to store all injected methods
private $anon_methods = array();
//create setter to fill array with injected methods on runtime
public function inject_method($name, $method) {
$this->anon_methods[$name] = $method;
}
//runs when calling non existent or private methods from object instance
public function __call($name, $args) {
if ( key_exists($name, $this->anon_methods) ) {
call_user_func_array($this->anon_methods[$name], $args);
}
}
}
$MyClass = new MyClass;
//method one
$print_txt = function ($text) {
echo $text . PHP_EOL;
};
$MyClass->inject_method("print_txt", $print_txt);
//method
$add_numbers = function ($n, $e) {
echo "$n + $e = " . ($n + $e);
};
$MyClass->inject_method("add_numbers", $add_numbers);
//Use injected methods
$MyClass->print_txt("Hello World");
$MyClass->add_numbers(5, 10);