Запутанный вызов класса и метода в OpenCart

У меня есть класс контроллера фреймворка (OpenCart) (например: catalog/controller/product/product.php), код выглядит так:

class ControllerProductProduct extends Controller {
    public function index() {
      //some code
      $this->response->setOutput($this->render());
      //some more code
    }
}

есть такое выражение, как $this->response->setOutput($this->render());. Я знаю, для чего используется это выражение, но я совершенно не понимаю, как оно работает.

$this относится к текущему классу, то есть ControllerProductProduct, это означает, что объект $this->response должен существовать либо в ControllerProductProduct, либо в его родительском классе Controller. Но это не так. Этот объект фактически существует в защищенном свойстве родительского класса Controller как Controller::registry->data['response']->setOutput(). Так что не следует ли говорить так:

$this->registry->data['response']->setOutput();

вместо $this->response->setOutput();

Я также даю фрагмент класса Controller, чтобы вы могли иметь представление.

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
}

Я не знаю, как это выражение работает? Любая идея, как это возможно?

Спасибо.


person 8thperson    schedule 16.04.2013    source источник
comment
У меня такая же проблема, за исключением того, что метод __get не объявлен, и его также можно использовать, см.: stackoverflow.com/questions/23183327/   -  person Dew    schedule 20.04.2014


Ответы (2)


Это очень легко работает с помощью магических методов __get() и __set().

Если вы пытаетесь получить недоступную переменную класса (например, которая не объявлена), вызывается магический метод __get('property_name').

Таким образом, когда вы пытаетесь получить $response, вызывается магический метод __get() и вместо него возвращается $this->registry->get('response') (поскольку свойство $response не объявлено).

Да, вы могли бы вместо этого написать $this->registry->get('response')->setOutput($this->render());, но от этого было бы не так много пользы и больше написания. Можно разрешить PHP извлекать переменную с помощью метода __get(), хотя это не так чисто.

В любом случае, в решении нет ничего плохого.

РЕДАКТИРОВАТЬ: немного более чистое решение было бы таким:

class Controller {
    //...
    function getResponse() {
        return $this->registry->get('response');
    }
    //...
}

Тогда Вы могли бы вызвать конкретный метод в Вашем коде, и это было бы достаточно ясно:

class ControllerProductProduct extends Controller {
    public function index()
        //...
        $this->getResponse()->setOutput($this->render());
    }
}

Но это будет означать, что потребуется метод getXYZ для каждого возможного свойства, в то время как __get() позволит вам расширить $registry без дополнительной работы (в описанном мной случае, если вы добавите еще одно свойство в $register, вам придется добавить еще один getProperty() метод - но все же это было бы более понятным/чистым решением).

person shadyyx    schedule 16.04.2013

Эта магия называется "перегрузка".
Вот демка поменьше:

<?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;

Посмотрите на http://php.net/manual/en/language.oop5.overloading.php. Это объясняется достаточно ясно.

person Alexander Kononenko    schedule 16.04.2013