[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Микро шаблонизатор на обычных инклудах
Aeq
Очередной велосипед от меня, на этот раз шаблонизатор.
Очень простой, но в то же время очень функциональный и расширяемый класс шаблонизатор ATPL.

Я не любитель всяких шаблонизаторов со своим синтаксисом (типа смарти). По мне так чем меньше языков в проекте тем лучше, тем легче влиться в проект другим. И меня более чем устраивает нативный синтаксис PHP. Примерно с такими предпосылками родился этот класс.

1. Особенности
  • Наследование шаблонов в обе стороны: обворачивание лэйаутом или заворачивание внутрь.
  • Обворачивание другим ATPL объектом.
  • Добавление дочерних ATPL объектов.
  • Практически никаких накладных расходов: в процессе рендера рассчитываются только 2 инта.
  • Никакой буферизации, все работает на обычных инклудах, без использования всяких ob_*.
2. Использование

2.1 Методы
$mytpl = new ATPL();
$mytpl->addViewFile('MyTemplate.phtml');
$mytpl->render();

Пример выше просто инклудит файл "MyTemplate.phtml" когда вызывается метод "render".

Для упрощения, когда ATPL используется внутри классов (или при наследовании ATPL), предусмотрен метод "addView", который принимает аргументом константу __CLASS__ или __METHOD__ и преобразовывает ее в путь к файлу представления в стиле PSR-0 (по сути просто замена резделителя неймспейсов "\" на разделитель директорий "/") с парой отличий: резделитель методов "::" тоже преобразовывается в разделитель директорий "/", и в качестве расширения используется ".phtml".
$mytpl->addView(__CLASS__);

Можно добавить несколько вьюх к одному объекту: каждая следующая вьюха будет заворачиваться внуть предыдущей. Чтобы определить, где нужно рендрить следующую вьюху, нужно вызвать метод "contents" в файле-представлении.
$mytpl = new ATPL();
$mytpl->addViewFile('Around.phtml');
$mytpl->addViewFile('Inside.phtml');
$mytpl->render();

<!-- Around.phtml -->
Around begin
<?php $this->contents() ?>
Around end

    <!-- Inside.phtml -->
Inside contents

Результатом будет:
<!-- Around.phtml -->
Around begin
<!-- Inside.phtml -->
Inside contents
Around end

Можно завернуть один объект в другой с помощью метода "wrap". Когда объект, который обвернули другими объектами, рендрится, он использует сначала все обвернутые объекты начиная с последнего. Немного запутано написал, на примере станет понятно.
$around = new ATPL();
$around->addViewFile('Around.phtml');

$aroundMore = new ATPL();
$aroundMore->addViewFile('AroundMore.phtml');

$inside = new ATPL();
$inside->addViewFile('Inside.phtml');

$inside->wrap($around);
$inside->wrap($aroundMore);

$inside->render();

Результатом будет:
<!-- AroundMore.phtml -->
AroundMore begin
<!-- Around.phtml -->
Around begin
<!-- Inside.phtml -->
Inside contents
Around end
AroundMore end

Можно спокойно совмещать оба метода "addView" и "wrap" в любых вариациях.

Для добавления дочерних объектов предусмотрен метод "push". Все дочерние будут отрендрены в порядке добавления при вызове метода "contents" у родительского объекта.
$child1 = new ATPL();
$child1->addViewFile('Child1.phtml');

$child2 = new ATPL();
$child2->addViewFile('Child2.phtml');

$parent = new ATPL();
$parent->addViewFile('Parent.phtml');

$parent->push($child1);
$parent->push($child2);

$parent->render();

<!-- Parent.phtml -->
Parent begin
<?php $this->contents() ?>
Parent
end

    <!-- Child1.phtml -->
Child1 contents

Результатом будет:
<!-- Parent.phtml -->
Parent begin
<!-- Child1.phtml -->
Child1 contents
<!-- Child2.phtml -->
Child2 contents
Parent end


2.2 Наследование

Если отнаследовать класс ATPL, можно добавить файл-представление в конструкторе.
Вообще говоря не только можно но и нужно, это основной способ использования который я предполагал изначально, а использование "addViewFile" только для простоты понимания и в реальности нужен редко.
class LayoutBase extends ATPL
{
public function __construct()
{
$this->addView(__CLASS__);
}
}


    <!-- LayoutBase.phtml -->
LayoutBase begin
<?php $this->contents() ?>
LayoutBase end

Этот класс можно отнаследовать следующим, и добавить в нем еще один файл-представление. При этом если добавить его до вызова родительского конструктора, то файл-представление будет добавлен в очередь до родительского. Это значит что он будет обвернут вокруг родительского.
class LayoutAround extends LayoutBase
{
public function __construct()
{
$this->addView(__CLASS__);
parent::__construct();
}
}


<!-- LayoutAround.phtml -->
LayoutAround begin
<?php $this->contents() ?>
LayoutAround end

$tpl = new LayoutAround();
$tpl->render();

Результатом будет:
<!-- LayoutAround.phtml -->
LayoutAround begin
<!-- LayoutBase.phtml -->
LayoutBase begin
LayoutBase end
LayoutAround end

Если же добавить файл-представление после вызова родительского конструктора, то файл-представление будет добавлен в очередь после родительского. Это значит что он будет рендриться внутри родительского.
class LayoutInside extends LayoutBase
{
public function __construct()
{
parent::__construct();
$this->addView(__CLASS__);
}
}


        <!-- LayoutInside.phtml -->
LayoutInside begin
<?php $this->contents() ?>
LayoutInside end

$tpl = new LayoutInside();
$tpl->render();

Результатом будет:
    <!-- LayoutBase.phtml -->
LayoutBase begin
<!-- LayoutInside.phtml -->
LayoutInside begin
LayoutInside end
LayoutBase end


В целом это все.
В проекте есть один юнит-тест, написан он совершенно не круто, но покрывает все используемые методы )))
Описалова много, но на самом деле класс настолько тонок и прост, что если убрать все комменты то код умещается на страницу с хвостиком.
Код лежит на GitHub.

Жду мнений, предложений, пожеланий, багов.

Напоследок еще обобщающий пример. Описывать классы из этого примера не буду, по выводу все вроде понятно.
$layout1 = new Layout1Inside();
$layout2 = new Layout2();
$content = new Content();
$child1 = new Child1();
$child2 = new Child2();

$content->wrap($layout2);
$content->wrap($layout1);

$content->push($child1);
$content->push($child2);

$content->render();

Результат:
Layout1 begin (extended around Layout1Base)
Layout1Base begin
Layout1Inside begin (extended filling Layout1)
Layout2 begin (wrapped around $content before than $layout1)
Content begin (extended ContentBase)
ContentBase begin
Child1 contents
Child2 contents
ContentBase end
Content end
Layout2 end
Layout1Inside end
Layout1Base end
Layout1 end
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.