Введение
На определенной стадии изучения серверного программирования мне захотелось написать свой простенький фреймворк. Я рассчитывал, что это поможет более глубоко понять идеологию MVC и Zend Framework в частности. Когда дело дошло до части представления и генерации html я вспомнил о паттерне Компоновщик (Composite pattern). Возможно я несколько исказил его применение, но мысль пошла оттуда.
К сути
Любая страница состоит из объектов, которые так-же могут состоять из объектов. Этакая вложенность наталкивает на мысль рекурсивной генерации html-кода. Так же хочется иметь возможность дополнять содержимое любой части страницы в любой момент исполнения логики приложения и генерировать весь html-код разом.
К делу
Любой объект может содержать другой объект и т.д. Каждый объект обладает методом draw(), который отображает html-код своего объекта и вызывает метод draw() для всех дочерних объектов. Объекты могут быть разных типов (например: статья, список, пост, фотоальбом, шапка в конце концов). Конечно реализация метода draw() для каждого типа объектов будет своя. Хотелось бы выразить выше написанное кодом.
abstract class AbstractView { public $fillings; abstract public function draw(); protected function insert($filling_name){ if(isset($this->fillings[$filling_name])){ $this->fillings[$filling_name]->draw(); } } }
Метод insert() — метод, с помощью которого в методе draw() будут вызываться методы draw() дочерних объектов. Все дочерние объекты содержатся в ассоциативном массиве $fillings. Часто необходимо не генерировать html-код, а просто написать текст или число. Можно конечно Для таких целей наследовать объект View, метод draw() которого будет просто выводить необходимое значение, указанное, например, в конструкторе или дополнительном свойстве. Но это крайне не удобно и будет усложнять процесс формирования страницы. Поэтому создадим второй ассоциативный массив для хранения таких значений и метод для их вывода.
abstract class AbstractView { public $fillings; public $values; abstract public function draw(); protected function insert($filling_name){ if(isset($this->fillings[$filling_name])){ $this->fillings[$filling_name]->draw(); } } protected function write($value_name){ if(isset($this->values[$value_name])){ echo $this->values[$value_name]; } } }
Теперь напишем два простейших наследника для демонстрации. Один будет представлять шапку, а второй приветствие.
class LayoutView extends AbstractView { public function draw(){ include '/layout.phtml'; } } class IndexView extends AbstractView{ public function draw(){ include '/index.phtml'; } }
Что же такое файлы layout.phtml и index.phtml?
layout.phtml:
<!DOCTYPE html> <html> <head> <title><?php $this->write('title')?></title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <?php $this->insert('content'); ?> </body> </html>
index.phtml:
<div>Hello, Habrahabr!</div>
Соединим все и с генерируем страницу:
$page = new LayoutView(); $page->values['title'] = "greating"; $page->fillings['content'] = new IndexView(); $page->draw();
Результат:
<!DOCTYPE html> <html> <head> <title>greating</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div>Hello, Habrahabr!</div> </body> </html>
На данный момент такая реализация кажется мне удобной, но не исключено, что в скором времени я изменю свое мнение.
Как сказал Вольтер:
«Я могу быть не согласным с Вашим мнением, но я готов отдать жизнь за Ваше право высказывать его.»
P.S.: Прошу обратить внимание на комментарии sdevalex о классе VariableView и arturgspb о типовой безопасности.