Я сильно упростил свой код, но я делаю что-то вроде этого:
class App{
protected $apps = [];
public function __construct($name, $dependencies){
$this->name = $name;
$apps = [];
foreach($dependencies as $dependName){
$apps[$name] = $dependName($this); // returns an instance of App
}
$this->apps = $apps;
}
public function __destruct(){
foreach($this->apps as $dep){
$result = $dep->cleanup($this);
}
}
public function __call($name, $arguments){
if(is_callable([$this, $name])){
return call_user_func_array([$this, $name], $arguments);
}
}
}
function PredefinedApp(){
$app = new App('PredefinedApp', []);
$app->cleanup = function($parent){
// Do some stuff
};
return $app;
}
Затем я создаю приложение, как это:
$app = new App('App1', ['PredefinedApp']);
Это создает App
экземпляр, а затем элементы в массиве создают новые экземпляры приложения, что бы ни было определено, и помещают их во внутренний массив приложения.
Когда я запускаю свой деструктор в главном приложении, оно должно вызвать cleanup()
на всех дочерних приложениях. Но что происходит, так это то, что он выполняет бесконечный цикл, и я не уверен, почему.
Я замечаю, что если я закомментирую call_user_func_array
затем __call()
вызывается только один раз, но не выполняет closure
затем.
Я также заметил, что если я сделаю var_dump()
в __call()
это сваливается бесконечно. Если я сделаю var_dump()
в cleanup()
вместо этого я получаю http 502
ошибка.
Итак, давайте пройдемся по коду и посмотрим, что здесь происходит и почему:
01| class App{
02|
03| protected $apps = [];
04|
05| public function __construct($name, $dependencies){
06| $this->name = $name;
07|
08| $apps = [];
09| foreach($dependencies as $dependName){
10| $apps[$name] = $dependName($this);
11| }
12| $this->apps = $apps;
13| }
14|
15| public function __destruct(){
16| foreach($this->apps as $dep){
17| $result = $dep->cleanup($this);
18| }
19| }
20|
21| public function __call($name, $arguments){
22| if(is_callable([$this, $name])){
23| return call_user_func_array([$this, $name], $arguments);
24| }
25| }
26| }
27|
28| function PredefinedApp(){
29| $app = new App('PredefinedApp', []);
30|
31| $app->cleanup = function($parent){
32| //Do stuff like: echo "I'm saved";
33| };
34| return $app;
35| }
36|
37| $app = new App('App1', ['PredefinedApp']);
Замена вашего __call()
Функция со следующим предотвратит рекурсию, которую вы видите:
public function __call( $method, $arguments ) {
if ( $this->{$method} instanceof Closure ) {
return call_user_func_array( $this->{$method}, $arguments );
} else {
throw new Exception( "Invalid Function" );
}
}
Смотрите ответ @ Rizier123 для более подробной информации, но версия TLDR:
Проблема в том, что вы изначально вызываете cleanup()
как вызов метода с $dep->cleanup()
который вызывает __call()
, Если вы тогда используете call_user_func_array()
и передать [$this, $method]
он вызывает его как метод, вызывая _call()
снова и запуск цикла (и никогда на самом деле вызывая cleanup()
метод), тогда как при использовании $this->{$method}
будет вызывать его как Closure
и предотвратить зацикливание.