__destruct () и __call () создают бесконечный цикл

Я сильно упростил свой код, но я делаю что-то вроде этого:

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 ошибка.

4

Решение

Итак, давайте пройдемся по коду и посмотрим, что здесь происходит и почему:

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']);
4

Другие решения

Замена вашего __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 и предотвратить зацикливание.

3

По вопросам рекламы [email protected]