Наследование шаблонов в PHP без использования сторонних библиотек

    При разработке Web-приложений мы обязательно сталкиваемся с проблемами рендеринга HTML-страниц. Обычно эти проблемы решает шаблонизатор — собственно PHP или какой-нибудь парсер шаблонов. Если приложение большое и страницы содержат множество блоков, то сложность шаблонов может резко возрасти, а у разработчиков появляется желание упростить работу с ними. В ход идут разные техники, но обычно это выделение в шаблонах повторяющихся блоков и правильная их декомпозиция — включая наследование шаблонов.

    Мне нравится, как сделано наследование шаблонов в Django. Идея простая — есть базовый шаблон, в нем выделены контентные блоки, которые будут меняться в зависимости от страницы. При рендеренге страницы можно указать, что за основу берется базовый шаблон и переопределить только нужные блоки. Если в проекте много однотипных страниц, то можно сделать промежуточный шаблон, наследующий от главного, а затем переопределять его данные. При умелом проектировании количество повторяющегося кода можно свести на нет, а также облегчить жизнь при изменении дизайна.

    Похоже этот подход нравится не только мне и разработчикам Django, но и Fabien Potencier автору фреймворка Symfony и шаблонизатора Twig. Twig обладает множеством интересных функций, включая компиляцию шаблонов в нативные PHP-классы, фильтрацию данных, встроенными циклами и т.д. — в общем всем тем, что полагается иметь современному шаблонизатору. Но самое интересное — это то самое наследование шаблонов, о котором я говорил выше. Вот пример из официальной документации:

    Базовый шаблон:

    <!DOCTYPE html>
    <html>
        <head>
            {% block head %}
                <link rel="stylesheet" href="style.css" />
                <title>{% block title %}{% endblock %} - My Webpage</title>
            {% endblock %}
        </head>
        <body>
            <div id="content">{% block content %}{% endblock %}</div>
            <div id="footer">
                {% block footer %}
                    © Copyright 2011 by <a href="http://domain.invalid/">you</a>.
                {% endblock %}
            </div>
        </body>
    </html>
    


    Дочерний шаблон:

    {% extends "base.html" %}
    
    {% block title %}Index{% endblock %}
    {% block head %}
        {{ parent() }}
        <style type="text/css">
            .important { color: #336699; }
        </style>
    {% endblock %}
    {% block content %}
        <h1>Index</h1>
        <p class="important">
            Welcome on my awesome homepage.
        </p>
    {% endblock %}
    


    В базовом шаблоне определены основные блоки, а в дочернем — пример их переопределения. Копания в исходных кодах показало, что реализуется это наследование также нативно — путем наследования “скомпилированных” классов. Отличная идея! Все хорошо, но меня несколько смущала необходимость изучения пусть простого, но все-таки отличного от PHP синтаксиса шаблонизатора. А моя нелюбовь к ненативным шабонам началась еще со Smarty (в котором тоже есть наследование) и до сегодняшнего дня не претерпела существенных изменений.

    Совсем близко к иделу подошел разработчик из Сан-Франциско Adam Shaw. Очевидно, он также как и я не любит эксперименты с синтаксисом шаблонизаторов и придумал незамысловато названную библиотеку Template Inheritance На сайте жирным по желтому написано, что «There is no need to learn another template language», мол не нужно учить другой язык шаблонов. С этим я согласен. Что же он предлагает? Смотрим пример опять же из официальной документации:

    Базовый шаблон:

    <?php require_once 'ti.php' ?>
    <html>
        <body>
            <h1>
                <?php startblock('title') ?>
                <?php endblock() ?>
            </h1>
            <div id='article'>
                <?php startblock('article') ?>
                <?php endblock() ?>
            </div>
        </body>
    </html>
    


    Дочерний шаблон:

    <?php include 'base.php' ?>
    
    <?php startblock('title') ?>
       This is the title
    <?php endblock() ?>
    
    <?php startblock('article') ?>
       This is the article
    <?php endblock() ?>
    


    Синтаксис натуральный, блоки выделены явно, подключил библиотеку в базовый шаблон и забыл. Все. Автор говорит, что сделал это с помощью буферов и стека (возможны вложенные блоки). Код действительно интересный, но пестрит наличием глобальных переменных. Чего же остается ещё желать?

    Вот здесь-то мы и подходим к главной теме нашего повествования. А не сможет ли PHP сам переопределить блоки базового шаблона? Я думаю, что вполне! Смотрите:

    Вот базовый шаблон:

    <!DOCTYPE HTML>
    <html lang="ru-RU">
        <head>
            <title><?php echo isset($title) ? $title : ''; ?></title>
            <meta charset="UTF-8"> 
        </head>
        <body>
            <div class="content">
    	    <?php if(isset($content)){echo $content}else{ ?>
    	        Default content
    	    <?php }?>
    	</div>
    	<div class="sidebar">
    	    <?php if(isset($sidebar)){echo $sidebar}else{ ?>
    	        Default sidebar
    	    <?php }?>
    	</div>
        </body>
    </html>
    


    Здесь в 3 блоках шаблона проверяется наличие соответствующей переменной, хранящей некоторый контент и, если она присутствует в области видимости, то ее можно выводить в шаблон, а если нет, то выводится контент по-умолчанию. Нам остается только переопределить эти переменные в дочернем шаблоне. А вот собственно и он:

    <?php if(!isset($content)){ ob_start(); ?>
        <h1>Переопределенный контент</h1>
    <?php $content = ob_get_clean();} ?>
    
    <?php require 'baseTemplate.php'; ?>
    


    В этом примере переопределяется переменная $content, если она не была установлена заранее. Это сделано для того, чтобы была возможность наследовать этот шаблон и переопределить блок контента. Думаю, идея понятна. Не требуется никаких библиотек — просто пишите шаблоны в таком стиле и будет вам счастье.

    Конечно, и здесь не обошлось без недостатков. Во-первых, это не очень лаконичный синтаксис определения и переопределения блоков: расплата за нативность. Во вторых, в дочернем шаблоне нельзя получить код родительского блока. В-третьих, при таком способе включения шаблонов перед собственно HTML-кодом может образоваться некоторое количество пробелов из-за отступов между блоками. Здесь я бы посоветовал подключать шаблон также с помощью буферов и фильтровать контент. Так делается во многих фреймворках:

    function render($pathToTemplate, $data)
    {
    	extract($data);
    	ob_start();
    	require $pathToTemplate;
    	return trim(ob_get_clean());
    }
    


    Эта функция возвращает вывод шаблона из файле $pathToTemplate с подстановкой переменных, полученных из массива $data. extract — на любителя — можно и не делать, а обращаться напрямую к $data. Перед выводом из контента убираются начальные и конечные пробелы. В шаблоне можно делать все, что позволит делать ваша совесть, не нарушая принципы разделения логики и представления, и PHP. Например, в зависимости от ситуации подключать тот или иной базовый файл.

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

    Комментарии 58

      +23
      Я не сторонник шаблонизаторов, но вот это:

      <?php if(!isset($content)){ ob_start(); ?>
          <h1>Переопределенный контент</h1>
      <?php $content = ob_get_clean();} ?>

      Ужасно. Куда лучше выглядит:

      {% block content %}
          <h1>Переопределенный контент</h1>
      {% endblock %}

      P.S. Поправьте «за ранее», сильно режет глаза.
        0
        Элементарно в шаблонизаторе (если он есть).
        $tpl = $preg_replace('/{%\sblock\s([a-Z0-9_]*?)\s%}/','<?php if(!isset($$1)){ ob_start(); ?>',$tpl);
        

        А если надо очень сложную логику, пишется напрямую.
        +26
        Спасибо, что не про Singleton.
          +3
          Вся прелесть Twig в том что он позволяет довольно легко и изящно добавлять и наращивать ф-ционал.
            +5
            Здорово, весьма интересно.
            Сейчас почему то считается стыдным на хабре писать про свои реализации и велосипед(«ведь есть twig, symphony, Zend, Yii, %frameworkname%»).
            У меня подобный подход, могу рассказать о дочерних шаблонах, если кому интересно.
              0
              Интересно, расскажите.
                +3
                Есть такая метод/функция, назовём её render или show
                При вызове render('content'); выводится переменная content либо (если она пустая или необъявлена) результат работы функции (назовём её так, это по сути не совсем функция, может быть методом класса, шаблоном, неважно) content()
                При этом, если вызвать print $users->show(), то она вернёт готовый HTML код. Вот примерный пример:
                class users_controller
                {
                   function show()
                   {
                       $users_data = User->find($id); //Псевдокод
                       print view(); //Псевдокод
                   }
                }
                

                Таким образом, начиная от main() и заканчивая users#show, все контроллеры запускают код, затем рендерят шаблон и возвращают результат, что позволяет использовать значение, возвращённое функцией контроллера для дальнейшей обработки, например, вывода в лайоуте.
                Далее, в контроллере лайоута (т.е. у меня HMVC, каждый слой содержит вложенные) основной разметки, вызывается (псевдокод)
                function main_controller()
                {
                 $content = content();
                 print main_layout(); // Аналог render('main_layout.html') в других фреймворках
                }
                

                И в шаблоне вывода лайоута (который выполняется после этого кода) запускается render('content'). Т.е. будет выведена переменная, если по каким-либо причинам её нет, запустится функция.
                Внутри content() есть свои контроллеры и шаблоны, при этом, если там будет запущено
                function content_controller()
                {
                $footer = footer();
                print content_layout();
                }
                

                , то это даст возможность переопределить переменную footer, если её нет, то всё возьмётся из шаблона footer.html.
                Также в шаблоне content() может быть следующая конструкция
                <content for footer>
                <div>Переопределенный контент</div>
                </content>
                

                Она работает через тот же самый буфер. HTML-подобные теги выбраны для подсветки парных тегов через IDE.
                Что при вызове переопределит переменную footer. Таким образом из вложенного шаблона (например, основного содержимого) можно переопределять внешний. Явный минус в том, что всё, что можно переопределить — в разных файлах.
                Описал немного сумбурно, без соблюдения используемого синтаксиса, но суть такова: вначала вызываем то, что вложено, в переменные записываем результаты, в лайоуте их используем.
                Чего тут нет — вызова не функции а сразу фрагмента на месте, как в примере статьи.
                –1
                Сейчас почему то считается стыдным на хабре писать про свои реализации и велосипед

                Вам не кажется, что это просто не профессионально? Что гораздо лучше было бы написать статью про разбор внутренностей того же самого twig'a, про его недостатки и плюсы, про его альтернативы и чем они лучше/хуже. Таким образом статья будет действительно полезной и профессиональной.

                Ну, а если уж статья про велосипед, то и велосипед должен быть хорош, во всяком случае, настолько, чтобы про него не было стыдно писать.
                  +1
                  Хабр — место для того, чтобы делиться знаниями.
                  Да, использовать самописные шаблонизаторы в крупных проектах с фиксированными сроками часто бывает непрофесиональным.
                  Но мне кажется непрофессиональным не уметь написать шаблонизатор (подобный тому, что в посте, а не какую-нибудь ересь) и уметь пользоваться twig-ом.
                  Лично мне немного жаль, что на хабре мало тем, посвящённым шаблонизаторам, реализациям ActiveRecord, собственным велосипедам, ибо мне эта тема жутко интересна. Интереснее, чем читать про twig (у него всё равно родная документация на пятёрку). А всё потому, что в первых комментариях будет либо упоминание Yii, либо картинка с троллейбусом из батона.
                    +1
                    Хабр — место для того, чтобы делиться знаниями.

                    Да, только уровень знаний бывает разный, не так ли?
                    Да, использовать самописные шаблонизаторы в крупных проектах с фиксированными сроками часто бывает непрофесиональным.

                    Порой можно написать шаблонизатор с гораздо большей производительностью, чем у уже существующих. За счёт того, что в шаблонизаторе не реализуются многие вещи, которые не нужны проекту.
                    Но мне кажется непрофессиональным не уметь написать шаблонизатор (подобный тому, что в посте, а не какую-нибудь ересь) и уметь пользоваться twig-ом.

                    Не профессионал — может написать шаблонизатор, но не сможет разобрать внутренности twig'a и тем более описать плюсы и минусы увиденного.
                    Лично мне немного жаль, что на хабре мало тем, посвящённым шаблонизаторам, реализациям ActiveRecord, собственным велосипедам, ибо мне эта тема жутко интересна.

                    В данном случае разговор идёт о «Hello World», мне кажется, что это не уровень хабра.
                      0
                      Дело вот в чем. Хабр читают люди разного уровня. Думаю, что для кого-то пост будет полезен. И уже почему-то более 60 человек добавили его в избранное. Значит, действительно это кому-нибудь было нужно.

                      Я часто вижу, как делаются представления в проектах — это либо include header & include footer либо 2-уровневый шаблон с layout и с шаблоном контента. Мне не нравятся оба подхода. Первый — за то, что в каждый файл дублирует подключения блоков. Второй — за то, что content обычно весь body, а если не весь, то теряется гибкость или приходится иметь несколько layout.

                      Идея наследования мне очень нравится. И вот я прошелся по основным методам добавить наследование в проект — от высокопрофессиональных типа Twig, до простейших — типа предложенного мной варианта. И честно говоря — я еще не видел, чтобы кто-нибудь его применял — этот простейший вариант. Возможно он не красив внешне, но поверьте — делает работу с шаблонами намного приятней и проще.
                        +1
                        Подобный поход я применял, когда рефакторил «унаследованный» «спагетти»-код, где вообще шаблонов не было — каждая из 50+ страниц начиналась с <html> и т. д., то есть даже мелкое изменение основной вёрстки приводило к необходимости менять 50+ страниц. Выглядело у меня примерно так:
                        <?php
                        $data = get_data(); // получение данных для страницы
                        $title = "Название страницы";
                        ob_start();
                        ?>
                        <p><?php echo $data['some_key']; ?></p>
                        <?php
                        $content = ob_get_clean();
                        ob_start();
                        ?>
                        <ul>
                          <li>Меню 1</li>
                          ...
                        </ul>
                        <?php
                        $sidebar = ob_get_clean();
                        ob_start();
                        require "layout.php"
                        

                        А layout.php практически такой же как у вас. Правда до необходимости трехуровневого наследования не доходило, потому в наследниках layout.php не было проверки типа isset($content);

                        P.S. В шаблонах мне больше нравится альтернативный синтаксис if (...):… endif чем if (...) {… } ^)
                          0
                          Последний ob_start не нужен :)
                          +2
                          Дело вот в чем. Хабр читают люди разного уровня.

                          Чего же мы ждём? Давайте, тогда я опубликую топик о том, как написать Hello World на bash'e?
                          Я часто вижу, как делаются представления в проектах — это либо include header & include footer либо 2-уровневый шаблон с layout и с шаблоном контента. Мне не нравятся оба подхода. Первый — за то, что в каждый файл дублирует подключения блоков. Второй — за то, что content обычно весь body, а если не весь, то теряется гибкость или приходится иметь несколько layout.

                          Не стоит забывать про partials, они очень часто спасают и избавляют от наследования шаблонов.
                          Идея наследования мне очень нравится. И вот я прошелся по основным методам добавить наследование в проект — от высокопрофессиональных типа Twig, до простейших — типа предложенного мной варианта. И честно говоря — я еще не видел, чтобы кто-нибудь его применял — этот простейший вариант. Возможно он не красив внешне, но поверьте — делает работу с шаблонами намного приятней и проще.

                          А никто не говорит, что идея наследования плоха и неказиста. Мне она тоже нравится, но её реализация в топике — именно плоха и неказиста.
                            0
                            А что вы имеете ввиду под partials?

                            К сожалению пока я не представляю, как можно сделать наследование более красиво. Разве что исполнять шаблоны в контексте класса. Тогда можно будет написать

                            /* @var $this Template */
                            <?php $this->openBlock(''content'); ?>
                            <h1>Переопределенный контент</h1>
                            <?php $this->closeBlock('content'); ?>
                            


                            а все проверки осуществлять в классе шаблона. Нужно будет попробовать. Но тогда мы придем почти к PHP TI, только с объектами. А идея простая, но честно говоря не вижу чем она сильно плоха кроме не сильно лаконичного синтаксиса.
                              0
                              Нет, вы просто неверно поняли. Partials не реализует наследование, а лишь помогает в некоторых случаях (более чем в 95%) обойтись без наследования. Например, вы можете почитать документацию по partials у symfony.
                    0
                    Я думаю многие фреймворки стоят того чтобы их изучать хотя бы чисто с теоретической точки зрения. Ведь они вбирают в себя лучшие традиции, методологии, техники, шаблоны и т.д. А вот потом, когда вы будете знать это все, можно садиться и писать свой велосипед. Но вот только будет ли у вас такое желание после изучения готовых решений. Вот в чем вопрос )
                      0
                      Ещё какое желание. Особенно если в сравнении смотреть несколько фреймфорков, видны неровности.
                      К тому же фреймфорки не заточены быть админками. Банально сделать новости на кодеигнитере чтобы администратор мог заходить под паролем и их менять, займёт около часа если не больше. И так для каждой мелочи.
                        +1
                        Ещё один пример.
                        Взять рельсы — всё здорово, но если пропробовать создать банальную форму обратной связи стандартными методами.
                        Для добавления валидаций приходится создавать модель. А это значит, что механизмы, заточенные на базу данныех, надо переопределять, чтобы они писали письмо вместо создания нового письма в базе данных. Просто так прикрутить валидатор уже как-то неровно получается.
                        Потому и рельсы не подходят для многих других задач.
                        Ещё примеры форм, где нужна валидация, но модель, записывающая данные не нужна:
                        Авторизация пользователя
                        Поиск по каталогу с валидацией формы поиска
                        Ввод имени, капчи, и последовательный переход на скрытую страницу после этого.

                        Это что касается рельс.
                        Дальше допустим Yii.
                        Для загрузки библиотек, использующих namespace по соглашению PSR-0 (например, Zend Framework 2 или Symfony2) необходимо сначала зарегистрировать корень библиотеки как псевдоним пути.

                        Для примера попробуем использовать Imagine. Скорируем директорию Imagine в protected/vendors. Ну и само использование:
                        Yii::setPathOfAlias('Imagine',Yii::getPathOfAlias('application.vendors.Imagine'));
                        // Далее стандартный код из README Imagine:
                        $imagine = new Imagine\Gd\Imagine();
                        // и т.д.
                        


                        Вроде всё клёво, но вот хорошо бы сделать сразу правильный автолоадер, чтобы сразу без подготовок:
                        // Далее стандартный код из README Imagine:
                        $imagine = new Imagine\Gd\Imagine();
                        // и т.д.
                        

                        Это что касается Yii.
                        Ну много таких моментов можно найти — и тем интереснее писать своё.
                          0
                          На счет форм я считаю, что нужно иметь 2 уровня валидации — на уровне модели и на уровне формы. Форма должна быть объектом со своими валидаторами, потому что не всегда модель данных напрямую отображается в форму и наоборот. Те валидаторы, которые работают с базой могут некорректно работать с пользовательскими данными. Да, возможно дублирование валидации, но это позволит избежать подобных проблем. На счет форм в PHP у меня есть свой велосипед :-)
                            0
                            Всё верно =) В моём велосипеде так и есть — валидация отдельно, модель отдельно. Да, минус в том, что через контроллер программист может попросить добавить неверные данные. Однако плюс в том, что если эти данные добавить всё таки надо, система не будет ставить палки в колёса.
                            Но только не всегда у формы есть модель как таковая, это может быть просто конроллер перенаправляющий.
                              0
                              В сифмони 2 можно валидировать как модель целиком, так и отдельные значения
                                +1
                                Просто ребят Вы не обижайтесь, но меня умиляет когда человек думает что он может написать своё, и оно будет лучше и круче чем то над чем работала целая команда профессионалов, архитекторов и т.д. Да в чем-то оно будет лучше, может даже быстрее, но зато у фреймворков

                                1) Готовое, оттестированная многими программистами ядро, которое просто работает
                                2) Если у вас большой проект и вы берете нового человека и он знает фреймворк, то его не надо обучать
                                3) Если у вас что-то не работает, вы нашли баг или что-то не так — issue на гитхабе обычно решает все Ваши проблемы довольно оперативно, в итоге вы занимаетесь работой, а не написанием и тестированием своего велосипеда
                                4) Огромное к-во расширений на любой вкус и поддержка комьюнити

                                Я бы ещё мог привести очень много плюсов. А что дает Ваш велосипед, кроме осознания своей крутости?
                                  0
                                  Да не везде велосипед, конечно. В качестве базового фреймворка я в последнее время использую Slim, а в качестве ORM php.activerecord. Использую я их потому, что они мне идеологически близки, и я понимаю, почему авторы сделали так, а не иначе.

                                  Что же касается Symfony2, то мы в компании сделали на ней пробный проект, чтобы понять, что это такое. И честно говоря — отгребли кучу проблем — скорее всего идеология разработчиков не совсем совпадает с нашими представлениями. Очевидно, что мы просто не научились ее готовить. Но честно говоря, я не хочу с ней связываться после этого. А как я смогу написать что-то хорошее, если мне не нравится инструмент?

                                  Конкретно про формы я могу сказать следующее — пока я не нашел ничего, что соответствовало бы моим запросам. Symfony Forms довольно близки к идеалу. Но как добавить в нее собственные контролы? И таких мелочей очень много.

                                  Кстати, не подскажете, может быть знаете какие-нибудь альтернативы для обработки форм? Мне нужно, чтобы я создал класс для формы, добавил в нее объекты контролы, засунул в них объекты валидаторы, и все это использовал бы в контроллере как в Symfony. Есть такая штука — phorms. Но мне не нравится качество исходников, активность разработки и некоторые архитектурные решения.

                                    0
                                    К сожалению по поводу форм я вам не подскажу. Использую пока тот ф-ционал который есть в Symfony 2, пока хватает. Есть классы форм. Модели биндятся, валидируются. Есть вложенные формы и т.д.
                                    0
                                    Написал выше. По определённой причине я не использую для создания блога joomla, и для создания визитки Wordpress (а хотя мог бы, и то и другое сделать можно, ядра оттестированы профессионалами, огромное количество расширений и так далее). Однако, в той же симфони, моя цитата:

                                    К тому же фреймфорки не заточены быть админками. Банально сделать новости на кодеигнитере чтобы администратор мог заходить под паролем и их менять, займёт около часа если не больше. И так для каждой мелочи.

                                    Симфони, Yii — для стартапов и блогов вроде myshows.ru, bash.org.ru хороши. По этой же причине такие вещи не делают на джумлах. Каждой задаче — своё решение.

                                    Но вот чтобы быть админками — генераторами админок встроенными не обойтись. Текстовые странциы должны создаваться за минуту, новости за пять минут, каталог за 10. И это с УЖЕ рабочей, более менее и готовой для преподнесения клиенту админкой, а не с формой авторзации на сайте с ссылками на формы редактирования с кучей валидаций.

                                    И о велосипедах. Если он достаточно качественен (а такое бывает), и сайты на нём создаются быстрее хотя бы в 3-4 раза, чем на популярных фрейморках, и новые сотрудники обучаются сходу за пару часов (ведь это не Zend с талмудами документаций), то причин не использовать не вижу. Особенно, когда являешься автором, занешь её от корки до корки, и можешь реализовывать вещи, для реализации которых на популярных фреймфорках придётся очень даже попотеть.
                                    К тому же, если самописный фреймфорк имеет много внедрений, документацию, открыт, имеет issues на гитхабе, имеет ряд готовых модулей, имеет сообщество, примеры, видеоуроки, развивается уже давно, то где грань между самописными и не-самописными? Наличие сообщества? Тогда Joomla — лучший в мире фреймфорк.
                                    Документация и поддержка? Тогда bitrix лучший в мире.
                                    Профессионализмом, тестами, объёмом и количеством разработчиков? Тогда Zend лучший в мире.

                                    Да, ктото напишет своё. Будет хуже, будет меньше оттестировано и сообщество меньше. Это всё не цели разработки. А цели — делать проекты быстро, очень быстро, чтобы работало качественно, багами не сыпало.

                                    Этот разговор будет вечным, всегда все пишут одно и тоже, и всегда одно и тоже отвечают.
                                      0
                                      Поделитесь вашим фреймворком?
                                        0
                                        Вроде на хабре не очень принято таким делиться. Последний тако пост (про CMS s3) ушёл в черновики, видимо затюкали в комментариях. Если только не указывать названия, чтобы за пиар не сочли и просто поделиться решениями.
                                        А вообще, она под GPL и на гитхабе есть.
                                        Вчера выкладывал скринкаст по созданию модуля новостей — vimeo.com/33580244
                                        Если вдруг будете смотреть — сразу предупреждаю — не судите по тому, что контролер — просто ряд функций. Есть возможность тоже самое сделать и классом news_controller и его методами роутя на метода класса или сразу на весь класс, классическим способом, или же совсем диким способом как в битриксе — в качестве контроллера страницы — просто php файл, впоследствии вызывающий шаблон.
                                          0
                                          Спасибо. Вы не переживайте так ) Просто видимо вам нравиться сам процесс написания чего-то своего, процесс создания архитектуры и т.д. Это тоже похвально. Удачи Вам
                                        0
                                        К тому же фреймфорки не заточены быть админками. Вы не путаете случайно фреймворк и CMS\CMF?
                                          +1
                                          Нет, конечно, надеюсь что не путаю.
                                          Фреймфорком я называю систему, каркас, где есть роутер, готовый механизм контроллеров, моделей, шаблонизатор либо возможность использовать внешний, валидаторы, ActiveRecord, скаффолдинг, ряд прикладных функций для упрощения жизни и возможность подключения внешних библиотек из того же Zend. В таких системах нет таблиц mos_tables или mos_catalogs_ids или mods_users_options_system_goods, только users, clients, comments, что позволяет легко получать доступ из кода к любым данным и быстро.
                                          На таких системах легко строить свякого рода CRM-системы, ToDo — листы, опросники, сайты знакомств, системы вроде хабрахабра.
                                          CMS/CMF (либо проще — админка) я называю систему, где можно дать возможность легко создать страницу, дать возможность редактировать всё, что надо администратору сайта, легко добавить какой нибудь текстовый блок на главную, запилить новости на сайте за 5 минут (в моей получается только за десять, могу скринкаст показать), какую нибудь галерею и так далее.

                                          Если внимательно присмотреться, они не взаимоисключают друг друга.

                                          То, что фрейморки (популярные) не заточены быть админками — то, что я говорил ранее. А большинство сайтов лично в моей практике именно такие.
                                          То есть с админками (какие бы они не были), проблема — текстовые страницы, новости — легко, регистрация в том виде, который предоставляют свои модули — легко, а вот регистрация с тремя абсолютно разными типами пользователей, с личными кабинетами, или вообще CRM — и начинается спагетти.

                                          Поэтому — велосипед, используется классическое MVC, упрощённое дальше некуда (чтобы не было ситуации подобной UMI.CMS, когда для создания банальной формочки приходится изучать API создания модулей, городить семь файлов, прописывать её в качестве модуля и так далее.
                                          То есть по образцу классических фреймфорков создаётся контроллер, шаблоны, модуль, выводится, а заботы по редактированию всего этого берёт на себя админка. Системных таблиц для CMS в классическом понимании вообще нет, только pages, news, catalogs, users — по названиям создаваемых моделей — админке как таковой это не мешает, зато на низком уровне данные запрашивать — самое то. По сути, конечно, админка, в которой можно сделать триаду MVC и её использовать.

                                          Другой вариант — написать такую на кодеигнитере например, или ActiveAdmin в рельсах. Но вот те, которые я встречал, сами те ещё велосипеды, превращающие кодеигнитер в, извините, какашку, что ругаются не только программисты, но и саи администраторы сайта (заказчики). А это уже критично.
                          0
                          Ребята, это прискорбно
                            0
                            Если забывать про такие вещи, PHP тоже станет считаться низкоуровневым языком.
                              +1
                              Что именно вас так расстроило?
                              +1
                              Мне как-то больше нравится резать шаблон на файлы и наследовать каталог от каталога
                              <html><body>
                                  <div><? template('header'); ?></div>
                                  <div><? template('content'); ?></div>
                                  <div><? template('footer'); ?></div>
                              </body></html>
                              

                              тут template аналог include, но с дополнительными проверками, если нет шабона в этой папке, смотрим на одну выше, а footer.php на каком-то уровне может быть такой:
                              <div>Что-то новое, еще что-то вставили <? template('counter'); ?>, и что-то унаследовали <? inherited(); ?></div>
                              

                                +1
                                Тоже очень интересно. Нужно поэкспериментировать. Кстати, есть пример реализации функции template?
                                  +1
                                  Там несложно должно быть. У нас на таком принципе была построена система с полусотней сайтов (выше основного шаблона сайта — основной шаблон дефолтового сайта).
                                  Тут основной вопрос — как роутить. Если имя папки — часть URL, то теряется много гибкости.
                                    +1
                                    Роутинг по умолчанию папками, и можно прописать любой реврайтинг урлов через регекспы и через if-ы как на уровне .htaccess, так и на уровне фреймворка.
                                    +3
                                    Есть готовый микрофреймворк Profiler, он не опубликован, (90кб весит, комьюнити не более 20чел., 40-50 проектов на нем живут всего, есть и очень крупные, например 15млн. страниц и 20тыс. хостов в день) при чем, в нем реализован inherited, которого я не встречал в других шаблонизаторах, это вставка предыдущего шаблона в наследника, если кто может рассказать какие распространенные подходы это поддерживают — очень прошу, расскажите. Если общественности будет интересно, то можно этот микрофреймворк и описать. По простоте вот посудите: например, если нужно сделать json, то создается папка c ".json" на конце имени /path/my-handler.json и в ней файл post.php такой:
                                    <?
                                    	context('res',data);
                                    	$data = application::someBusinessLogic($_POST['Parameter1']);
                                    	$res = db::queryAll('select * from MyTable where Parameter1=?',$_POST['Parameter1']);
                                    ?>
                                    

                                    Ну и так же есть упрощенная поддержка для разных маймтайпов xml, csv, ajax, есть библиотека доступа к БД, логирование, отладка, двухпроходной рендеринг (сначала отаем head, а потом начинает работать бизнес-логика, в то время, как css, js и картинки уже гружятся в браузер), и куча еще функций нужных везде.
                                      +3
                                      > двухпроходной рендеринг (сначала отаем head, а потом начинает работать бизнес-логика, в то время, как css, js
                                      Вот это идея на 5 с плюсом.
                                        0
                                        Это не моя идея, это как бы самоочевидные вещи, как и другие идеи, просто собранные в Profiler. Вот обрезал ненужное (осталось 42кб от фреймворка), кому интересно, посмотрите: http://meta-systems.com.ua/profiler320.zip
                                          0
                                          Кстати, gelas прикрутил к Profiler-у шаблонизатор Twig дополнительно и сделал систему плагинов для подключения других, например, можно быстро Смарти добавить.
                                          0
                                          Не спорю, по началу идея классная, но копнув чуть глубже понимаешь, что больше минусов.

                                          Мы не можем управлять заголовками, то есть если во время работы логики произошла ошибка или нам просто нужно отдать другой заголовок — сделать мы этого уже не сможем. Использовать сжатие при таком построении приложения тоже нельзя. Да и к тому же не все скрипты или стили можно отдать сразу, во время работы бизнес-логики может понадобится доп. скрипты стили и т.д.

                                          В итоге больше проблем, а ведь так красиво начиналось…
                                            0
                                            Если таких проблем только две — самое простое решение — отдавать отдлеьно только главную страницу.
                                            Там всегда HTTP 200, там всегда text/html, и там потенциально меньше вероятность ошибки.
                                            Далее фваскрипты и файлы стилей попадут в кеш браузера, дальше уже неактуально.
                                              0
                                              Обычно главная закеширована еще на стороне сервера, если контент не зависит от конкретного пользователя. Наибольший эффект от 2-х проходного рендеринга может быть достигнут, если время отработки бизнеслогики достаточно большое. Но в этом случае на помощь приходит AJAX — грузим страницу, а потом догружаем особо долгие блоки.
                                                0
                                                А если пользователь изначально зайдет не на главную страницу? Запоминать пользователя и отдавать в первый раз контент по этой схеме? А если отключены куки, а если это робот? А что если произойдет сбой на главной странице?

                                                Повторюсь, идея классная, но слишком много проблем.
                                            0
                                            >это вставка предыдущего шаблона в наследника,

                                            Не очень понял, что это означает.
                                              0
                                              Ну это аналог inherited в Delphi, например:
                                              procedure TMyClass.DoSomething(par:string);
                                              begin
                                              … some logic before…
                                              inherited DoSomething(par);
                                              … some logic after…
                                              end;
                                              Или в C# так: base.DoSomething(par);
                                                +2
                                                Если правильно понял, то в Twig делается так:
                                                {% block head %}
                                                    {{ parent() }}
                                                    <style type="text/css">
                                                        .important { color: #336699; }
                                                    </style>
                                                {% endblock %}
                                                
                                                  0
                                                  Точно, спасибо, это оно, а кроме Twig кто-то умеет?
                                                    +1
                                                    smarty 3.x
                                          0
                                          А что по поводу к-ва дисковых да ещё и ненужных операция в таком случае? Или этим можно пренебречь?
                                            0
                                            Так ни кто же с одной стороны не отменял кеширования готовых собранных страниц на уровне веб сервера и фреймворка, кеширования файловой системы в памяти на уровне ОС, а с другой стороны — не нужно же резать страницу на сотри файлов, в реальность файлов очень мало и все работает быстро.
                                              0
                                              ИМХО пока система укладывается в 3 сотых секунды, можно пренебречь всем, включая eval. Вон, симфони в 0.2 секунды уложилась — и то спасибо.
                                            +1
                                            кстати говоря, в smarty 3.x тоже есть наследование шаблонов.
                                            0
                                            Удивительно к каким близким результатам приходят разные люди. Я больше года назад написал настолько похожую вещь, что первая мысль была «плагиат»:
                                            pyha.ru/forum/topic/4342.0
                                            Ссылки на это я давал на хабре в темах про шаблонизацию.

                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                            Самое читаемое