# Книга для разработчиков

## Введение

Описание алгоритмов работы CMS, чтобы было понятно как писать код для нее и модифицировать.

В отличии от многих других CMS, использующих различные паттерны типа MVC (Model View Controller), Coffee CMS написана в процедурном стиле, без использования ООП (Объектно-Ориентированного Программирования) и это так и задумано. Чтобы для разработки под нее не требовалось изучать множество разных концепций. Чтобы код был минималистичным, а не разбросанным по тысячам файлов.

Взаимодействие модулей происходит через изменение глобальной переменной `$cms`. Да, это поругиваемый подход, но он позволяет сильно изменять поведение CMS без изменения кода ядра.

Существует много концепций и паттернов призванных ускорить и упростить доработки CMS, но они хороши для самих разработчиков CMS и плохи для тех кто такую CMS сопровождает. Coffee CMS в первую очередь пытается упростить жизнь сопровождающим. Именно поэтому в ней используются "плохие" практики. Если у вас возникнут предложения по улучшениям, но в рамках именно такого подхода, то оставляйте комментарии на сайте или пишите по любым другим каналам связи указанным на странице поддержки.

## Основы работы - Хуки

Вся работа движка Coffee CMS крутится вокруг одного глобального ассоциативного массива `$cms`.

В нем содержится цепочка хуков в `$cms["hooks"]`. Хук это некое событие к которому модули прикрепляют свои функции.

Основных событий четыре:

- Запрос `$cms["hooks"]["query"]`
- Шаблонизация `$cms["hooks"]["template"]`
- Ответ `$cms["hooks"]["echo"]`
- Запись страницы на диск `$cms["hooks"]["write"]`

Когда событие происходит, то все функции, прикрепленные к нему, поочередно вызываются. Когда все функции выполнятся, осуществляется переход к следующему хуку, записанному в `$cms["hooks"]["query"]["next"]`. Т.е. все хуки связаны в цепочку. Это позволяет определять логику поведения CMS, менять ее на лету и отключать ненужные хуки путем установки `$cms["hooks"]["query"]["disabled"] = true;` что вызовет пропуск хука и переход к следующему. Когда встретится пустой `next` CMS завершит свою работу.

Функциям при прикреплении можно задавать приоритет выполнения. По умолчанию он равен 10. Чем меньше число, тем раньше выполнится функция. Прикрепленная функция выглядит так:

`$cms["hooks"]["query"][5]["cms_base_connect"] = true;`

Отдельные функции можно отключать на лету, устанавливая в false.

Кроме того любая функция может сама инициировать запус хука вызовом функции `do_hook( "hook_name" );`. Так, например, делает модуль карты сайта, давая возможность другим модулям добавить в карту сайта свои ссылки.

## Основы работы - Роутинг (Маршрутизация)

Термин "маршрутизация" в контексте веб-программирования и CMS происходит от аналогии с маршрутизаторами (routers) в сетевых технологиях, где пакеты данных направляются к нужным получателям в зависимости от адресов.

Можно ли применять этот термин к Coffee CMS, вопрос пока открытый. Ведь все модули загружаются вне зависимости от URL поступившего запроса и любой из модулей может захотеть его обработать.

В зависимости от URL запроса, CMS должна вести себя по разному. Модули могут добавлять в `$cms["urls"]` регулярные выражения для сопоставления с URL вида /part1/part2 (без указания протокола и домена, но с полным путем от корня сайта) и хук, который должен запуститься. Например так:

    $cms["urls"]["^/admin_url$"] = "admin";
    $cms["hooks"]["admin"]["next"] = "template";
    $cms["urls"]["^/api_url$"] = "api";
    $cms["urls"]["^/cron_url$"] = "cron";

Это дополнительные три хука в Coffee CMS, для функционирования админки, выполнения ajax-запросов на сохранение настроек и текстов страниц, и выполнения периодических задач по таймеру.

Coffee CMS может быть установлена не в корневую папку, а в любую из подпапок и казалось бы излишне добавлять ведущий слеш /. Но это добавляет гибкости, можно с помощью CMS обрабатывать запросы /search, находясь в папке /cms/.

## Настройки CMS

Все настройки хранятся в файле `.cms/config.php` который создается при установке. Этот файл содержит экспортированную переменную `$cms["config"]` при помощи функции var_export(). Настройки каждого модуля хранятся в отдельном подмассиве с именем этого модуля. Такой подход, всмысле использование php-файла, позволяет быстро их загружать, независимо от наличия соединения с базой данных, а так же избавляет от необходимости писать парсеры и экпортеры данных. Встроенных средств php вполне достаточно. Кроме того PHP умеет кешировать `opcache.enable=1` в `php.ini` и файл находится в памяти в откомпилированном виде. При сохранении его на диск вызывается функция opcache_invalidate() которая заставит PHP перечитать файл при следующем его включении в код командой include().

