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

Рис 1. CMS — совокупность модулей
Модуль в терминах создаваемой системы — это статический класс, имя которого определяется именем модуля. Для функционирования многих модулей недостаточно одного статического класса — могут применяться дополнительные программные структуры, реализуемые в отдельных файлах. Все файлы модуля размещаются в одной директории (папке), а директории модулей в modules.

В корне web-директории существует файл index.php и файл базовых настроек config.php. При любом запросе к сайту, сервером исполняется файл index.php, именно с него начинается функционирование системы.
Системный модуль System
В index.php подключается системный модуль System и файл конфигурации config.php. После загрузки конфигурационного файла, управление передается системному модулю.

Рис 2. Запуск системного модуля – ядра cms
<?php
/**
* index.php
* Главный исполняемый файл.
*/
// Подключение системного модуля для обеспечения автозагрузки и работы других
// модулей
require('modules/system/System.php');
// Файл конфигурации
require('config.php');
// Запуск системного модуля
System::Process();
?>
Системный модуль обеспечивает автоматическое подключение всех остальных модулей и является ядром системы, приводя её в исполнение. Автоматическое подключение модулей обеспечивается php-функцией __autoload($class_name). Данная функция определяется в файле системного модуля, но вне его класса. При обращении к незагруженному классу, PHP интерпретатор обращается к этой функции, функция, в свою очередь, обращается к системному модулю. Системный модуль определяет наличие запрашиваемого класса (модуля), местонахождение его php-файлов и подключает их.
/**
* Функция автозагрузки классов (модулей).
* Эта функция используется только интерпретатором PHP
* @param string $class_name Имя класса (модуля).
*/
function __autoload($class_name){
System::IncludeClass($class_name);
}
После подключения файлов класса системным модулем автоматически вызывается статический метод Activate() подключенного класса (если этот метод есть), что позволяет статическим классам инициализироваться. Модули, как помните, являются статическими классами и многим необходима инициализация перед работой.
Таким образом, для использования модулей не надо ничего предварительно объявлять или подключать, достаточно просто обратиться к методам или свойствам класса модуля, и не нужно создавать ссылок (указателей) на модули. В этом заключается принцип простого использования модулей любым компонентом системы.
Модуль событий Events
Модуль событий Events предоставляет возможность модулям генерировать события. События генерируются вызовом метода Events::Send($module_name, $event_name). Обрабатываются события статическими методами классов других модулей, хотя ничто не мешает модулю обрабатывать и свои события.
Системный модуль System не определяет дальнейшее функционирование системы, он просто генерирует поочередно три события: «инициализация» (INIT), «выполнение» (START) и «завершение» (STOP).
static function Process(){
// Генерация основных событий:
// Инициализация
Events::Send('System', 'INIT');
// Выполннеие
Events::Send('System', 'START');
// Завершение выполнения
Events::Send('System', 'STOP');
}
Любой модуль может обрабатывать эти события и тем самым участвовать в функционировании системы. Но не только системный модуль генерирует события. На рисунке показаны основные события системы (кружочки) и их связи (линиями в полоску) с модулями, обрабатывающие события.

Рис. 3 Основные события
Модули, генерирующие события, не знают кто их события обрабатывает. Любой модуль может зарегистрироваться на обработку любого события. Регистрацию на событие достаточно сделать один раз при установке (инсталляции) модуля. Сохранность информации о том, какой модуль на какое событие зарегистрировался, обеспечивается модулем событий Event. Ниже приведён пример регистрации и удаления её на событие «инициализации» (INIT) системного модуля System модулем ошибок Errors.
/**
* Установка модуля ошибок
*/
static function Install(){
// Регистрация на события инициализации системы
// Аргументы:
// Модуль-источник, Имя события, Модуль-приемник, Имя метода-обработчика
Events::AddHandler('System', 'INIT', 'Errors', 'Process');
}
/**
* Удаление модуля из системы
*/
static function UnInstall(){
// Удаление регистрации на событие инициализации системы
Events::RemoveHandler('System', 'INIT', 'Errors', 'Process');
}
Применение событий позволяет расширять действия модулей, генерирующих события, либо просто реагировать на действия этих модулей.
Кстати, вспомните, как загружаются модули — только при первом обращении к ним. Это значит, что перед генерацией события START системным модулем, загруженными будут только сам системный модуль System и модули событий Events, ошибок Errors и сессий Session. Модули Errors и Session загружены, так как обрабатывали событие INIT, а модуль Events загружен, так как его использовал системный модуль.
Модуль ошибок Errors
Событие «инициализация» (INIT) системного модуля первым делом обработает модуль ошибок Errors, при этом он ещё и автоматически загрузиться. Модуль ошибок переопределяет стандартные обработчики ошибок и исключений PHP. Детально о нем пока говорить незачем. Можно упомянуть только то, что модуль ошибок в свою очередь генерирует события после обработки ошибок, что позволяет независимо от причины возникновения ошибки сформировать понятный ответ пользователю модулями, формирующими вывод, например, модулем Page.
Модуль сессий Session
В работе модуля сессий Session нет ничего особого, модуль запускается при событии инициализации системного модуля, определяет идентификатор сессии пользователя и после предоставляет доступ к данным сессии как модуль запросов Request.
Модуль запросов Request
Событие «выполнение» (START) системного модуля обрабатывает модуль запросов Request. Модуль запросов собирает все поступившие данные от пользователя в «контейнер», из которого они будут доступны любому модулю системы. Чтоб получить данные из «контейнера», нужно указать формат — тип и ограничения размера ожидаемого значения. Таким образом, в какой-то степени предотвращается проникновение данных, не соответствующих требованиям модулей. После завершения процесса помещения входящих данных в «контейнер», модуль запросов генерирует событие AFTER о завершении своей основной работы.
На поступивший запрос пользователя, как правило, необходимо сформировать вывод — ответ, и отправить его клиенту. Ответом обычно является html страница, но им может быть документ в ином формате, например XML, или файл, или что-то другое. В зависимости от параметров запроса пользователя (url’a), определяется один модуль для формирования вывода, точнее каждый модуль вывода сам определяет, работать ему или нет таким образом, что в итоге только один модуль отвечает за формирование вывода. Модули вывода начинают свою работу, обрабатывая событие AFTER модуля Request, что позволяет легко добавлять новые модули для формирования вывода в иных форматах. В принципе, любой модуль может быть модулем вывода, если будет хоть что-то передавать пользователю.
Перед формированием вывода, необходимо обработать входящие данные. Ими могут быть данные форм, которые следует сохранить в системе. Этим занимается модуль данных Data, обрабатывая событие AFTER модуля Request.
На рисунке стрелками отображен поток данных: пользователь выполняет запрос к серверу (передаёт данные), полученные данные обрабатываются модулями системы, каждый модуль выполняет свои действия с данными, в итоге формируется результат, который передаётся пользователю. Данные передаются прямым обращением к методам или свойствам модуля. Модуль, принимающий данные, сначала их запрашивает. Например, модуль данных Data, обрабатывая событие модуля Request, начинает свою работу и первым делом обращается к нему же (к модулю Request) за данными.

