问题
I have a framework (OpenCart) Controller class (like: catalog/controller/product/product.php) the code looks like:
class ControllerProductProduct extends Controller {
public function index() {
//some code
$this->response->setOutput($this->render());
//some more code
}
}
there is an expression like $this->response->setOutput($this->render());
. I know what this expression is used for, but I am pretty confused in how it works.
$this
refers to current class i.e. ControllerProductProduct
, it means $this->response
object must exist in either ControllerProductProduct
or its parent class Controller
. But this is not the case. This object actually exists in a protected property of parent class Controller
as Controller::registry->data['response']->setOutput()
. So shouldn't it say like:
$this->registry->data['response']->setOutput();
instead of $this->response->setOutput();
I am also giving a snippet of Controller
class so you can have idea.
abstract class Controller {
protected $registry;
//Other Properties
public function __construct($registry) {
$this->registry = $registry;
}
public function __get($key) {
//get() returns registry->data[$key];
return $this->registry->get($key);
}
public function __set($key, $value) {
$this->registry->set($key, $value);
}
//Other methods
}
I've no clue how this expression is working? Any idea how this is possible?
Thanks.
回答1:
This is working very easily using magic methods __get()
and __set()
.
If You are trying to get an inaccessible class variable (e.g. that is not declared) a magic __get('property_name')
method is invoked.
Thus when You are trying to retrieve $response
, a magic method __get()
is called and $this->registry->get('response')
is returned instead (as there is no $response
property declared).
Yes, You could write $this->registry->get('response')->setOutput($this->render());
instead, but this would be of not much use and more writing. It is OK to let the PHP retrieve the variable using it's __get()
method, though it is not so clean.
Anyway, there is nothing wrong with the solution.
EDIT: a little bit cleaner solution would be this:
class Controller {
//...
function getResponse() {
return $this->registry->get('response');
}
//...
}
Then You could call a concrete method in Your code and it would be clear enough:
class ControllerProductProduct extends Controller {
public function index()
//...
$this->getResponse()->setOutput($this->render());
}
}
But this would mean that there would be a need of getXYZ
method for each possible property while __get()
allows You to extend the $registry
with no further work needed (in the case I described if You would add another property to $register
You would have to add another getProperty()
method - but still this would be more clear/clean solution).
回答2:
This magic called "overloading".
Here it is smaller demo:
<?php
class PropsDemo
{
private $registry = array();
public function __set($key, $value) {
$this->registry[$key] = $value;
}
public function __get($key) {
return $this->registry[$key];
}
}
$pd = new PropsDemo;
$pd->a = 1;
echo $pd->a;
Look at http://php.net/manual/en/language.oop5.overloading.php. It's explained clearly enough.
来源:https://stackoverflow.com/questions/16031589/confusing-class-and-method-call-in-opencart