Сразу скажу( как многие считают в комментариях), что задача ставилось не написать шаблонизатор (которых и так много) и не заменить шаблонизатор JavaScript. Я прекрасно знаю, что true way это разделять html и данные. Но мне понадобилось писать html в классах, для создания компонентов фреймворка, на подобие CGridView в yii, стоит ли в таких местах выносить html в отдельные файлы решать вам.
Основная цель, избавится от html в классах и функциях.
Простой пример, обычная кнопка:
CHtml::create()
->p()
->a(array('href' => 'http://habrahabr.ru', 'class' => 'btn'))
->text('Перейти')
->render();
Результат:
<p><a href="http://habrahabr.ru" class="btn">Перейти</a></p>
Ничего хитрого, можно было бы этим и ограничется, но захотелось циклы:
$arr = array('1' => 'Первый', '2' => 'Второй');
CHtml::create()
->select($options)
->each(CHtml::plainArray($arr, 'value', 'text'))
->option('array("value" => $data->value)')
->text('$data->text')
->end()
->endEach()
Тут понадобилось вызвать функцию plainArray() которая превращает массив в виде:
$arr = array(
array('value' => '1', 'text' =>'Первый'),
array('value' => '2', 'text' => 'Второй')
);
Теги внутри цикла могут содержать функции или строки с eval выражениями, вложенность любая, пример с таблицей:
$columns = array(
array('id' => 'NAME', 'label' => 'Имя'),
array('id' => 'AGE', 'label' => 'Возраст')
);
$data = array(
array('NAME' => 'Петр', 'AGE' => 29),
array('NAME' => 'Василий', 'AGE' => 32)
);
CHtml::create()
->table()
->thead()
->tr()
->each($columns)
->th()
->text(function($column){
return $column['label'];
})
->end()
->endEach()
->end()
->end()
->tbody()
->each($data)
->tr()
->each($columns)
->td()
->text(function($row, $column) {
return $row[$column['id']];
})
->end()
->endEach()
->end()
->endEach()
->render();
Незакрытые теги закрываются автоматически.
Класс можно расширить, вплоть до использования в формах. Расширить можно за счет наследования или внедрения зависимости, за то, как будет выводится каждый тег и его атрибуты используется одна функция, поэтому можно легко переопределить это поведение.
class CMyHtml extends CHtml {
public function a($options = array()) {
$default = array(
'href' => 'javascript:void(0)'
);
return parent::a(array_replace($default, $options));
}
}
class CForm {
private $_lastLabel = '';
public function __construct(CModel $model, CHtml $html = null) {
$this->_model = $model;
$this->_html = $html ?: CHtml::create();
}
public function __call($method, $ps) {
$options = $ps ? $ps[0]: array();
if ($method === 'label') {
$this->_lastLabel = isset($options['for']) ? $this->_model->getLabel($options['for']) : '';
}
if ($method === 'text' && $this->_lastLabel) {
$options = $options ?: $this->_lastLabel;
$this->_lastLabel = '';
}
$this->_html->$method($options);
return $this;
}
}
Само решение можно посмотреть и попробовать на github.
Спасибо за внимание.
Комментарии (87)
fleaump
04.11.2015 15:20+11Есть же шаблонизаторы, чтобы не мешать код и верстку. А тут верстка остается все равно в коде.
stagnantice
04.11.2015 15:23-4Задача не ставилась облегчить работу верстальщику, а только облегчить жизнь тому, кто вынужден в php вставлять html.
fleaump
04.11.2015 15:30+1так шаблонизаторы и убирают этот «костыль», когда впаивают в код верстку, чтобы так не делали.
fleaump
04.11.2015 15:51+23Это походу просто этап такой.
1) начал кодить — код+ html в одном файле
2) уже делишь код на несколько php файлов, освоил include, но верстка все равно вперемешку
3) начал понимать что что то как то не очень — решил «генерировать» html в ходе кода
4) гордишься своей гениальностью и хочешь со всеми поделиться
5) понимаешь, что все равно херь какаято
6) начинаешь понимать, что надо разделять не просто по php файлам, а еще по сильнее — отделять верстку
7) недолго гугля находишь — шаблонизаторы
8) пытаешься освоить какойнибудь легковестный шаблонизатор
9) освоил — понял, что много кода тянет лишнего — в просто шаблонизаторе овер кода, чем ты написал на проект
10) написал свой шаблонизатор в 100 строк!
11) гордишься своей гениальностью и хочешь со всеми поделиться
12)начинает не хватать функциональности и гибкости — городишь костыли в коде и расширяешь функционал своего шаблонизатора, чтобы всё же используя возможность своего шаблонизатора все же не сорваться и не воткнуть кусок верстки
13)Шаблонизатор уже подошел к 1к строк кода, и все равно всплывает, что не хватает возможностей.
14)Успокоился пришел в %ПОПУЛЯРНЫЙ_ШАБЛОНИЗАТОР%Akdmeh
04.11.2015 16:51+715) Написал об этом коментарий на хабрахабре
mapron
04.11.2015 16:54+1конечно, ведь
хочешь со всеми поделиться
продолжу:
16) возвращаешься к идее использования php как шаблонизатора, просто вынося шаблоны в отдельный файл.stagnantice
04.11.2015 17:23if($element instanceof CFormInputElement) { if($element->type==='hidden') return "<div style=\"visibility:hidden\">\n".$element->render()."</div>\n"; else return "<div class=\"row field_{$element->name}\">\n".$element->render()."</div>\n"; }
Код из Yii, скажите, а они на каком сейчас этапе?fleaump
04.11.2015 17:37на этапе
? надо переписать
подход собрать гору данных и скормить их шаблонизатору для отрисовки в разы проще — не надо в ходе кода городить и подстраиваться под отрисовку.stagnantice
04.11.2015 17:44-1То есть вы против html кода в php в любом случае? Ну это ваша позиция, я считаю что он допустим для отрисовки стандартных интерфейсов, например того же bootstrap. И есть куча решений, где html код вшит в php.
fleaump
04.11.2015 18:34+1php и создан чтобы отдавать html. Но логику от отрисовки лучше отделять, чтобы в будущем не мучиться если надо перенести кудато код.
stagnantice
04.11.2015 18:54-1я согласен, но у меня отрисовку можно будет менять только через расширение классов, так изначально задумывалось. Я пишу компоненты, через которые будут использоваться при написании сайта, а не сам сайт и его вьюшки с дизайном.
fleaump
04.11.2015 19:43Но вот это
CHtml::create()
->table()
->thead()
->tr()
->each($columns)
->th()
->text(function($column){
return $column['label'];
})
->end()
->endEach()
->end()
->end()
всёж верстка, если кому-то понадобиться после допилить сайт и где то что то передвинуть или дорисовать атрибут, он полезет в ядро, менять там. В будущем, когда надо будет обновить движок, то апдейт сломает, то что поправили. Поэтому и рисуют элементы и мелкие части используя шаблоны — просто отправил туда массив переменных, и оно само наложило это всё и отрисовало. И это никак не влияет на обновления + скины.stagnantice
04.11.2015 20:13-1Я не для сайта это делаю, а для фреймворка, это компонент выводит таблицу. Но можно пользоваться им, можно от него от наследоваться, а можно и вовсе написать свое, никто не запрещает. Но будет сделано так, чтобы в 90% случаев хватило использование стандартного, чтобы облегчить всем жизнь.
michael_vostrikov
04.11.2015 17:58На этапе «уже давно вышла вторая версия», которая использует класс Html.
Класс для генерации html подходит для внутреннего использования (например, в другом классе для работы с формами), но для верстки страниц лучше использовать обычные теги с кодом на php или шаблонизатором.
Delphinum
04.11.2015 17:34+28) пытаешься освоить какойнибудь легковестный шаблонизатор
PHP достаточно легковесный шаблонизатор? )
kovalevsky
04.11.2015 17:364й шаг уже давно пройден, автор даёт ссылку на гитхаб, где у него уже целый фреймворк собственный
andrew_tch
04.11.2015 15:32-7Оггосподи.
С ужасом понимаю что PHP постам только минусы ставить, увы.mapron
04.11.2015 16:55-5И не говорите… (сам писал 5 лет на PHP, но то что в посте — лучше даже не распространять).
michael_vostrikov
04.11.2015 17:11+4Сделаю картинкой, чтобы было видно все сразу:
Скрытый текстstagnantice
04.11.2015 17:24-4Скажите, что я тут должен увидеть?
michael_vostrikov
04.11.2015 17:30+4— меньше строк
— разная подсветка для html и php кода
— подсветка открывающего и закрывающего тега
— возможность свернуть содержимое тегаstagnantice
04.11.2015 17:40-3а теперь представьте что этот код у вас где-то в классе, и этот html надо возвращать чтобы отправить письмо.
Delphinum
04.11.2015 17:44+2$template = new Template('mail.tpl'); $mail = new Mail; $mail->setBody($template->render($data)); $mail->send();
stagnantice
04.11.2015 18:04-5а файл у вас прям рядом лежит? и так с каждым классом надо будет думать куда файл положить да?
Delphinum
04.11.2015 18:06+1Лично у меня шаблоны писем складируются в каталог mails.
и так с каждым классом надо будет думать куда файл положить да?
Это не столь сложная задача чтоб о ней думать )stagnantice
04.11.2015 19:30-3Ну, мне это не подходит. Мне нужно писать компоненты, в которых html внутри не изменится. Может если бы у меня была просто отправка писем, я бы не стал заморачиваться и сделал как вы.
Delphinum
04.11.2015 19:40+1Ну шаблонизатор можно использовать без шаблонизации:
$template = new Template('mail.tpl'); $mail = new Mail; $mail->setBody($template->render([])); $mail->send();
или вы о чем?
Народ возмущается тому, что вы предлагаете писать:
$html->head()->title('Hello');
Вместо:
<html> <head> <title>Hello</title>
Второй вариант может и длинее, но привычнее.stagnantice
04.11.2015 20:02Я так не предлагаю писать, боже упаси!
Этот класс я буду использовать только внутри других классов — компонентов, где заранее известно какие теги и классы будут.
Главная цель — избавить классы от html. Другие цели не ставились. Да, можно выносить html в файлы, но у меня html практически статичный, мне легче создать его каким-то объектом.
kovalevsky
04.11.2015 17:35Намного приятнее смотрится, если использовать if/endif, foreach/endforeach и и прочее вместо фигурных скобок в HTML.
kovalevsky
04.11.2015 17:24Для Вас расширяемость ограничивается extend'ом?
и…
->option('array("value" => $data->value)') ->text('$data->text')
шта?stagnantice
04.11.2015 17:26Там должна быть функция, которая вернет массив атрибутов, либо такой вариант. Переопределить можно любой тег, добавив классы. Первое расширение которое я пишу для bootstrap.
kovalevsky
04.11.2015 17:32Да я с вашего гитхаба Вам приколов ещё накидать могу. Там по логике в коде видно, что оно писалось из головы и придумывалось на ходу
kovalevsky
04.11.2015 17:31+2
vosi
04.11.2015 18:27+2картинка_про_тролейбус_из_хлеба.bmp
Delphinum
04.11.2015 18:31+3Похоже баг хабра. Не беспокойтесь, я поправлю!
m1el
04.11.2015 18:31+3Я считаю, что генерация HTML при помощи DOM-подобных приемов это очень правильный подход, и автор пытается это сделать.
Но получается криво.Delphinum
04.11.2015 18:32Вы про DOMDocument?
m1el
04.11.2015 18:35+1Можно использовать и DOMDocument для генерации HTML, и это подходит под мой критерий. Но апи у него очень громоздкое.
В качестве примера того что я имею в виду я выберу React.js.
andrew_tch
04.11.2015 19:06+1А вообще это haml. Или jade.
Чем это пилить — пофиксите пару багов в парсере того или другого для php.stagnantice
04.11.2015 19:16-1Да я в курсе про haml, писал на нем в Ruby on Rails. Но хотелось написать свое, и написал.
andrew_tch
04.11.2015 19:21+1ну так раз: packagist.org/search/?q=haml
и два — там надо было писать хамл, а не чудо спагететвое и называть его революционным способом регенации html из php )stagnantice
04.11.2015 19:27-1где я его так назвал?) Спасибо за ссылку на композер) я знаю где он лежит.
x88
04.11.2015 19:49+1Практически полностью отказались от компонентов CHtml. Один кастомный CGridView сжирает на 7мб памяти больше, чем нативные foreach. Сейчас php дорос до удобной шаблонизации без дополнительных надстроек, и не стоит бояться шорт-тэгов:
<?php foreach($myArr as $id =>$obj): ?>
<?php if ($obj->isShow): ?>
<?=$var; ?>
<?php endif; ?>
<?php endforeach; ?>
Delphinum
04.11.2015 19:52-2Можно легко заменить <?php на <? с помощью директивы short_open_tag, если вы не планируете пользовать PHP шаблонизацией в XML.
x88
04.11.2015 20:02-1В некоторых проектах использование такой формы еще является стандартом. О шорт тэгах я кстати и написал.
zelenin
04.11.2015 21:10что не соответствует psr-1 и добавляет проблем сторонним пользователям.
Delphinum
04.11.2015 21:14Когда вы пишете готовый сервис, у вас в компании могут быть свои стандарты, которые (возможно) отличаются от psr-1, и это вполне нормально.
zelenin
04.11.2015 21:16конечно же, но это не повод советовать их использовать на хабре.
Delphinum
04.11.2015 21:18-2Почему же не повод? Psr-1 это не обязательство, а всего лишь стандарт. Я его не соблюдаю и имею право советовать это другим программистам.
zelenin
04.11.2015 21:30а советовать надо best practices. а это следование рекомендованным стандартам. рекомендуя свои внутренние корпоративные стандарты без аргументации, вы плодите зло на Земле в виде например фреймворка автора.
Все современные популярные библиотеки следуют psr-1,2,4. Это удобно, единообразно, и не заставляет разработчика привыкать к новому для себя кодстайлу.
Стандарт в области кодстайла удобен не тем, что он лучше (многие моменты могут породить холивар вкусовщины), а тем, что он стандарт.Delphinum
04.11.2015 21:58+2а советовать надо best practices. а это следование рекомендованным стандартам.
А если я не согласен со стандартом, то мне надо вздохнуть и все равно рекомендовать стандарт?zelenin
04.11.2015 22:08Тут в комментах много советов автору. Комментирующие видимо хотят подтолкнуть его на путь истинный. Что хотите вы, я не в курсе.
Мое мнение таково: есть два варианта — либо вы советуете стандарт ибо он де-факто выигрышен тем, что стандарт, либо в случае несогласия со стандартом, предлагаете свой вариант, обосновывая почему ваш вариант лучше, ведь возможно вы и правы. Если вы считаете, что ваш, третий вариант — рекомендовать без аргументации — правилен, то обсуждение можно закрыть — я неавторитетен вас учить поведению на отраслевых ресурсах.Delphinum
04.11.2015 22:19Тут в комментах много советов автору. Комментирующие видимо хотят подтолкнуть его на путь истинный. Что хотите вы, я не в курсе
Я автору не советов не следовать psr-1. Вы что то путаете.
Мое мнение таково
Вы хотите чтоб я продемонстрировал используемый мной стандарт да еще и сравнил его с psr в комментариях? )zelenin
04.11.2015 22:29неиспользование шорт-тегов — один из пунктов psr-1.
Прочтите нашу ветку еще раз — не надо еще одной итерации с начала.Delphinum
04.11.2015 22:33неиспользование шорт-тегов — один из пунктов psr-1
Я разве спорю?
Прочтите нашу ветку еще раз — не надо еще одной итерации с начала
Не наблюдаю моего отступления от моего мнения, как и ваших аргументов.zelenin
04.11.2015 22:40вы не спорите, что это стандарт, но упоминаете, что можно юзать их, отключив директиву в php.ini. Это вредный совет, поскольку это пассивная рекомендация писать нестандартизированный код. Тот же автор увидит ваш комментарий и подумает: а действительно, зачем не везде <?php, ведь <? короче и компактнее. Не всем очевидно, что это вредно.
Delphinum
04.11.2015 22:48Это вредный совет, поскольку это пассивная рекомендация писать нестандартизированный код
Видимо кроме PSR вы другие стандарты не воспринимаете )
Тот же автор увидит ваш комментарий и подумает: а действительно, зачем не везде <?php, ведь <? короче и компактнее
И верно подумает.
Не всем очевидно, что это вредно
Даже мне не очевиден вред использования тегов.zelenin
04.11.2015 22:58есть принятый сообществом единый стандарт. Ключевое словосочетание «принятый сообществом». Это был прорыв, т.к. объединил все разрозненные кодстайлы, существовавшие до этого. Это значит, что все ведущие, все современные библиотеки используют один кодстайл. Вам очевидна выгода использование единого кодстайла? Очевидна выгода единого стандарта?
Подумает верно, ведь действительно шорт-теги короче и компактнее (хотя не дает никакого профита при разработке), но не полезнее.
Вред простой: а) для использования шорт-тегов нужно включать дополнительную директиву — не всегда возможно, б) библиотека с шорт-тегами не будет работать при настройках по умолчанию (а шорт-теги это все-таки не функциональная фича), в) тег <? неоднозначен (как вы упомянули выше)
С <?php проблем никаких нет, поэтому это стандартизированный тег.Delphinum
04.11.2015 23:02-1есть принятый сообществом единый стандарт
Да что вы привязались то ко всему стандарту в целом, я о частностях его говорю )
Вред простой: а) для использования шорт-тегов нужно включать дополнительную директиву — не всегда возможно, б) библиотека с шорт-тегами не будет работать при настройках по умолчанию (а шорт-теги это все-таки не функциональная фича), в) тег <? неоднозначен (как вы упомянули выше)
С <?php проблем никаких нет, поэтому это стандартизированный тег.
Меня всегда улыбало использование стандарта во вред разработки )zelenin
04.11.2015 23:06Я аргументировал все что можно — вы продолжаете оперировать абстракциями.
Я указал, что использование шорт-тега идет во вред разработке. Неиспользование во вред не идет.Delphinum
04.11.2015 23:08Ок, приведу пример. У меня компания, в ней работает десяток программистов. Код закрытый и распространять его мы не планируем. Все программисты сходятся во мнении, что писать <? ?> им проще чем <?php ?>. Хостится система на наших серверах и включить дериктиву мы можем. Мы пользуем PSR, но не пользуем некоторыми его частностями. Как думаете, мы все попадем в ад?
zelenin
04.11.2015 23:14так с этого и началось: внутри своей компании используйте что хотите.
Аналогично: у нас продукт 4-летней давности, в легаси-коде используются шорт-теги, но все разработчики понимают ценность следованию стандартам и весь новый код пишут в psr-1,2,4.
Вы продолжаете юлить. Есть понимание, что следование стандартам важно?Delphinum
04.11.2015 23:16А где в этой ветке вы нашли мои слова относительно использования <? в библиотеках?
Вы продолжаете юлить. Есть понимание, что следование стандартам важно?
Я лишь изучаю ваш догматизм, не более того )
Finesse
05.11.2015 12:49+1Предлагаю такой вариант шорт-тэгов:
<?php foreach($myArr as $id =>$obj) { ?>
<?php if ($obj->isShow) { ?>
<?=$var; ?>
<?php } ?>
<?php } ?>Delphinum
05.11.2015 12:56Говорят что использование { } в HTML делает код менее читабельным, ибо не понятно что именно закрывается очередной скобкой }, а закрываться это может далеко внизу.
Finesse
05.11.2015 13:21Название закрывающего тега тоже не всегда говорит, какой именно тег открывался (если много одинаковых тегов). Нужно применять отступы, в этом случае будет понятно и с }, и с endif. HTML, в котором не используются отступы, плохо читаем.
zelenin
04.11.2015 21:22+2на всякий случай напишу: у автора там на гитхабе целый фреймворк, который явно вдохновлен yii1 (год создания — 2008) и его стандартами тех лет. Поэтому автор не отделяет добро от зла и вряд ли внимает доводам из комментов.
MTonly
04.11.2015 22:55+1Смешивать на одном логическом уровне разные сущности (в данном случае — имена элементов типа
tbody
и действия типаeach
илиrender
) — путь в никуда. Вот добавят в стандарт HTML элементыeach
илиrender
, и приехали. ;-)
Finesse
05.11.2015 12:44Обычный HTML вместо такого кода компактнее, проще и понятнее. Конструкции с тег()->end() выглядят отталкивающе. Было бы более элегантно, если бы оно было сделано как в jQuery.
duke_nu
05.11.2015 13:25+1верстальщик вернул хтмл, потом его полностью надо переписать на шаблонизаторе. затратно по времени, могут возникнуть проблемы с внесением изменений.
enleur
Один вопрос — зачем?
stagnantice
Ну во-первых код выглядит чище без <?, ?>, во вторых для генерации более сложных компонентов системы на php, без использования html.
Delphinum
HTML код на чистом PHP, выглядит чище, чем HTML код с примесью PHP? )
stagnantice
если им нужно манипулировать, то так и так будет через php.