Рис. 4 Поток данных
Модуль данных Data
Модуль данных Data — самый важный модуль системы, именно от его возможностей зависит гибкость и функциональная мощь системы. Модуль данных предоставляет общий доступ к данным. Позволяет их создавать, изменять, удалять, а также осуществлять поиск с мощными возможностями. Только модуль данных обращается к базе данных через модуль базы данных Database. Для работы с данными другие модули не используют SQL запросы, ими применяется простой, но гибкий способ описания условия запроса, а также методы для манипулирования данными. Модуль данных на основе условий создает SQL запрос к базе данных и возвращает объекты данных. Получив объект «Новость», можно без явного обращения к модулю данных узнать всё, что связано с новостью — хоть автора раздела, к которому принадлежит новость. Благодаря модулю данных, пользователь-конфигуратор (да и сама система) может создавать свои классы данных — свои структуры данных. Об этом и многом другом будет рассказано в следующей статье. Для ясности только добавлю, что класс данных не имеет прямой связи с классом структуры языка программирования.
Модуль данных тоже генерирует события, обрабатывая которые можно осуществлять дополнительные действия с объектами данных при их сохранении, чтении и удалении.
Модуль страниц Page
Модуль страниц Page, используя шаблоны оформления, формирует html страницу, которую потом отправляет пользователю. Страница формируется из блоков в соответствии с входящими данными — параметрами запроса (url’a). Модуль страниц интенсивно использует модуль данных, так как страница — это представление данных, да и многие фрагменты — блоки страниц — тоже являются данными. Принцип генерации страниц и все тонкости функционирования модуля — тема одной из следующих статей.
Модуль файлов Files и фотографий Image
Модули файлов и фотографий — это одни из тех модулей, которые дополняют действия модуля данных. Кроме того, модули файлов и фотографий, при установке в систему, создают соответственно классы данных «Файл» и «Фотография». После, при работе модуля данных Data с объектами этих классов, модули файлов или фотографий выполняют дополнительные действия. В частности, при создании объекта данных «Файл», происходит загрузка файла на сервер и сохранение его в файловой системе сервера. Модуль файлов также является модулем, формирующим вывод. При соответствующих запросах, модуль передаёт файлы пользователю для скачивания, при этом ведется контроль доступа, статистика и все что может делать система в целом.
Основная функция модуля фотографий — масштабирование фотографии. Так как фотография является файлом, то её тоже нужно уметь загружать на сервер, для этого используется модуль файлов.
Модуль действий Actions
Модуль действий Actions позволяет классифицировать и контролировать действия, совершаемые модулем данных Data над объектами. Условием совершения действия может быть теоретически любое логическое выражение: в нем может быть и проверка текущего пользователя (его свойств), и проверка объекта, над которым совершается действие, и другое.
Пример: модуль данных Data загружает данные из БД и генерирует событие чтения, которое обрабатывается модулем действий Actions. Модуль действий Actions проверяет условия данного действия и вычисляет возможность его совершения, логический результат вычислений принимается модулем данных Data, который просто подчиняется результату, то есть продолжает или завершает чтение объекта данных. Условие и действие тоже являются объектами данных. Разобраться в тонкостях функционирования модуля действий Actions и гибких возможностях контроля действий можно будет после изучения модуля данных Data.
Модуль форматов Formats
Задача модуля форматов Formats — предоставлять разнообразные форматы для значений. Например для строк форматами будут представления e-mail адресов, телефонов, форматы оформления (разметка). Формат — это объект данных, который связывается с объектом значения, что и определяет формат для значения. Модуль форматов не просто предоставляет объекты-форматы, он ещё и обеспечивает проверку значений на соответствие формату. Сама проверка происходит, когда модуль данных Data сохраняет объект данных. Также некоторые преобразования значения (форматирования текста, например) в соответствии с форматом происходят при чтении данных. С функционированием данного модуля есть ещё множество нерешенных вопросов. Работа продолжается.
От автора
Я рассказал о двух способах взаимодействия модулей — при помощи прямого обращения к методам и через обработку событий. Другие затронутые темы касательно функций и назначения основных модулей рассмотрены поверхностно, только чтобы показать в целом картину функционирования системы. Впереди самая важная тема – модуль данных, допуск к прочтению только со свежей головой
Обращаю ваше внимание, что некоторые детали архитектуры CMS могут быть в будущем изменены.
Проектирование продолжается.
Опубликовано на Хабрахабре: http://boolive.habrahabr.ru/blog/51152/
Завтра на работе зачитаюсь!
мда!!! надеюсь сил хватит чтоб все доделать
круто, должно работать
Всё описано хорошо, но поверхностно.
Отличным дополнением было бы к каждому параграфу пару десятков строк кода, как пример реализации описываемого действия. Иногда бывает нужно просто взглянуть на код, чтоб понять то, что не понял, читая описание.
Я остановился только на взаимодействии модулей. Детальное описание с программным кодом основных модулей, по моему мнению, было бы преждевременным и перегружало бы понимание общего (да да именно поверхностного) принципа функционирования системы. Это статья – вступление для понимания следующих публикаций.
Спасибо за комментарии.
Хорошо. Тогда не было бы лишним описание правил написания кода (стилистика).
Например, называть классы CMyClass или class_MyClass и т.д.
Ок, подготовим… всё в своё время)
Интересно, конечно, но, мой Вам совет — не заморачивайтесь с event’ами!
Лучше, проще, быстрее прямые вызовы.
С event’ами будет тяжело и главное — выигрыш сомнителен! В чем выигрыш-то?
гибкость, расширяемость. Тот же друпал использует нечто похожее на события — хуки
Гибкость и расширяемость — используйте ООП — гибкость, расширяемость бесконечны!
События — элемент ООП. Именно события позволяют программам свободно расширяться. Другое дело — способ их реализации.
В предложенной архитектуре с событиями есть одна существенная проблемка, сам осознаю это — от порядка вызова обработчиков события будет зависить логика работы cms. Вот на что хотелось бы получить комментарий)
Я думаю, порядок вызова обработчиков событий должен происходить тогда, когда происходит само событие, имхо это очевидно. Другое дело, когда обработчики вынуждены «стоять в очереди», т.к. за короткий промежуток работы кода произошлло слишком много событий. Примером очереди может быть цикл, в котором происходят события «запись в базу данных». По окончании цикла в дело вступают обработчики, которые «регистрируют» эти записи для возможного бэкапа действий.
Когда вызывать обработчики события, очереди — не в этом вопрос.
Регистрация на событие происходит только при установке модуля. В зависимости от того когда устанавливались модули будет зависеть вызов обработчиков события, т.е., например, событие INIT системного модуля может быть обработано сначала Errors потом Session, а может сначала Session потом Errors. В данном случаи порядок не критичен, просто если возникнет ошибка в модуле Session она не будет обработана модулем Errors, так как он еще не будет запущен.
В других ситуациях, например обработка события AFTER модуля Request, порядок будет критичен
Я же вам говорю — вы запутаетесь с этими событиями.
Не плодите сущности без необходимости!
У вас не windows-программа, скрипт отработал и всё.
Покажите хоть один пример, где без событий не обойтись.
«События — элемент ООП» — кто вам это сказал?
Может лучше задуматься об MVC — model-view-controller?
Ну хотя бы то что Г.Буч об этом пишет. Событие – один из способов передачи сообщения между объектами или просто говоря вызова методов)) при этом объект передающий сообщение не заботиться о том кто его получит. Это позволяет расширять систему без внесения изменений в существующий код. Написал модуль, сделал в нем пару событий, где возможно расширение действий модуля и все. Далее свободно можно создавать новые модули, которые будут подключаться к системе, регистрируясь на события нужных модулей.
То что «скрипт отработал и всё» не имеет практически никакого значении. Регистрацию на событие не нужно делать при каждом запуске скрипта, как в windows программах. Регистрация сводится к тому, что модуль Event просто будет знать, какой метод вызывать при таком-то событии. Модуль Event как посредник между прямым вызовом метода объекта. Может, на пальцах объясняю плохо — непонятно.((
На счет запутаться в событиях. В этом есть доля правды)) Я щас обо всем задумываюсь, но и в общепринятой реализации MVC не вижу решения ВСЕХ проблем.
у меня то тоже MVC только своеобразный))) Возможно, вы подумали, что модули определяют и управляют своими типами данными — страницы, файлы, фотографии. Но нет, все не так. Модули — это только логика системы. Объяснения в следующей статье
«в общепринятой реализации MVC не вижу решения ВСЕХ проблем» — интересно, это какие проблемы она не решает?
Кстати, нашел статью про Event-Driven Web Application Design
Правда речь идет о стороне клиента, а не сервера, но, может пригодиться:
_http://yuiblog.com/blog/2007/01/17/event-plan/
Спасибо за ссылку. Про проблемы MVC пока промолчю)) сорри))
Как-то странно долгая тишина…
не беспокоиться) дело кипит, готовится очень сложная)) статья
И когда же примерно появиться следующая статья а то очень интересно
Статья очень понравилась. Сам занимаюсь разработкой подобной CMS.
Интересно следить за Вашим проектом, Владимир.
Жду продолжения.
Статья следующая очень сложна в понимании, поэтому сейчас занимаюсь всем тем чтобы облегчить её. Выйдет ориентировочно в течение одной — двух недель. Кстати, толькочто опубликовал текущий исходник системы для ознакомления. Инфо в разделе «СКАЧАТЬ»
Всем привет, пишу свою cms, точнее еще не пишу, а проектирую…архитектура похожа на вашу кое что почерпнул у вас…я опишу свою архитектуру, может вам поможет в каких то моментах, а вообще хотел бы вашей критики со всех сторон
Просто уже третий день голова пухнет никак все мысли в кучу и пополкам не расставлю
Итак архитектура тоже модульная, но в этой организации я ставил задачу независимости модулей и возможности системы работать без них, все это как дополнения, надстройки…
еще одной задачей было уйти от привязки к конкретной базе данных, по-этому ядро и соответственно все модули абстрогированы от каких-то конкретных запросов. Ядро предоставляет для них (модулей) некий интерфейс работы с данными (например посредством комманд ADD(p1,p2), DELETE(p1,p2) и т.д.) само же ядро работает со средой хранения данных через библиотеку, т.е. захотели на мускуле, подключили соответствующую библиотеку…Таким образом можно работать хоть на файлах, хоть на чем…
система работает по-этапно, т.е. 1) пришел запрос 2) Юзер хочет получить данные 3) Юзер хочет добавить данные 4) отдаем пользователю…Регистрируясь каждый модуль указывает ядру на каком этапе он работает…входящие данные лежат в контейнере и каждый модуль берет только те которые ему нужны, после своей работы каждый модуль записывает свой результат во второй, выходной, контейнер…переключением этапов управляет ядро, однако в некоторых ситуациях такую возможность можно дать некоторым модулям (например, если во входящих данных обнаружен зловредный код и модуль безопасности это обнаруживает, благоразумно будет переключиться на 4й этап и вывести то что он записал в выходящий контейнер)…ядро ведет процесс от получения данных от пользователя до вывода ему ответа, однако модули опредяют как это делать…если модулей нет, то ничего и не выведеться…если какой-то отсутствует, выведется все кроме него…
Теперь о самих модулях, ну собственно это структуры данных назовем их объектами, эти объекты состоят из каких-то элементов назовем их атомы и плюс функции которые эти объекты и атомы забивают значениями исходя из входящих данных (Пример: есть модуль блога, для системы это объект со следующими свойствами: Заголовок, Текст, краткое описание и т.д. и вот регестрируя этот модуль он создает в базе свои таблицы и работает с ними через механизмы ядра…итак юзер тыкает по ссылке со статьей, ядро устанавливает что этап «пришли данные» активным, запускаются модули этого этапа, далее ядро определяет (вот тут еще не продумал как оно это делает, предложения приветствуются
) включает этап того что юзер хочет что-то получить включается наш блоговый модуль, по имени страницы находит статью и записывает в выходной контейнер свой объект наполненный данными…) с выводом еще тоже никак не проясниться в моей голове…вроде бы понятно что есть набор данных в контейнере на них надо натянуть шаблон и отдать клинту…однако как выглядит этот шаблон пока не представляю…
вообщем надеюсь я более менее ясно выразил тот бардак мыслей в моей голове по-этому поводу, прошу помочь разложить
а если кому-то даже понятна эта идея хочу жесточайшей критики
P.S. это моя первая cms, не ругайте если я высказал глупость…наверное все через это проходили, мне кажется что эта идея достойна появиться здесь…спс
P.P.S. буду рад если пошлете на форум где можно будет это конструктивно обсудить
Добрый день, posmotret.
Вы идете по пути существующих CMS – у них тоже есть абстрагируемость от СУБД и модульность причем с тем же назначением, что вы описали. Если ваша цель сделать что-то новое уникальное, то необходима новая архитектора, естественно понимание архитектуры приходит со временем, поэтому пожелаю удачи в работе над вашей первой cms и в получении важного опыта. Если есть вопросы – спрашивайте, можно и в аську постучаться))
P.S. планирую форум открыть для обсуждения как раз таких тем ну и самого проекта в первую очередь))
Архитектура — это часть структуры, которая может быть изменена пользователем.
То что вы описали — это не архитектура, а структура. Поменяйте название статьи. -))
Архитектура не обязательно должна изменятся пользователями. Архитектурой опредляется структура системы и её поведение в целом. И всетаки на счет названия — оно выбрано по популистическим соображениям для сайта habrhabr
Скажите, если не секрет — в какой программе вы делаете ваши великолепные схемы?
во флеше обычно)
>»Архитектура не обязательно должна изменятся пользователями.»
Архитектура — это часть структуры, которая может быть изменена пользователем. Архитектура НЕ меняется пользователем -))
>»Архитектурой опредляется структура системы и её поведение в целом.»
Извините — но это бред.
Как структура может определятся архитектурой? Ничего что две различные системы могут иметь одинаковую архитектуру.
Структура — внутреннее устройство, компоненты, объекта вместе с их взаимосвязями. (если брать с википедии, но определение достаточно точное)
Так вот — структура более охватывающее понятие и она не может быть определена архитектурой.
>»И всетаки на счет названия — оно выбрано по популистическим соображениям для сайта habrhabr»
Вот так и создается поколение людей, которое абсолютно технически не грамотно.
Ну, насчет архитектуры можно поспорить. Скажите, чем занимаются архитекторы зданий, архитекторы (проектировщики) программ? Они проектируют структуру, поведение, внешний вид и т.п. А изменять можно все, если захотеть и приложить силу.
Вы бы в той же википедии про архитектуру сначала почитали, про её ключевые понятия.
Я работаю с понятиями архитектура, структура по роду своей профессии.
Про архитектуру(компьютерных систем, систем проектирования (САПР) да и вообще софта) могу сам написать в википедии.
>»А изменять можно все, если захотеть и приложить силу.»
приложите силу к процессору и заставте изменить свою логику АЛУ.
Вы сами ссылаетесь на википедию, говоря про структуры, почему тогда к сведениям об архитектуре недоверие??
> Про архитектуру … могу сам написать в википедии.
Спасибо, вы уже здесь написали: «Архитектура — это часть структуры, которая может быть изменена пользователем». Информативности у этого определения никакой, только путаница, и причем здесь вообще изменяемость??
> Модуль в терминах создаваемой системы — это статический класс
всё. дальше можно не читать.
при подобном подходе нет необходимости создавать объектные структуры; можно обойтись несколькими библиотеками функций. рекомендую ртфм ооп. ничего личного
форматы (и действия, насколько я понял) реализуются через фильтры.
бд, файлы — тип аксессоры
картинки наследуются от файлов
запросы — орм
события реализованы странно. могу показать три файла\класса (event, handler, dispatcher) которые работают, и на которые я потратил достаточно времени. и в том числе, я пришёл к выводу, что подобная событийная архитектура несколько избыточна для большинства проектов
модуль ошибок можно отнаследовать от событий
если будет интересно, могу описать архитектуру цмф, которую использую я
Пожалуйста, опишите, будет всем интересно. Позаимствуем ваши идеи, если они будут полезны))
>»Информативности у этого определения никакой, только путаница, и причем здесь вообще изменяемость??»
Вообще то это полное определение. И все становится понятно, когда смотришь определение структуры.
На счет ссылки на википедию — я беру от туда определение структуры — т.к. мне лень его было печатать. А там оно правильное.
При чем здесь изменяемость — а это просто из определения следует rtm как говорится.
На счет определений.
«Принцип — это спопособ объединения элементов в систему.»
Не зная определения «сособа» и «системы» — вам определения «принципа» ничего не даст.
Тоже самое с архитектурой.
Вы какой словарь читаете? принцип — это как объяснение на пальцах или схематично что как устроено или работает или сравнение чего-то сложного с более понятным и простым, от того и выражение «в принципе,…»
хм … словарь. -))
такс … порылся в интернете … вот не плохое объяснение систем, структур и остальных базовых понятий: _http://window.edu.ru/window_catalog/pdf2txt?p_id=643
А вообще в универсистете был такой предмет информатика. В хороших универах, там объясняют систематику. После неё начинаешь по другому читать техническую литературу. В большинстве своем интернетовские статьи — дрянь, т.к. их пишут не доценты и не доктора наук.
А вообще советую _http://intuit.ru/
Там многое излогают с пониманием того что пишут. А не ляпают термены без их понимания.
—
Если вернутся к КМС, то это система.
MVC — всеми вами любимая — это небольшая верхушка айсберга(посмотрите робототехнику — и вы увидите, что MVC это вообще обрезок из полной схемы). Если изучить ее, то проектирование КМС будет представлять из себя не такую уж и сложную задачу.
Как говорится, все уже придумали за нас.
Прямо зачитываюсь вашими статьями, давно искал что-то подобное для реализации своего проекта.И только сейчас нашел…Вы раскрываете очень интересную тему. О таком в рунете не пишут, спасибо вам Владимир)
Интересная тема, личьно меня сводит с ума не понимание всего того что вы гаварите. СMS мечтал напесать давно по скольку намучился с DLE и прочим.
Итог — Падумал лучше в своём капаца чем в чюжих каракулях )) Пашол на курсы майкро-софта. Припадаватель с var, functiion и прочего начал ну я что професор ( вобщем.. Пака занимался вопросам о замени препадавателя, набор в групу законьчился, пака мол начни с Фото-Шопа ( прешлось,заплатил уже.
Как опатных прошу, всего-лишь направить в нужном направлении осваения
логики языка,чтоб пака они вар учат я уже скрипты вавсю песал-бы )).
Всего лишь прошу вспомнить себя в начале вашего пути.
Что памагло вам понять что к чему? И извиняюсь что не по теме
Сам — буду слидить за всем вашим искуством и может быть наступит тот день когда я даже смогу памочь вам чемнить ну а пака изучаю ваши статьи.
Главное цель и стремление к ней, изучайте не язык, а способ его применния для создния своих программ, реализации своих идей
GuRam,
Учись писать грамотно.
Deja Vu, а какая в принципе разница, как называется статья. Думаю суть не в названии, а в содержании. Пусть он эту статью хоть строением бутерброда назовет. Главное, что тема затронута полезная и нужная. Я вот, например, во всем инете не нашла нормальной статьи по написанию CMS. Я не пробовала писать что-то подобное, только по этой причине — отсутствие понятного для меня обучающего контента. Думаю, теперь я его нашла.
А название… далось оно вам…
Эх… разрабатываю свою систему уже несколько месяцев (вторую, полностью переработанную) самостоятельно по вечерам. Все это время искал статьи, описания, чьих-то идей или реализации. И только вчера наткнулся.
Спасибо, автору за этот труд.
Своей системе, вчера решил устроить полную ревизию… дошел до стадии, когда всю логику мысленно представить не получается — слишком много деталей. Результат: начал собирать ядро системы заново, в новом проекте, предварительно документируюя все свои действия…
Еще не успел прочитать все Ваши материалы, но… с Событиями — согласен на все 100%
.
Правда, меня вот какая идея еще посетила — нет ли смысла, например, в приоритетах у событий? как раз, что бы бороться со случаем о котором писали выше, когда событие произошло, до появления объекта который его обрабатывает. Что если иметь например 2-3 уровня приоритета у событий? И класс управляющий вызовами отслеживает — если «клиенты» на определенное сообщение отсутствуют, но приоритет у сообщения высокий, то его «откладывают» до следующего захода (или подгрузки нового модуля системы (т.к. никто не запрещает модулю сообщений обрабатывать свои собственные)).
Идея сыровата, но я в ней смысл вижу.
Я наоборот стараюсь сделать так, чтоб приоритеты не требовались, чтоб не было зависимости от порядка обработки событий. Их сложно и накладно реализовать. Даже если сделать приоритеты, то возможны конфликты из-за самих приоритетов. Правильно сделать так, чтоб модуль X1 не волновало существование модуля X2 и наоборот.
Насчет того, что объекта, который обрабатывает событие, может не существовать ещё. Здесь всё в порядке. События обрабатывают только модули, а модули (они же классы со статическими методами) загружаются автоматически по требованию. Если срабатывает событие, а модуль-обработчик ещё не загружен, то он будет системным модулем автоматически загружен и активирован и после приступит к обработке события. Таким образом, кстати, загружается модуль ошибок Errors — он явно не подключается системой, подключение происходит автоматически обработкой системного события INIT
Целиком и полностью согласен с теми, кто против events в данном случае. И тому, навскидку, я вижу сразу несколько причин. Во-первых есть классика жанра — где у каждой программы есть самое ядро — оно же реактор, инициатор микроцикла, или микро-последовательности — которая в небольшое количество строк вызывает последовательно функции, классы и методы, и прочее которые в ходе своих исполнений потом в конце концов завершаются возвращая результат или просто отработав в своей среде. Событийная модель это вообще блюдо из другой кухни, где взаимоисключающий по отношению к предыдущей модели подход, ИМХО, где реактора и ядра типа System::Process() — как такового нет, а функции и вызовы стартуют от «падающих с неба» событий. И чтобы знать кого запускать по какому событию — функции регистрируются на события.
То есть это, подчеркиваю, практически БЕЗУСЛОВНЫЙ переход по событию. Зачем у вас реализована регистрация на событие?
Events::AddHandler(‘From’, ‘What’, ‘To’, ‘What’);
Что она делает? Я так понимаю:
либо
а) модуль Events по возникновению ситуации запустит сразу же To::What
либо
б) модуль Events как-то просто «расшарит» доступ к событию тому модулю или просто «передаст» — тогда эта регистрация — просто дань реальным событийным парадигмам — а в данном случае избыточность
Предположим, что это а)
Если event — настоящий как в event-ориентированной парадигме — то грабли неизбежны, я думаю. Представим, что вызов метода который привязан к событию осущестляется ежемоментно при возниконовении самого события, то есть при первом же Events::Send() — сам же Events его и запустит.
Вот например некий модуль Pages сейчас отрабатывает какойнить метод SaveData с целью что-то записать. Перед началом работы метод сгенерил событие Events::Send(…) что тут же привело к целому каскаду событий которые вызвали другие события, в одном из которых назначено запускаться Pasges::SaveData — это в лучшем случае приведет к зацикливанию. А вообще могут быть неизбежные изменения данных, которые при дальнейшем выполнении строк кода после генерации им события, потеряют контекст выполнения. То есть мы типа планируем что-то сделать с чем-то, генерим событие которое тут же что-то запускает, и после генерации события чудесным образом обнаруживаем что продолжить процесс мы не можем среда действия ушла из под контроля и мы не смогли отработать задачу.
Вообще вот такие казалось бы свободноплавающие events это тоже самое, что
операторы GOTO в бейсике — то есть, то что приводит с ростом программы/систему к неизебежному запутыванию и породжению коллизий.
Такой синтетический параллелизм это хождение по грабл-полю — на первых порах все работает, а потом грабли начинают детонировать другие грабли
Вот пример из теории паралелльных процессов едва ли решаемый в автоматной среде — загадка Дейкстры о 5 обедающих философах и к чему приводят события
http://www.softcraft.ru/auto/ka/fil/fil.shtml
Event-ы вообще имхо нужны в ислючительных случаях, как например в случае с JavaScript когда явного реактора нет и мы ждем OnButton, OnClick _ на самом деле он конечно есть — он у самого интерпретатора JavaScript’a — но точки входа типа пляшут от событий. Или там в случае действительно каких нить параллельных процессах, тредов. Не всегда имеет смысл наследовать порочные пути у якобы гибких систем.
И в дополнение — а чем так сильно Errors отличается от Events и от Actions… Я так понимаю, что Ошибка — это вид события, и Требование_На_Запись — это тоже вид события…
Мне кажется если и делать события, чтобы модули знали что делали до этого другие модули и что породили очередной раз передав управление, просто вести хеш событий как глобальную переменную. Вы однако, храните пользовательские данные полученные request-ом в неком контейнере который расшарен для всех, а события почему-то надо куда адресовать и назначать. Имхо правильнее сделать стэк событий где ясно кто и что сказал, а что с этим делать решит каждый модуль в отдельности когда до него дойдет дело это проще и будет работать всегда нисколько не ущемляя гибкости, но исклюяя колизии с перехватом управления во время события.
В данном случае, события это всего лишь неявный вызов методов. Это необходимо для свободного подключения новых модулей и наращивания функционала системы. Тут меняется отношение между существующими модулями (будем считать системой) и новыми подключаемыми. За счет событий система открыта для расширения, нужно только в новом модуле указать, какие действия расширять, т.е. выполнить регистрацию на событие, а системе не нужно догадываться, что там ещё могут к ней прикрутить, только предоставить вызовом события возможность что-то прикручивать.
При возникновении события вызываются методы, которые зарегистрированы на данное событие. Обработчик события может возвращать результат и этот результат можно использовать там, где событие вызвано, например, перед чтением данных выполняется проверка возможности чтения, там уже генерируется событие, обработкой которого будет осуществлен контроль доступа модулем контроля доступа.
Конечно, я согласен с тем, что если всю логику системы пересвязать событиями, то получится не то что коллизия… Не пойму только, почему вы события сравниваете с GOTO, ведь неслабую запутанность можно реализовать и прямым вызовом функций, которые вызывают другие, а следующие ещё каике-то, а потом зацикливание, рекурсия…
Нужно понимать, что в данной системе модули не есть некий набор подобного функционала, как в существующих движках, типа модуль каталога и новостной ленты принципиально ничем не отличаются. В этой CMS каждый модуль это уникальное звено и связывание этих звеньев, я бы даже сказал, самоконтролируется общей логикой. В общем плане прорисовывается иерархическая последовательность и только в исключительных ситуация эта иерархия нарушается, например при возникновении системной ошибки прекращается общий ход исполнения и от модуля Errors обработкой его события отправляется ответ клиенту. Не нужно полагать, что раз событие, то оно будет обрабатывать всеми подряд, во всяком случаи модули будут разрабатывать не идиоты, да и если я пишу модуль расширяющий что-то, то зацикливание могу осуществить, если напрямую вызову функции, которые расширяю (может не явно их), поэтому это должно по любому контролироваться, так как это сознательное создание цикла или рекурсии. Тут ты можешь зацепиться за мелкие детали и продолжить спор
)
С общими данными запроса всё просто. Запрос – это фактически вызов одного действия и указание в юрле, что требуется клиенту в ответ получить. Так вот, фактически с данными запроса не будут работать все модули подряд. В обще, в данный момент серьезно прорабатываются понятие действия в движке (до этого всё время было уделено модулю данных). Так вот, о чём это я – о том, что возможно данные $_POST будут просто являться параметрами действия и не будут оставаться в модуле Request в «контейнере данных», а этот контейнер будет передаваться действию.
Не будем забывать что мы работаем на вычислительной среде архитектуры фон-Неймана с последовательным вычислением и операциями, и в этой теории есть понятие машины Тьюринга и КА (Конечного Автомата)
http://ru.wikipedia.org/wiki/Конечный_автомат
и это многое объясняет. А если учесть вообще последовательность веба
request / response — то и подавно.
AddHandler(Event=Calc, Function=SecondMethod) # В упрощенной форме зарегали событие
# calc на вызов SecondMethod()
$n = 4 # состояние системы перед
$i = 2 # моментом исполнения кода ниже
…
FirstMethod{ # некий код некое место которое сейчас выполняется
if $n>0 and $i>0 {
EventSend(calc) # сгенерилось событие — «мы готовы считать»,
# ничего не зная кто и как его обрабатывает в уже данный момент
$n / $i # ожидали деления 4/2, а получили 0/2 и Error dev 0,
# этот участок кода был к этому не готов к этому, потому что не знал КАК и КТО
# обрабатывает это событие
}
}
EventSend{ # Событийный движок — делает свое «грязное» дело
# вызывает ежемоментно методы зареганые на событие
if calc{
SecondMethod() # просили — получайте
}
}
SecondMethod{
$n = 0 # Допустим этот метод обычно всегда сбрасывает
# в нулевое значение переменную $n
# но тогда этот метод работая с $n должен 100% знать КТО, КОГДА,
# и при КАКИХ обстоятельствах работает с $n, чтобы не допустить ошибки
# по возвращению из обработчика событий
# То есть один метод должен знать обо всех методах
# которые так или иначе затрагивают общие данные — а общие данные всегда будут
# и аутентификации и контент, и состояния и пр.
}
P.S.: А поскольку в прямом смысле слова глобальных переменных нет, то глобальными переменными будут аспекты более выского порядка — сущности аутентификаии, контента, еще чего-то — рано или поздно но риск есть, ходить осторожно — одно, а быть застрахованным — другое
Так что в этой архитектуре, я боюсь, один из философов Дейкстры рискует остаться без обеда — потому всегда все вилки будут заняты по разным причинам
«…За счет событий система открыта для расширения, нужно только в новом модули указать, какие действия расширять, т.е. выполнить регистрацию на событие, а системе не нужно догадываться, что там ещё могут к ней прикрутить…»
А что в ином случае системе надо догадываться что к ней прикрутили?
ИМХО функционал расширяется путем расширения самих функций и методов изнутри, и ненадо системе знать.
Вчера функция была такой:
File.Read(
File.open()
File.readline()
)
Завтра прикрутили штуку File.check()
Теперь это
File.Read(
File.check()
File.open()
File.readline()
)
Все, кто вызывал File.Read() будут по-прежнему его вызывать
не зная что функционал обогател.
Я не рассматриваю модульность (новости, и прочее) я понимаю что модули тут есть аспектные компоненты.
Ну так ты расширил функционал непосредственно изменив получается модуль, тут нет никакой гибкости для конечного пользователя системы. Смысл то в том, чтоб без вмешательства в существующий программный код расширять функционал.
Ну хорошо, предлагаю отойти от возможности запутывания и перейти к получаемой за эту цену гибкости и расширяемости. Итак, что же это за возможность, а это возможность прикрутить новый модуль который ориентируясь на общие события будет что-то привносить. И в код остальных модулей лезть не придется. Попробую промоделировать так ли это.
Предположим, есть модуль который работает с файлами, он генерирует событие:
READFILE. Появилась необходимость добавить модуль какой-то проверки до чтения файлов — пусть это будет модуль CheckBefore. Я ввожу этот модуль в систему просто регаясь на событие READFILE. Но что я должен знать: В каком именно месте кода другого модуля это событие — сгенерено ДО чтения файла, а не в МОМЕНТ или ПОСЛЕ, это очень важно — иначе технически мой новый модуль становится не удел. Хорошо, а допустим мне завтра нужно внести какую-то пост-обработку файла что тогда? Я должен ожидать что в системе существует
событие READFILE_END? И уже на него регистрироваться? Пока я вижу что события представляют собой высокоуровневые абстрактные события быстро исчерпывающие свой потенциал по гибкому наращиванию системы.
Я склонен полагать, что чтобы завтра прикрутить модуль который не был необходим вчера, не найдется заранее подготовленного события, и тогда мы руками лезем в код тех модулей события которых нам нужны — гибкость ли это и расшираяемость? Чтобы система была гибко расширяема практически без вылазок в код других модулей, технически, надо чтобы почти после каждых значимых строк кода всех модулей были генерации событий, потому что только в эти места можно будет потом вклинить вызовы. Мне кажется, что выглядит не очень гибко и не очень лаконично. Не проще ли было следовать тому, что и так уже придумано, вводить модули ни как орбитальные станции, а как подключаемые и наследуемые библиотеки — то есть каждый функционал системы это вызов функции — который дробится на более простые функции, а они в свою очередь на еще более простые, таким образом перекрывая или дополняя, мы всегда можем внести коррективы практически в любой участок системы — а технически это разделено отдельными папками и файлами — как модули в питоне. И ненужно достигать этого неродной для PHP-интерпретатора событийной моделью и в идеале испещрять каждые две строчки кода генерацией события, в стиле READFILE_BEFORE, READFILE_NOW, READFILE_AFTER, и т.п.
Да и вообще зачем внутри системы такое жесткое API? При взаимодействии со сторонними ПО? — но это другое дело и другая история. Сегодня приступаю к детальному чтению следующих глав по архитектуре данных. Вообще у меня закрадываются подозрения что автор смотрит дальше и хочет привести не только данные к общей гомогенной среде но и функционал по возможности — этим возможно и продиктованы введенные Events и Actions. Я тоже думаю, что на самом программа — есть какой-то частный случай общего хранилища данных, то есть инфморционное сетевое хранилище не только статично и имеет связи, но еще и активно и производит действия по управлению какой-то части информации. Эту точку зрения я слышал у г-на Нариньяни — директора РосНИИ ИИ. И с ней согласен, но мало пока представляю с какого бока к этому подобраться.
P.S.: На самом деле мне жутко интересно обсуждать все теоритические и практические моменты, до которых у меня, я надеюсь, хватает ума. Я ЗА эту CMS-ку потому что её концепция в любом случае ближе всех мне по духу. Вчера очередной раз почитал про DJANGO — ну не нравится мне их ORM — ну что тут поделаешь, ну слишком они ложатся по фактическую архитектуру рели в угоду производительности и слабо мне кажется думали о теоритической модели информационного пространства, более близкого по признакам к реальности.
Думал и отчасти ещё продолжаю думать о создании некого базового функционала, который расширяется уточнением, реализацией конкретных действий, но как его расширять? Наследованием классов, декорированием…? Это же опять или прямое вмешательство в код или использование событий. Фактически ведь сейчас так и получается, что есть базовые модули и они расширяются через события. А вот, что касается дробления функционала на мелкие куски и предоставление возможности полного переопределения их, тут я с радостью ещё подумаю, здесь есть свои плюсы.
В статье приведены базовые события, на самом же деле по мере уточнения логики модулей (движок то ещё делается) у них появляются более специфичные события. В данной системе нет стремления к аспектному программированию, когда реально нужно было бы вызывать события после каждого оператора или в лучшем случаи вначале и в конце тела каждой функции/метода. На такую мысль может навести событие AFTER в модуле Request. Его уже нет
Дело в том, что вызов события предполагает какой-то вид действия, тем самым указывая направление расширения функционала, а не просто выполнения чего-то, чего угодно только от того что тот модуль сработал. Здесь пока логика контролируется в основном стремлением культурно программировать, процесс движется и к его финалу сформируется некое правило по событиям.
Но главное внимание нужно обращать совсем на другое! Посмотри, как вызываются события – просто указывается строкой название, а это даёт возможность, исходя из любых условий, формировать событие динамически. А теперь рассмотрим, где это применяется.
Работу системы необходимо рассматривать глобально. Есть клиент, есть сервер, на сервере наша система обрабатывающая запросы клиента. Учитывается, что запросы могут быть в разных форматах, это может быть в большинстве случаев обычный HTTP, а далее на его основе и JSON, и XML-RPС, REST, да даже чуть на забыл непосредственное обращение к фалам. В данной статье эта возможность обозначена модулями Page, X, FILE как пример. Так вот, представим работу модуля XML-RPС.
Он разбирает полученный xml и должен вызвать соответствующее действие в системе. Именно здесь события выдают максимальную гибкость. Так модулю XML-RPС достаточно вызвать событие с динамически сформированным именем (имя может быть указано в запросе) и всё. Далее уже какой модуль зарегился на такое событие, тот и будет выполнять действие. При подключении нового модуля не нужно модернизировать все модули обрабатывающие запросы, дополнять их алгоритм, их API.
Ну… не знаю…
Если у системы есть внутренние границы — то всё что внутри должно обеспечивать работоспособность и отказоустойчивость, а то что во вне — это стандартный ввод/вывод — и тут уже форматирование потока — HTML, XML~SOAP, JSON, и т.п. Неужто предполагается разнести модули системы по сети?
А смысл, делать это на высоком уровне, проще если уж нужна распределенность использовать или сетевую файловую систему, или среду исполнения. Вообщем, если есть интутивное видение что нужно так, то иногда даже трудно объяснить почему — но потом оказывается что интуиция просчитала все правильно — будем с нетерепением ждать продолжения.
В продолжение примеру. За счет применения событий можно устанавливать модули обрабатывающие запросы без вмешательства в код. Выбор обработчика запроса производиться не явно через генерацию события на основе первого параметра URL. Тут также обеспечивается и безопасность — вызвать через запрос любой модуль не получиться, можно только если модуль сам зарегистрировался на событие, только тогда он будет работать.
Событие — это как связующее действий между слоями логики движка. Сами же действия централизуются модулем Action, почти также как централизованы данные. Но об этом в деталях пока сложно рассказывать, так как сейчас над этим ведется работа.
Ещё полезно с паттернами проектирования разобраться. Там найдёшь ответы на многие вопросы. А на некоторые вопросы даже и не появятся.
p.s. MVC но не совсем звучит как-то по джуниорски