You create an instance of: App
with the following line:
$app = new App('App1', ['PredefinedApp']); //Line: 37
The constructor gets called:
public function __construct($name, $dependencies){ /* Code here */ } //Line: 05
2.1. Following parameters are passed:
$name
= "App1"
$dependencies
= ["PredefinedApp"]
You assign $name
to $this->name
with this line:
$this->name = $name; //Line: 06
You initialize $apps
with an empty array:
$apps = []; //Line: 08
Now you loop through each element of $dependencies
, which has 1 element here (["PredefinedApp"]
)
In the loop you do the following:
6.1 Assign the return value of a function call to an array index:
$apps[$name] = $dependName($this); //Line: 10
//$apps["App1"] = PredefinedApp($this);
You call the function:
PredefinedApp(){ /* Code here */} //Line: 28
Now you create again a new instance of: App
in PredefinedApp()
same as before (Point 2 - 6, expect in the constructor you have other variable values + you don't enter the loop, since the array is empty)
You assign a closure to a class property:
$app->cleanup = function($parent){ //Line: 31
//Do stuff like: echo "I'm saved";
};
You return the new created object of App
:
return $app; //Line: 34
Here already the __destruct()
gets called, because when the function ends the refcount
goes to 0 of that zval
and __destruct()
is triggered. But since $this->apps
is empty nothing happens here.
The new created object in that function gets returned and assigned to the array index (Note: We are back from the function to point 6.1):
$apps[$name] = $dependName($this); //Line: 10
//$apps["App1"] = PredefinedApp($this);
The constructor ends with assigning the local array to the class property:
$this->apps = $apps; //Line: 12
Now the entire script ends (We have done line: 37)! Which means for the object $app
the __destruct()
is triggered for the same reason as before for $app
in the function PredefinedApp()
Which means you now loop through each element from $this->apps
, which only holds the returned object of the function:
public function __destruct(){ //Line: 15
foreach($this->apps as $dep){
$result = $dep->cleanup($this);
}
}
Array(
"App1" => App Object
(
[apps:protected] => Array
(
)
[name] => PredefinedApp
[cleanup] => Closure Object
(
[parameter] => Array
(
[$parent] => <required>
)
)
)
)
For each element (Here only 1) you execute:
$result = $dep->cleanup($this); //Line: 17
But you don't call the closure! It tries to call a class method. So there is no cleanup
class method, it's just a class property. Which then means __call()
gets invoked:
public function __call($name, $arguments){ //Line: 21
if(is_callable([$this, $name])){
return call_user_func_array([$this, $name], $arguments);
}
}
The $arguments
contains itself ($this
). And is_callable([$this, $name])
is TRUE
, because cleanup
is callable as closure.
So now we are getting in the endless stuff, because:
return call_user_func_array([$this, $name], $arguments); //Line: 23
Is executed, which then looks something like this:
return call_user_func_array([$this, "cleanup"], $this);
Which then again tries to call cleanup
as a method and again __call()
is invoked and so on...
__call()
然后使用call_user_func_array()
引起的...它会再次调用__call()
。永远。或者至少直到你的内存耗尽。 - doublesharp