## Структура папок

Все файлы CMS хранятся в папке `.cms` что позволяет в корне сайта размещать что угодно, в том числе и сгенерированные страницы. И последующее к ним обращение из браузера не запускает CMS. Файлы отдаются с диска веб-сервером.

Файлы и папки начинающиеся на точку считаются скрытыми и прямой доступ к ним запрещен. Но веб-сервер, через файл `.htaccess`, настраивается так, что всегда обращается к файлу `.cms/index.php`. Если приходит запрос `/mytpl/css/style.css`, то идет попытка найти файл `.cms/mytpl/css/style.css`, и если он есть то он копируется в корневую папку по пути указанному в запросе и отдается браузеру. Если на диске уже имеется такой файл, то он не перезаписывается. Единственная тут странность в том, что если файл был на диске, то такого запроса к CMS быть не может. Но на самом деле может, если два запроса поступили почти одновременно и файл еще не успел создаться. Но ничего страшного не произойдет, второй файл перезапишет первый. Создание файлов в корневой папке происходит атомарно и веб сервер не может отдать частично записанный файл.

## Меню в шаблоне

В админке можно настроить каждое меню, в какую область его выводить. Список областей и переводы на разные языки задаются в файле `template.settings.php` лежащем в папке шаблона. Пример того как это делается можно подсмотреть в шаблоне mini в файле `.cms/mini/template.settings.php`.

Если вы хотите сделать сайт на разных языках, в шаблоне проверять условия и в зависимости от условий выводить меню на русском `menu( "header_en" );` или меню на английском `menu( "header_en" );`, то достаточно добавить эти строки. Шаблон сканируется на эти вызовы функций и определяются имена областей.

## Как происходит процесс обновления

В модуле admin.mod.php есть функция cms_update_cms_update() и алгоритм ее работы такой:

Сначала она проверяет что есть информация о новых версиях, полученная при нажатии на кнопку "Проверить" в разделе "Модули". Что прошла хотя бы минута с последнего нажатия на кнопку обновления. Что присутствует файл .cms/filelist.php, в котором перечислены файлы, их размеры и контрольные суммы.

Затем идет еще две проверки что новая версия больше чем текущая, просто сравниваются две строки. Вторая проверка - совместимость новой и текущей версии. Изредка CMS будет терять обратную совместимость и в этом случае, чтобы не сломать сайт, обновление с помощью кнопок в админке будет отклонено.

Затем идет проверка текущих файлов на изменения. Если хотите обновить измененный файл, то достаточно в .cms/filelist.php поставить пустую sha256.

Следующий этап - проверка прав файлов, иначе могут быть ошибки при удалении старых файлов.

Затем скачивается новая версия, если существует папка .tmp она удаляется и пересоздается. Скачанный .zip файл записывается в эту папку. Распаковывается в эту же папку.

Затем загружается файл .tmp/.cms/filelist.php и проверяются контрольные суммы файлов.

Старые файлы CMS удаляются по списку, но так же проверяется не находится ли файл в списке разрешенных к изменению и что sha256 файла изменилась. Если это так, то это означает что пользователь внес изменения и их нужно оставить, а не закидывать новый файл. Если изменений нет, то даже разрешенный к изменению файл будет обновлен.

Затем файлы копируются из папки .tmp в папку .cms, но при этом так же проверяется не существует ли файл в папке .cms и не изменена ли sha256 и нахождение его в списке разрешенных. Если это так, то копирование пропускается, иначе происходит копирование.

Если в процессе возникнут ошибки, то пользователю об этом будет уведомление. Затем папка .tmp удаляется.

Считывается файл .cms/update.sql и выполняются запросы к БД из него. Это позволяет изменить базу данных под новые возможности.

Иногда могут потребоваться более сложные действия чем закидывание новых файлов, для этого существует файл .cms/update.php. Если он существует, то загружается и соответственно исполняется.

Затем идет установка переменной $cms["config"]["admin.mod.php"]["clear_cache"] = true; Чтобы в процессе перезагрузки админки после обновления еще раз было произведено удаление статических файлов сайта с диска. Для какого случая это нужно - нужно еще вспомнить. И прямо сейчас тоже выполняется очистка от статических файлов.

Пользователю отправляется сообщение что обновление успешно произведено. Админка перезагружается спустя 5 секунд когда сообщение исчезнет.