Вперёд, в будущее!
Вперёд, в будущее!

Начало

Да, именно так: я начинал писать основу PHP движка в 2001-ом году. 

Тогда всё было проще: каталог inc/, в нём header.php, footer.php, common.php.

<?php

include 'inc/header.php';
include 'inc/common.php';
?>

Вот тут самое интересное.

<?php
include 'inc/footer.php';
?>

Но это было скучно и неинтересно, надо было ещё больше фишек, которые я бы мог предложить заказчикам. Так появился раздел admin/. Но примерно в тоже время  появился PHPNuke, это была бомба! Такой уровень кастомизации! Я понял, что ничего подобного я предложить не смогу, но и по требованиям мой движок был куда как проще, хотя позволял организовать каталог товаров, список статей, галерею. Несколько лет я дальше двигался по основной своей работе: администрирование Linux-серверов. 

Но как-то мне предложили работу в фирме именно PHP-разработчиком. Я ознакомился с проектом, и меня порадовали две вещи: 

  1. Все шаблоны в своём формате.

  2. И они вместе с настройками лежат в базе данных.

<div id="block_news">
<div class="block_header">Новости</div>
<div class="block_shadow"></div>
<div class="block_content">
[%loop_begin%]
<div class="news_title">[%row(title)%]</div>
<div class="news_date">Дата:  [%row(date)%]</div>
<div class="news_content">[%func(get_news_short_content)%]</div>
<a href="[%SUBDIR%]news/index.php?show=[%row(id)%]" class="news" title="[%row(title)%]">Подробнее >></a>
[%loop_end%]
<div align="center"><a href="[%SUBDIR%]news/index.php" class="button">Все новости</a></div>
</div>
<div class="block_end"></div>
</div>

Это же прекрасно! Какая гибкость! Ну и пусть на открытие заглавной страницы уходит 50+ запросов! С таблиц, в которых и 100 строк бывает редко, MySQL делает выборки мгновенно.

От наследия той работы я до сих пор не могу избавиться: например меню в моей CMS строится именно на основании данных с БД, но есть кэширование. Так же до сих пор поддерживается мной придуманный формат шаблонов, хотя вряд ли когда-нибудь я им воспользуюсь.

Ещё с той работы я привнёс в свою работу Subversion: это было круто! Пара команд - и мой домашний репозиторий с рабочим синхронизирован за бесплатные  60 секунд через 8w180! А когда начальство говорит что я нифига не делал то легко подвести статистику добавленных/изменённых строк! Работал я тогда, как правило, сдельно

Продолжение

Так я "проспал" появление AJAX, появление ООП в PHP, хотя последнее я просто  не воспринял: зачем, когда у тебя в inc/ теперь уже не три файла, а пять, но всего пять и всё работает? 

И да, примерно в то время благодаря одному из своих друзей я поставил на основной сервер Gentoo. Это было круто! Совсем другой уровень управления сервером! Когда на основном сервере стоит и абсолютно правильно работает Gentoo - на админов всяких Редхатов и Дебианов смотришь известно как :)

