Как реализовать «обратный» порядок вызова методов класса PHP?

Добрый вечер, подскажите пожалуйста как можно реализовать возможность вызова методов в таком поряке
$tpl = new Template;
$tpl->render('templateName')->withData($dataArray);

То-есть сначала должен выполнится метод withData() и записать массив данных, а затем уже метод render() и отрендерить шаблон с полученным массивом.
Буду также благодарен за ссылки на статьи, которые объясняют как работает подобный алгоритм.
  • Вопрос задан
  • 3195 просмотров
Решения вопроса 2
AMar4enko
@AMar4enko
А какова цель? Получить более естественный, легко читаемый код?
Чем не нравится конструкция типа:
$this->template('templateName')->withData($array)->render()


Есть еще вариантик - перекрыть в Template метод __toString():
В render устанавливаете имя шаблона, в withData параметры.
В __toString вызываете непосредственно render с установленными ранее именем шаблона и параметрами.
Тогда весь вызов будет как (string)$tpl->render('templateName')->withData($dataArray)

Так или иначе, вам придется определять какой-то терминальный метод. Просто во втором случае он неявно вызывается, в том числе и в конструкциях типа
$html = "<some html code>$tpl</some html code>"
Ответ написан
shineblu
@shineblu
Добрый день,

В дополнение к решению от @AMar4enko

class Template {
	private $callback = array();
	private $name = 'dummy';
	private $data = array();

	private function doCallback() {
		foreach (array_reverse($this->callback) as $func) {
			call_user_func_array(array($this, $func[0]), $func[1]);
		}
		$this->callback = array();
	}

	// реализация вызываемых методов (вызываются только из doCallback)
	private function _render($name) {
		echo "Call Render<br>\r\n";	
		$this->name = $name;
	}

	private function _withData($data) {
		echo "Call WithData<br>\r\n";
		$this->data = $data;
	}

	// эти функции записываются в стек и видны извне
	public function render($name) {
		$this->callback[]=array('_'.__FUNCTION__, func_get_args());
		return $this;
	}
	
	public function withData($data) {
		$this->callback[]=array('_'.__FUNCTION__, func_get_args());
		return $this;
	}

	// конечная функция которая вызовет функции из стека в обратном порядке
	public function __toString() { 
		$this->doCallback();
		echo "Show output<br>\r\n";
		return "";
	}

	// конечная функция которая вызовет функции из стека в обратном порядке
	public function compile() { 
		$this->doCallback();
		echo "Show output<br>\r\n";
	}
}

$tpl = new Template;
$tpl->render('templateName')->withData(array('a','b','c'));
echo $tpl->compile(); // или echo $tpl;


Наслаждайтесь :)
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
AMar4enko
@AMar4enko
$cookie = Cookie::make('name', 'value');
return Response::make($content)->withCookie($cookie);

Тот, кто получит Response, сам отправит данные в браузер. Попробуйте их схему применить в случае, если make($content) непосредственно отправляет контент в браузер - не получится.
Потому что вы данные отправите в браузер, а заголовок уже не сможете, так как перед этим отправлены данные.
Это все называется fluent interface или текучий интерфейс.
Но он был придуман не для того, чтобы выполнять методы в произвольном порядке, а для того, что бы писать более компактный и читаемый код.

Но я вот еще что придумал, пример (могут быть ошибки, не запускал)

class SomeFreakyEntity {
    var param1, param2;
    public function terminalMethod(){
    // Этот метод всегда выполняет некое действие с настроенным объектом, конец текучего интерфейса
        var_dump($this);     
    }
    public function withSomeParams1($value){
        $this->param1 = $value;
        return $this;
    }
    public function withSomeParams2($value){
        $this->param2 = $value;
        return $this;
    }
    // Ловим любой вызов необъявленного статического метода
    public static function __callStatic($method, $arguments){
        $o = new __CLASS__;
        return call_user_func_array(array($o, $method), $arguments)        
    }
}


Теперь можно писать как-то так:
SomeFreakyEntity::terminalMethod();
SomeFreakyEntity::withSomeParams1('blabla')->terminalMethod();
SomeFreakyEntity::withSomeParams2('blabla')->terminalMethod();
SomeFreakyEntity::withSomeParams2('blabla')->withSomeParams1('blabla')->terminalMethod();
SomeFreakyEntity::withSomeParams1('blabla')->withSomeParams2('blabla')->terminalMethod();


А в вашем случае
Template::withData($dataArray)->render('templateName')
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы