Анонс и история Explay CMS 3 (Core)

    Explay

    Немного истории



    Где-то в конце августа — начале сентября, постепенно понимая парадигмы объектно-ориентированного программирования и приходя в ужас от своего старого кода, я решил забыть Explay 2.1 как страшный сон и взялся писать все с чистого листа. Да, это, вероятно, было не самым лучшим шагом в сторону пользователей Эксплея, но вы уж извините мою глупую натуру.

    Основная идея Explay 3 — это дать разработчику сайта полный контроль над типами данных: добавить к статье 10 полей, необходимых именно на этом проекте или же вовсе ввести новый тип данных, например, подарки а ля Вконтакте. При всем этом разработчик, внедряя новые типы, не должен тратить время на написания PHP-кода или собственных модулей. В основном из-за такой универсальности система за несколько месяцев разработки имеет только ядро и довольно нелегкое.

    В этот раз воплощение планов по захвату мира мыслей началось не с index.php, а с чистого листа бумаги. Исписав несколько листов вдоль и поперек, я определился со структурой БД. Сама же идеология была ясна заранее: основная сущность — объект, а объекты классифицируются по типам. Таким образом к концу первого этапа в базе данных появилось 6 обязательных/главных/исходных таблиц, описывающих всё и вся, в том числе сами себя.

    В проектировании БД мне очень помогла утилита MySQL Workbench 5.0 OSS.


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

    С декабря я стал почти свободным человеком и вплотную занялся движком — разработка пошла быстрее. Таким образом сейчас представляю вам ядро движка Explay CMS 3. Работоспособность, примерно, 70-80%.

    Кто-то может видел мои попытки рассказывать о процессе написания движка, но, к сожалению, меня снова надолго не хватило :(


    Технические характеристики



    Код



    Код полностью на ООП. ООП используется на всю катушку со всем вытекающими плюсами и минусами. Ядро CMS — это 24 класса: контроллеры и объекты представляющие их записи в БД — ORM (поля, типы и простые объекты). Все без исключения методы уже комментированы для дальнейшего составления документации.

    Лирическое отступление:

    Три основополагающие сущности движка — это три вида объектов: объект типа, объект поля и обычный объект.

    Тип (Type) — это категория (вид) обычных объектов. В физическом смысле — это таблица, в которой и хранятся объекты определенного типа.
    Поле (Field) — это описание свойства объекта. В физическом смысле — поле (столбец) таблицы. Но поле не всегда может описывать столбец таблицы типа, которому оно принадлежит; поля могут быть связанные («JOIN» свойства другого типа) и справочники (селекторы, значения которых хранятся в отдельной таблице).
    Объект (Object) — это запись в таблице его типа.

    Для получения объекта необходимо знать его тип и id. Все свойства объекта описаны полями.


    MVC



    Паттерн «Модель-Представление-Контроллер», к моему счастью, оказался в проекте не как дань моде, а как потребность. Благодаря нему на модель/бизнес-логику/модуль уходит мизерное количество кода. Как следствие красота кода и низкая стоимость разработки.

    ModuleResponse — обязательный объект «ответа» вызванного метода модуля. Он содержит данные (объекты), которые будут представлены в виде XML-таблицы.


    image
    Рис. 1. Жизненный цикл страницы

    Шаблоны



    В качестве стандартного шаблонизатора используется XSLT. Тут говорить особо нечего, красиво :)

    Реализация на уровне PHP-кода: необходимый шаблон задается объекту ответа модуля (ModuleResonse) в странном формате: относительный путь до xsl-файла без расширения (предполагается, что шаблон может быть и tpl). Например в my::__default(): $response->setTemplate ('my/__default');

    Если метод модуля вызывается из адресной строки, например example.com/module/method, то шаблон указанный в его ответе будет использоваться как «точка вхождения».

    К слову, благодаря макросам и модулям, не требующи никакой установки (за исключением, есть они связанны с какими-либо типами), последние могут выступать как плагины, подключаемые из шаблонов.

    Для упрощения жизни разработчика и верстальщика были введены макросы. Синтаксис: {{имя_модуля:: название_метода(параметры)}}. Шаблон, собранный согласно ответу указанного модуля, будет просто вставлен на место макроса. Может, это не самый красивый вариант сборки шаблона, но он явно быстрее подгрузки XML-таблиц прямо в XSLT через document().

    Высокие нагрузки и кеширование



    Поскольку CMS ориентирована на создание социальных сетей и интернет-сообществ, то кеширование — это все. Идеология кеширования — сохранение целых объектов, которые, к слову, никогда не будут устаревшими, т.к. при их изменении сбрасывается соответствующий кеш.

    Архитектура движка не позволяет выбирать несколько объектов одним запросом к БД — для каждого объекта свой запрос. Здесь сидит самая главная проблема и самая вкусная изюминка. Например, у нас 100 комментариев к статье (100 запросов + 1 некешируемый на выборку списка id). При выключенном кешировании у нас всегда будет 101 запрос (обоги), а при включенном — минимум 1. При изменении одного комментария модератором — минимум 2, то же самое при добавлении нового объекта. Таким образом, имея быстрый движок кеширования мы получаем довольно гибкую и относительно легкую систему. А вот использование CMS без кеширования я бы рекомендовал только для домашней разработки.

    ~/explay/classes/CacheController — фронт-енд контроллер кеша. В комплекте файловый и Memcache бэк-енд.

    Возможно различное время жизни для обычных и системных объектов (прим. автора).


    Системные требования



    Обязательные:
    • PHP 5.x
    • XSLT
    • MySQL с поддержкой InnoDB (желательно 5.1)
    • mb_string
    • mod_rewrite

    Рекомендовано:
    • Memcahced


    Запуск



    Текущая версия в SVN скорее для ознакомления с кодом и собственно архитектурой в целом.

    В корневой папке лежит файл dump.sql — его в базу данных. Настройки в explay/config.php. Рабочий модуль my.

    Основное правило ЧПУ: example.com/имя_модуля/имя_метода/param0/param1/.../paramN
    example.com/something/uri/to/hell.xml — выдаст Вам XML-таблицу ответа модуля :)
    example.com/something/uri/to/hell?debug — немного Debug-информации.

    Цель этой статьи



    Данная статья — это всего-лишь ознакомительное описание к CMS. А Хабрахабр — это, пожалуй, единственное место, где я могу услышать независимые и профессиональные отзывы и мне они очень нужны. Так же я хотел бы понять, будет ли кто-нибудь работать с ней в будущем. Критика, советы, деловые предложения приветствуются.

    Посмотреть код в SVN

    Спасибо за внимание!
    Поделиться публикацией

    Похожие публикации

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

      –5
      под кат пожалуйста
        +6
        спасибо, исправился :)
          0
          Из под ката, пожалуйста.
        0
        Анонс и исторя

        А в заголовке-то буковку «и» забыли :)
          +5
          спасибо, представляю что в коде :)
          0
          >Например, у нас 100 комментариев к статье (100 запросов + 1 некешируемый на выборку списка id)
          Жесть… 1 запросом никак не получиться?
            0
            В кеше лежат отдельные объекты, а при выборке одним sql-запросом мы не знаем какие объекты закешированы, а какие нет — теряется смысл кеша.
              0
              >а при включенном — минимум 1
              >При изменении одного комментария модератором — минимум 2

              Дык всё равно будут запросы к базе будут, я бы не парился и выбирал бы 1!
                +4
                И все таки здесь лучше делать запрос на каждый коммент. Я так понимаю, после записи в БД нового коммента он сразу же и отображается => кешируется и потом не меняется.

                Вцелом explay мне не очень понравился (прости, о Автор!), но сам подход очень радует. Спасибо Автору за старания.
                  +1
                  Ой не факт…
                  Подобная стратегия работает хорошо если у вас памяти под кэш немеряно, да и то… очень уж разные ситуации бывают и жёстко оставлять только один вариант — могут быть большие проблемы.
                    +1
                    Памяти под кеш всегда «немеряно» — память очень дешевая, мемкеш очень легко масштабируется. Обычно в это не упирается. А вот в количество запросов (по 1 на каждый комментарий) — как раз можно и упереться.
              • НЛО прилетело и опубликовало эту надпись здесь
            • НЛО прилетело и опубликовало эту надпись здесь
                –2
                Считаю это правильнее, нежели писать шаблонизатор на шаблонизаторе. Тем более возможно прикрутить тот же Smarty.
                  +1
                  странно, если вы к пхп относитесь как к шаблонизатору, почемы вы до сих пор, пишите на нем подобные проекты. =)
                0
                ничего не понял… отличие от старой версии только во внутренностях?
                  +2
                  старая еще и работала, а эта пока не совсем :)
                    0
                    да, я тоже мучаюсь — никак не могу заставить работать уже битый час =)
                    0
                    это отнюдь не ужасно :)
                    +4
                    > Архитектура движка не позволяет выбирать несколько объектов одним запросом к БД — для каждого объекта свой запрос.

                    Мне кажется, что это ужасно. Когда у меня был приступ «давайте сделаем универсальный контент-репозитарий», этого удалось избежать с помощью небольшой денормализации (если память не изменяет).
                      0
                      Я бы с большим интересом послушал об этом :)
                        +1
                        хмм. позырил код в транке и то, что там есть сейчас — не для простых людей. не смог я полностью разобраться во всех таблицах этого «объектного» хранилища. но название класса «Object» очень понравилось :)
                        когда будет документация, мне кажется, можно будет что-то посоветовать в плане кеширования на уровне БД.

                        кстати, хорошая модель контент-репозитария у системы ez publish ( ez.no/doc/ez_publish/technical_manual/4_0/concepts_and_basics/content_management/datatypes ) у меня однажды был такой классный велик для хранения контента, а она мне все колеса отшибла ;-)
                      +3
                      Вы молодец! Хватило хотя бы даже и того, что вы проект не бросили до сих пор, чтобы порадоваться.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          +3
                          Обязательные:

                          mb_string

                          0
                          Как пользователь могу пожелать удачи. Ваша CMS нравилась и раньше, а теперь, надеюсь, будет нравится еще больше.
                            +9
                            Думаю дальше ужаснешься от текущий архитектуры и начнешь использовать фреймворки. Узнаешь про паттерны. А дальше может и python или ruby начнешь смотреть. Это нормально, главное не зацикливайся на каждом уровне больше 4-6 месяцев.

                            P.S. А много абстракций ни к чему хорошему не приводят. По крайне-мере на производительности это отражается в худшую сторону.
                              0
                              Не сочтите за завышенную самооценку, но про паттерны и фреймфорки я знаю, а паттерны еще и умудряюсь применять. Про Python & Ruby спорить не буду, но мне пока PHP & C++ хватает :)
                                +1
                                По поводу абстракций да, но если есть возможности, почему бы их не использовать? Мое мнение: лучше потратиться один раз на мощный сервер, а не платить постоянно огромные деньги за разработку.
                                  +1
                                  До определенного предела это окупается. Но по мере роста нагрузки становится выгоднее оптимизировать код, чем покупать железо. Представьте себе масштабы Яндекс или vkontakte, например.
                              • НЛО прилетело и опубликовало эту надпись здесь
                                  0
                                  Вам теперь надо XSLT к PHP подключить
                                    +4
                                    [offtop]
                                    > Fatal error: Class XSLTProcessor or him interface not found!
                                    А почему «him»? Если я правильно понимаю, то «its» или хотя бы «his»?[/offtop]
                                  0
                                  Вообще конечно похвальное увлечение, но по сути получается велосипед, причем далеко не лучший.
                                  ИМХО, фреймворки не дураки придумали.
                                    +8
                                    Велосипед — да, зато есть чем гордится и к чему стремиться :)
                                    +1
                                    Прочитав статейку, подумал что вы написали не CMS, а фреймворко-CMS
                                      0
                                      На данный момент так и есть. CMS с админкой будет дальше.
                                      +1
                                      Спасибо вам за старания, надеюсь, новая, 3 версия будет много лучше предыдущей. Удачи вам!
                                        0
                                        Ну что ж, будем следить за развитием! Текущие ветки совсем забросил? Err 500 частенько падает на текущих сайтах…
                                          0
                                          Да, и в админку сервера доступа нет =\
                                            0
                                            Это к Александру :)
                                          +1
                                          :-) Двигайся дальше.
                                            +9
                                            > CMS ориентирована на создание социальных сетей и интернет-сообществ, то кеширование — это все
                                            Несколько вопросов.
                                            1. Есть какие-то замеры? Например типичная простая страничка 10 объектов (начала статей), листалка, навигация. Или же статейка с комментариями. На каком железе сколько запросов обрабатывается без кэша и сколько с кэшем в секунду. Например устроив стресс тест с помощью http_load.
                                            2. Как определяется что попадает в кэш а что нет? Заносить все — памяти у серверов много, но в пределах разумного =) А если не все объекты в памяти не держать, то с базой желательно работать более-менее оптимально
                                            > лучше потратиться один раз на мощный сервер
                                            все понятие относительное. говорить о удачности или неудачности того или иного решения можно когда есть числа, а не обсуждая сферического коня в вакууме. Например, сталкивался с системой, написаной очень модно, но обрабатывающей два запроса в секунду на четырехядерном сервере, что на мой взгляд ни в какие ворота.
                                              0
                                              > Синтаксис: {{имя_модуля:: название_метода(параметры)}}.
                                              >… но он явно быстрее подгрузки XML-таблиц прямо в XSLT через document()

                                              А это действительно на существенно быстрее?

                                              PS Кстати, Explay3 на UMI.CMS чем-то похожа (как мне показалось)
                                                +2
                                                PS Кстати, Explay3 на UMI.CMS чем-то похожа (как мне показалось)

                                                Может, потому что lauri работает(или работал) над umi?)
                                                  0
                                                  Ваша правда :)
                                                    0
                                                    поправочка — все-таки работал
                                                    0
                                                    Вот ведь, а =)
                                                    А я думал, что заговор раскрыл. Отбой =)
                                                    0
                                                    Быстрее: в тойже ЮМИ в XSLT шаблонах везде обращение к сайту через document(), при этом куча времени уходит на генерацию запрашиваемой страницы, а в случае Explay время отработки макроса — это только лишь время работы вызываемого модуля.
                                                      0
                                                      Жаль. Мне идея с document() очень понравилась… Надо будет замеры какие-нибудь сделать.
                                                        0
                                                        Document(), как родной способ, будет, конечно, красивее, а макросы в XSLT — это, к сожалению, костыль. Возможности движка позволяют использовать оба способа, достаточно вызвать необходимую страницу с расширением xml.
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      Напоминает Drupal, лет этак 5 назад, да? :)
                                                        0
                                                        Я с друпалом дела не имел. Он 5 лет назад был на ООП?
                                                          0
                                                          Думаю был, он довольно взрослый :) Просто ООП в нём реализовано на непривычном для большинства уровне, классов совсем нет.
                                                            0
                                                            он и сейчас не ооп ))
                                                              0
                                                              Для вас ООП — это классы?
                                                          +1
                                                          lauri Все без исключения методы уже комментированы для дальнейшего составления документации.

                                                          doxygen в помощь и ничего не надо писать ручками, если комментированы по правилам.

                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                              –1
                                                              А где у Юмисофта 3-ка? 0_о
                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                +2
                                                                Начнем с того, что фирменного стиля Вы еще не видели и не надо строить предаоложения, что это будет синий, немного эпполовский, дизайн. Во-вторых, я уважаю труд разработчиков Юмисофта и я бы не стал пи… их труд, хотя бы по этой причине. Никто в веб-разработке за последниие 3-5 лет не изобретал ничего революционно нового =) Подумайте, прежде, чем обвинять кого-либо в пи…
                                                                +2
                                                                Для меня интерес представляла ваша структура БД, просто для ознакомления, так как, судя по описанию, я предположил, что она достойна того, чтобы на нее взглянуть. Просматривая sql-файл, вспомнилась проблема, с которой я недавно столкнулся при проектировании. Похожая проблема на stackoverflow. Архитектура БД, конечно, разная, но суть в негативном отношении профессионалов к применению метода Entity-attribute-value.
                                                                  +2
                                                                  Насчет кеша.
                                                                  Один запрос будет быстрее, чем 100 обращений к memcached, тем более к диску.
                                                                  Мне нравится такая схема: в самой таблице с объектами (как вариант в присоединенной, всеже шаблонов на тип может быть несколько) хранится кеш. Выборка айдишников идет вместе с кешем, смотрится если кеш есть — выводится он. Если кеш пуст — вызывается что-то, что кеш заполнит, внесет его в таблицу объектов и отдаст выводу. Лучше сразу после получения кешей определять объекты с промохом, делать на всех один запрос и заполнять кеш тоже одним запросом.

                                                                  В результате при пустом кеше: 1 запрос легкий, кеш пустой, одни айдишники. 1 запрос тяжелый, скопом собираем всю информацию для всех объектов, которые нужно вывести. Один мощный апдейт.
                                                                  При полном кеше: один запрос достаточно тяжелый по трафику, но зато в нем уже прямой вывод на страницу.
                                                                    0
                                                                    Спасибо за отличную идею. В движке еще можно многое изменить, так что я попробую Ваш совет.
                                                                    +3
                                                                    Здорово :) С любопытством слежу за развитием LiveStreet и Explay. После этой публикации ставлю мысленный "+" Explay. Предыдущий код был ужасен поистине… А этот намного-много лучше!

                                                                    Конечно, запрет «нескольких объектов одним запросом» в пользу кеширования — это смелый ход. Подумаю на досуге насколько он может быть оправдан. На самом деле я сам давно заметил тенденцию упрощения запросов в наших проектах с JOIN-ов на пост-JOIN-ы. Только по 100 элементов мы обычно не выбираем, а используем несколько WHERE id IN ()
                                                                      +4
                                                                      хм… блин, что-то интерестное… точнее странное… нельзя выбрать например одним запрос много комментов? насколько это оправдано? если так получиться, что кэш уйдет в даун, а на сайте будет около 5000 тем, в каждом по 500 комментариев… это ж скока нужно времени будет первому «счастливчику», который зайдет на сайт… он походу успеет вырастить ребенка и построить дом и посадить дерево… за это время пока ему откроется главная… вообще код клевый, но выборка из базы- что-то нужно с этим делать… неужели нету других вариантов?
                                                                      P/S а функцией Count можно пользоваться? или подсчитывать нужно по одному объекту?
                                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                                          +6
                                                                          explay/classes/i18n.parser.php

                                                                          /**
                                                                          * Купить:
                                                                          * — молоко «Простоквашино»
                                                                          * — бананы
                                                                          * — шампунь
                                                                          * — мыло (жидкое + обычное)
                                                                          * —!!! масло «Русские кружева»!!!
                                                                          */
                                                                            +1
                                                                            Нашёл у вас дырку: example.com/my/__default();var_export($this);die
                                                                            Давайте незачётку! eval без должной фильтрации запроса до добра не доведёт =)

                                                                            А ещё: подскажите, пожалуйста, где у вас там находится место, в котором происходят выборки объектов (из кэша, а если нет — из таблиц)
                                                                              0
                                                                              Как раз в раздумьях про фильтрацию eval'ов, у Вас случайно нет идеи?

                                                                              Объекты проходят через ObjectsController, в частности ObjectsController::getObject (...);
                                                                                0
                                                                                Имхо, в каждом отдельном случае должны быть свои проверки.
                                                                                if (!method_exists($this->modules[$moduleName]['object'], $method))
                                                                                 throw new CoreException (lang('error_controller_module_return_invalid_object','core'));

                                                                                Ещё можно посмотреть в сторону Reflection
                                                                                  0
                                                                                  Спасибо, исправил :) Правда method_exists () тут не обошлось — в модулях можно реализовывать множественное наследование, поэтому пришлось писать свой method_exists ().
                                                                                  0
                                                                                  ObjectsController::getObject (...);
                                                                                  Я тут покопался в коде, почитал комменты…
                                                                                  В кеше лежат отдельные объекты, а при выборке одним sql-запросом мы не знаем какие объекты закешированы, а какие нет — теряется смысл кеша.
                                                                                  Именно поэтому вы и не сможете уйти от ста (или тысяч ?) запросов к кэшу/БД.

                                                                                  Имхо:
                                                                                  Вы не то кэшируете. Запись таблицы — слишком маленькая сущность, чтобы быть закэшированной.
                                                                                  Если вы от этого не откажетесь, именно в «социальных сетях и интернет-сообществах» ваши принципы кэширования проявят себя с отрицательной строны. Правда, если откажетесь — придётся всё переделывать.
                                                                                    0
                                                                                    Имхо, вы не правы. Отдельный объект — это отличная сущность для кеширования, особенно в «социальных сетях и интернет-сообществах», где можно вывести объекты сначала по хранологии, потом по тегам, потом по авторам и по вашей схеме 10 раз пересторить кеш.
                                                                                      +1
                                                                                      Может быть и не прав.
                                                                                      Отдельный объект — это отличная сущность для кеширования
                                                                                      Но с другой стороны — всё зависит от того, что именно считать за объект!
                                                                                      Что лучше Объект «100 Комментариев» или 100 Объектов «Комментарий»?
                                                                                        0
                                                                                        Если комментарии древодины — очевидно, что рациональнее второй вариант. Если линейны — первый, причем можно даже кеш не перестраивать, а дозаписывать новыми комментариями.
                                                                                          +2
                                                                                          В этом случае надо учитывать одну особенность комментариев: отношение между прочитанными комментариями и добавленными. Пускай будет 100:1 (100 раз прочитали, один добавили). Очевидно, что менее накладно будет перезаписать структуру с комментариями, чем каждый раз искать закеширован ли каждый коммент.
                                                                                          И потом, возникает вопрос: в чём тогда ускорение с помощью кеша будет, если проверяется каждая атомарная сущность на изменение, а не страница (часть страницы, например, где находятся сами коменты)?

                                                                                          Здесь же на 100 обращениях к базе теряется весь смысл кеша.
                                                                                +1
                                                                                Наконец-то, разработчики правильно начали подходить к своим проектам.
                                                                                Т.е. сначала архитектура.
                                                                                Вы совершенно правильно начали разработку и самое главное правильно определили путь проекта.
                                                                                Т.е. самое главное объект. Тогда можно очень гибко подходить к развитию любого проекта — это очень радует.
                                                                                Но прочтя комментарии, я как бы немного впал в ступор. Знаете почему. Мне совсем не понятно зачем делать 100 запросов на 100 комментов. Мне кажется вы немного ошиблись в проектировании архитектуры именно БД.
                                                                                Можно вполне спокойно сделать это 1 запросом. Во всяком случае я так реализовал у себя. (это довольно легко кстати делается если вы в первую очередь сделали объектную ориентацию архитектуры)
                                                                                Во вторых тогда можно сделать один гибкий, настраиваемый контроллер.
                                                                                Если интересно пишите в личную.

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

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