А как программист я стагнировал :( Переломным моментом должен был стать сайт для моих друзей с продвинутым каталогом товаров: тогда количество костылей в catalog/index.php переполнило все мыслимые пределы. Там на несколько лет появилась строка

$input["list_id"];

И всё. Просто написал! Ничего ничему не присваивается, никак не обрабатывается!

Но я несколько лет не удалял эту строку, она просто была незаметна на фоне огромного спагетти кода подпёртого костылями.  

И тут очень хороший человек предлагает для его сайта сделать весьма интересный функционал. Ладно, я готов к новым испытаниям. Я тогда не знал, что такое Битрикс... Специалисты по нему, увидев мой код, могут упасть в обморок: я писал исключительно прямыми запросами к базе данных через $DB->Query. Часто они бывали весьма многострочными, но это всё равно было лучше, чем использовать недоORM Битрикса. И да, сколько вечеров было проведено, пока мы с другим очень хорошим человеком разгребали почему штатная выгрузка с 1С не работает! И тогда был курьёзный случай: я уже в модуль ядра полез, чтобы логирование добавить – чтобы узнать на каком именно элементе каталога стопорится выгрузка, коллега подходит и говорит: Игорь, это же нифига не английский!? И тут я понимаю, что это, блин, немецкий: функции и переменные в XML парсере Битрикса на немецком! А я по запарке даже и не заметил! Хоть раз в карьере пригодился язык, который я в школе и институте учил. Проект был выполнен, но сейчас, к сожалению, он оказался никому не нужен.

Позднее, когда пара людей мне предложили развить интересный проект на Yii1, я понял, как много я упустил за эти годы! Но свою CMS я не трогал особо: и страшно и вроде как не нужно. Проект на Yii1 в итоге не взлетел, но это был бесценный опыт. И когда мне предложили сделать свой, достаточно серьёзный проект, с нуля я недолго раздумывал: Yii2. С базой данных у меня не было сомнений: MariaDB, ибо с MySQL я, будучи сисадмином, научился делать EXPLAIN, чтобы запрос вместо трёх минут выполнялся меньше секунды. 

Прозрение

И тут я споткнулся об реальность: я же толком никогда ООП не использовал в PHP. Спасибо видеоурокам Дмитрия Елисеева и шаблону приложения от Vova07. За пару месяцев я написал CRUD'ы для админского раздела, разумеется используя миграции и RBAC, дальше сам разобрался как сделать REST API, разбил всё на модули и так далее. Но сегодня не об этом.

В своей CMS всё так же несколько файлов в каталоге inc/. Для приличия переименовал в include/. Поломал обратную совместимость :( На сервере ln -s ./inc ./include, но даже полному идиоту понятно, что это нереальный костыль :( Собираю и раскидываю функции по include/lib_*, лезть в скрипты, касающиеся каталога, боязно. Но тут опять же спасибо Дмитрию Елисееву: его курс про микрофрэймворк стал определяющим: нафиг свой шаблонизатор, когда есть Twig? Какие ещё отдельные JS скрипты и CSS файлы, когда благодаря laravel-mix можно в пару команд собирать бандл? Причём вместо CSS использовать SASS, который для меня стал небольшим, но всё же открытием, и я за минут двадцать все стили перевёл на него. 

А тесты!.. Это отдельная тема. С начала своей карьеры как сисадмина я видел, что в руководствах часто между make и make install бывает make test. Как долго идёт ./configure с ключами и при этом много что тестируется, какие ещё тесты нужны? Но тут попробовал - и понравилось! Меняю что-то в ключевых классах, запускаю composer test - и как приятно видеть, что они проходят! А если не проходят, то сразу понятно, где я что поломал. К сожалению, связность в моей CMS очень сильная, TDD я попробовал, но этот подход при такой связности скорее замедляет разработку, чем помогает.

В итоге решил переписать 100500 костылей под нормальную архитектуру.

Решение

Это было непросто. Особенно морально: сисадмин внутри меня даже не говорил - кричал: "Работает? Не трожь! Обратную совместимость поломать решил? Идиот, сам же будешь чинить!". Но жребий был брошен, Рубикон перейдён: начав с самого простого: модуля статей, я в итоге переписал все модули, даже модуль каталога товаров. Да, я кодом недоволен и сейчас: до чистого кода далеко. Но код ради кода - это не то, к чему я стремлюсь: например DI контейнер в такой простой задаче будет перебором. Хотя кроме DI контейнера можно многие практики привнести, например, использовать request, response, log, cache, да и сам контейнер по PSR, но я пока не вижу смысла тащить такие зависимости в проект :) Ведь чем меньше зависимостей тем проект надёжнее. Но для миграций я добавил в проект Phinx, который за собой притянул  symfony/console и фреймворк Сakephp. Первый грех было не использовать а с Сake я в итоге взял модули кэширования и логер: зачем в очередной раз писать свои костыли или тянуть зависимости если эти компоненты и так уже в проекте?

Итог

В итоге это был непростой путь, но я доволен результатом. Конечно, впереди ещё многое предстоит сделать, но уже сейчас я готов поделиться своими наработками с сообществом. Жаль, что заказчики не видят разницы между набором костылей и более чистым кодом, в который можно вносить изменения, не опасаясь выбить карту на которой весь костыльный домик держится. Но оно того стоило, однозначно!

Ах, да: GitHub