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

Отделение данных от php кода

Бытует мнение, что отделение представления (html) от php-кода спасёт мир (ну, или хотя бы программиста). Такое отделение нужно, но толкуется оно чаще всего превратно. Например, как можно удалить html из кода, формирующего галерею изображений? Предполагается, что тут поможет Шаблон: код php в цикле перебирает Данные (наименование файла картинки, описание картинки) и достаёт откуда-то текст шаблона, в котором заменяет некоторые именованные фрагменты Данными. Текст шаблона может быть примерно таким:

Шаблон такой обычно храниться в отдельном файле. И вот, php-функция читает этот файл, потом с помощью preg_replace заменяет в тексте фрагменты с условными метками на соответствующие Данные. Это называется "отделять HTML от php". Это даже не смешно. Тэги действительно находятся ну совсем отдельно, вообще в другом файле. Но прикованы они к коду толстенной ржавой цепью – явными наименованиями полей. Существуют, конечно, и варианты "без имён", например, такой:

Как видно по знаку процента, такой вариант рассчитан на использование функции sprintf. Связка с кодом менее жёсткая, но она всё равно есть: функция точно должна знать количество этих знаков процента, иначе кранты. Но главная проблема даже не в этом, она появляется, когда нам нужно добавить в шаблон, например, ссылку на страницу редактирования элемента галереи. Эта ссылка не должна быть в шаблоне всегда; она должна появляться, только если пользователь авторизован. И вот во что превращается наш "отделённый от php" шаблон:

А дальше ещё хуже: в шаблоне появляется FOREACH. В итоге шаблон превращается во фрагмент достаточно сложного кода, но только без метки "<php" в начале. И всё ради того, чтобы торжественно отрапортовать: мы отделили HTML от php! Можно попытаться добавить условную ссылку как более крупный блок хотя бы в стиле того же sprintf:

А в функции, использующей шаблон, определять первое %s как пустую строку, если пользователь не авторизован. Но тогда (вот незадача) внутри функции придётся написать тэги. А если строго следовать религиозным предписаниям, нужно будет завести ещё один, отдельный ФАЙЛ шаблона с таким содержанием:

Когда количество таких файлов перевалит за сотню, мы не выдержим, плюнем на "теорию отделения" и запихнём все "однострочные" шаблоны в массив:

Ну, и, оценив тормоза при работе с НЕ-php файлами, в итоге вообще поместим все оставшиеся шаблоны внутрь функций (и вопрос об их "активности" станет чисто академическим). Знаю, знаю: возникнет вопрос, как же бедный верстальщик будет верстать свою вёрстку внутри php? Да так же, как и раньше – в отдельных файлах, из которых шаблоны будет забирать программист. Такая "ранняя компиляция".

***

Есть более важный водораздел: практика показала, что удобство использования CMS очень сильно возрастает, если стремиться максимально отделять от кода данные. В таком разрезе можно наладить и отделение представления от вычислений более точно. Если, конечно, согласиться рассматривать шаблоны в качестве данных.

Строго говоря, данными можно делать в Приложении что угодно. Данные – это то, что хранится в явном виде (1) и что можно изменять, редактировать (2). Вот откуда, например, функция получения Данных в классе Mysql берёт параметр limit (если он не указан при вызове)? Да из массива Def::$options, который хранится в файле options_xml.php (ключ menu_length). Пользователь (администратор) может изменять этот параметр через визуальный интерфейс.

Казалось бы, отделять данные от кода так же смешно, как и отделять HTML от php: ведь код постоянно работает с данными. И в том, и в другом случае важно не абстрактно "отделять" (например, разносить в разные файлы), а обнаруживать, осознавать: вот это – код, а вот это – данные, с которыми код работает; вот это – HTML... С HTML проще, его всегда можно обнаружить по открывающей угловой скобке. Понять, что какой-то фрагмент Приложения можно рассматривать в качестве данных, сложнее.

Фактически речь идёт о логике Приложения, о том, чтобы максимальное количество логики фиксировать в виде данных – обнаруживать некоторые правила, алгоритмы внутри функций и выносить их из функций в отдельные управляющие массивы. А отдельные массивы уже можно делать доступными для изменения через визуальный интерфейс CMS.

Можно сказать, что логика – это совокупность зависимостей Приложения. Или совокупность связей системы (где система – множество всех элементов Приложения). В таком понимании, логика – это структура Приложения. Структуру можно изменить, улучшить, если большинство связей системы обнаруживать и фиксировать в виде данных, делать зависимости явными. Эти действия, кстати, уменьшают связанность кода, повышают удобство «повторного использования».

Как уменьшить связанность кода

Общий алгоритм примерно такой: найти неявные зависимости и создать из них данные, хранимые отдельно (и явно). Самый простой признак любой зависимости – это как раз наличие данных (в строках, заключённых в кавычки). На втором месте – имена переменных (причина их появления).

Пример. Допустим, мы решаем, куда на обработку передать поступивший http-запрос. Мы разобрали УРЛ, и получили, в частности: $filename="about_us.html", $extention="html". Дальше у нас такой фрагмент кода:

То есть все УРЛ, заканчивающиеся на .html, мы рассматриваем как идентификаторы статей и пытаемся вывести пользователю содержание запрошенной статьи. И вот в этом коротеньком фрагменте у нас уже есть "неоприходованные" данные, появившиеся необъяснимо, "с потолка". Это – слово в одинарных кавычках, то есть 'html'. Мы не сможем использовать этот код "повторно", если на другом сайте решим сделать расширения для статей не 'html', а, скажем, покороче – 'htm'. Это и есть зависимость. Возникшая вдруг ни с чего, в середине функции.

Даже если мы не будем ничего менять на другом сайте, проверка расширения ведь используется в каких-то других местах программы: в базе данных идентификаторы статей хранятся без расширений (просто 'about_us'), и нам надо, как минимум, а) отсечь расширение от имени файла при поиске в базе, б) добавить расширение к ссылкам при генерации меню. Во всех случаях мы должны помнить этот фрагмент текста – 'html', а это же очень сложно!

Гораздо проще будет запомнить путь к Конфигу, где это (или любое другое) расширение будет доступно всем:

То есть это как бы ирония, Def::$options['ext']['ext_articles'], конечно, сложнее, чем просто 'html'. Где-то в недрах массива настроек мы создали новую именованную Сущность, содержание которой теперь можно произвольно менять. Гибкость приложения повысилась, но код усложнился. Никуда не денешься. Хотя, если подумать, в последнем примере мы почти ничего не выиграли: вместо одной "неявной" строки "html" ввели другую – "ext_articles". Код надо ещё больше усложнить – заменить "имя с потолка" на переменную, опираясь на соответствующую структуру конфига.

В конфиге массив ext должен иметь следующий вид:

То есть ключами в массиве ext должны быть "зарегистрированные" расширения, а значениями – наименования сущностей. Тогда при вызовах внутри исполняемого кода мы можем выкинуть "непонятные константы" типа ext_articles и заменить их переменными, примерно так:

Заодно мы вынесли в данные конфига часть логики самого кода: теперь вместо десятка возможных if'ов у нас только две строки, а "условный выбор" делается простым обращением к ключу массива ext. Контроллер стал более "тонким", а данные (хранимая конфигурация тоже ведь данные) – более толстыми.

Дилетант

Комментарии

лесник 29.07.2012 00:07:58

И, кстати, в последнем примере мы всё равно получили волшебное слово – 'ext'.