Подключаем шаблонизатор Fenom

Знаю-знаю, мы собирались работать на чистом PHP, безо всяких фреймворков, но о шаблонизаторах речи не было!
Если серьёзно, то я по всякому прикинул, как заставить наш простенький сайт выводить HTML, и не вписывать его в PHP, а хранить в шаблонах.
Варианта ровно два: написать собственный жуткий глючный велосипед, который будет читать шаблон и заменять в нём плейсхолдеры на значения через str_replace(), или подключить нормальный шаблонизатор.
Так как мы все тут от MODX люди не очень далёкие, так почему бы не освоить работу с Fenom, который в нём с некоторых пор доступен? Думаю, возражений не будет, так что поехали!

Зачем?

Для начала давайте определимся, зачем нам вообще нужен шаблонизатор? Можно же писать HTML прямо в PHP и выводить его через echo?
Можно, конечно. Но такой код будет очень сложно развивать и поддерживать, даже если вы работаете над ним в одиночку. А если с вами будет работать еще и дизайнер\верстальщик, то ему придётся выучить PHP и разобраться в вашем коде, чтобы вносить изменения.
Подключение же шаблонизатора позволит очень просто менять внешний вид сайта, используя его синтаксис. Знания PHP не нужны, а код и представление разделены.

Подключение

Распаковываем архив и переносим всё из директории scr в нашу Core. Так как Fenom придерживается PSR, то свою директорию называет с большой буквы. Чтобы не ломать единообразие, переименуем и нашу директорию controllers в Controllers.
Заодно порадуемся тому факту, что путь к контроллерам у нас задан в конфиге класса Core под ключом controllersPath - указываем заглавную букву и там. Код на GitHub сейчас такой.
Теперь нам нужно как-то добавить работу с Fenom в наш класс Core. Пишем для этого отдельный метод getFenom():

    public function getFenom() {
        // Работаем только, если переменная класса пуста
        if (!$this->fenom) {
            // Пробуем загрузить шаблонизатор
            // Все выброшенные исключения внутри этого блока будут пойманы в следующем
            try {
                // Подключаем класс загрузки
                if (!class_exists('Fenom')) {
                    require 'Fenom.php';
                    // Регистрируем остальные классы его методом
                    Fenom::registerAutoload();
                }
                // Проверяем и создаём директорию для кэширования скомпилированных шаблонов
                if (!file_exists($this->config['cachePath'])) {
                    mkdir($this->config['cachePath']);
                }
                // Запускаем Fenom
                $this->fenom = Fenom::factory($this->config['templatesPath'], $this->config['cachePath'], $this->config['fenomOptions']);
            }
            // Ловим исключения, если есть, и отправляем их в лог
            catch (Exception $e) {
                $this->log($e->getMessage());
                // Возвращаем false
                return false;
            }
        }

        // Возвращаем объект Fenom
        return $this->fenom;
    }
Как видите, загрузка Fenom происходит только один раз, потом Core будет отдавать уже инициализированный экземпляр. Также по коду видно, что добавились и новые параметры в настройки, и метод log(), для вывода ошибок.
Предлагаю всё это посмотреть вам на GitHub, вместе с нашим первым шаблоном в /Core/Templates/home.tpl:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Третий курс обучения на bezumkin.ru</title>
</head>
<body>
    <h1>Привет, мир!</h1>
</body>
</html>
Который выводит контроллер Home:

    public function run() {
        // Метод getFenom() может вернуть или false, или объект
        // Так что нужно проверять, что именно приходит
        if ($fenom = $this->core->getFenom()) {
            return $fenom->fetch('home.tpl');
        }
        else {
            return '';
        }
    }
Скелет шаблонизации готов, можно работать дальше.

Шаблоны

Предлагаю теперь нам еще подключить Bootstrap для оформления страниц и нарисовать простейший шаблон, которым будут оформлены главная страница, и test.
Качаем последний Bootstrap, распаковывем и кладём в директорию /assets/. Вот состояние нашего репозитория с подключенным Bootstrap.
Дальше предусматриваем в шаблоне переменные Fenom, которые он потом заменит на полученные от контроллера значения:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{$pagetitle}</title>
    <link rel="stylesheet" href="/assets/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h3>{$longtitle ?: $pagetitle}</h3>
        {$content}
    </div>
</body>
<footer>
    <script src="/assets/js/jquery-2.1.4.min.js"></script>
    <script src="/assets/js/bootstrap.min.js"></script>
</footer>
</html>
После чего в контроллере Home меняем метод run() вот так:

    public function run() {
        if ($fenom = $this->core->getFenom()) {
            return $fenom->fetch('home.tpl', array(
                'pagetitle' => 'Тестовый сайт',
                'longtitle' => 'Третий курс обучения',
                'content' => 'Текст главной страницы курса обучения на bezumkin.ru',
            ));
        }
        else {
            return '';
        }
    }
Как видите, наш шаблон уже умеет проверять longtitle на пустоту и подставлять вместо него pagetitle, если нужно. Пользуемся этим в методе run() контроллера Test:

    public function run() {
        if ($fenom = $this->core->getFenom()) {
            return $fenom->fetch('home.tpl', array(
                'pagetitle' => 'Тестовая страница',
                'longtitle' => '',
                'content' => 'Текст тестовой страницы курса обучения на bezumkin.ru',
            ));
        }
        else {
            return '';
        }
    }
Вот мы и немного оформили наши две страницы с помощью шаблонов Fenom и Twitter Bootstrap.

Заключение

На следующем уроке мы сильнее погрузимся в шаблонизацию и научимся работать с синтаксисом Fenom.
Попробуем вывести панель навигации по сайту, написать отдельный шаблон для страницы Test и выделить общие элементы двух шаблонов в отдельный файл, чтобы не копировать один код (head, footer).
На данный момент наш сайт выглядит вот так.