Часть 1. Разработка PHP-фреймворка :: Начало
2014-05-26 в 19:10 PHP Projects Bun Framework
Первая статья о разработке собственного фреймворка будет посвящена обзору основных намеченных возможностей и архитектурных особенностей нового фреймворка. Здесь я опишу модули и компоненты, разработка которых будет описана подробно в следующих статьях. Итак, определимся, как мы организуем код фреймворка и приложений, которые его будут использовать, и далее накидаем roadmap разработки основных компонентов.
Для начала я выбрал простую и лаконичную структуру директорий проекта:
- /
- app – директория webroot сервера, сюда кладем index.php
- public – здесь будем хранить статичные файлы приложения
- src – директория с исходным кодом самого приложения
- Blog – каждое приложение находится с своей директории
- lib – директория для внешних библиотек. Тут же будет располагаться и код фреймворка
- var – директория для файлов приложения (кэш, хранение файлов, сессий и т.п.)
С директориями определились. Сейчас мы можем удобно разделять отдельный компоненты приложений по разным директориям, использовать PSR0 для автозагрузки классов и т.п.
Далее нужно выбрать имя фреймворка, чтобы элементарно создать по него директорию внутри lib. Недолго думая, я выбрал имя Bun – короткое, запоминающееся и вроде ни с чем не конфликтующее.
Внутри lib создаем директорию bun (неймспейс вендора), в ней еще одну директорию bun (неймспейс библиотеки), а внутри нее src – тут и разместим код нашего фреймворка. Такая замудреная структура директорий позволит нам в дальнейшем подключать фреймворк через http://packagist.org без лишних проблем.
Далее выделяем namespace для базовых классов фреймворка – я выбрал Core. Чтобы организовать в дальнейшем подключение компонетов вне директории Core потребуется организации модульной структуры фреймворка. Поэтому первым компонентом системы у меня появляется модуль (Bun\Core\Module).
Bun\Core\Module
Модуль должен предоставлять базовую информацию независимом компоненте фреймворка. Для начала - используемые модулем конфигурации и зависимости, версия, описание модуля и т.п. Каждая директория внутри lib/bun/bun/src – будет являться отдельным модулем и должна внутри себя содержать одноименный класс (например, Bun\Core\Core.php) – который представляет собой реализацию модуля.
Далее переходим к следующему основополагающему элементу Bun Framework – Application (Приложение).
Bun\Core\Application
Приложение представляет собой реализацию паттерна Front Controller – единая точка входа в приложение для всех запросов. В архитектуре Bun будет предусмотрено, что все запросы, включая php cli или запросы статичных файлов должны обрабатываться внутри Application. Экземпляр приложения будет создаваться в index.php (стартовом файле), при инициализации приложения будет указываться его среда исполнения (production, development, testing, etc). Уже приложение дальше будет выполнять работу по загрузке конфигурации, инициализации контейнера сервисов, роутингу запросов через вспомогательные компоненты, вызов контроллеров, выдачу ответа и завершение приложения.
Первое, что делаем приложение при старте, как-правило -– загружает файлы конфигурации, поэтому следующим базовым компонентом будет Config
Bun\Core\Config
Для конфигурации приложения и компонентов фреймворка выделен отдельный набор классов – Bun\Core\Config\ApplicationConfig – непосредственно сервис для управления конфигами и класс Bun\Core\Config\AbstractConfig – абстрактный класс, являющийся базом классом для элементов конфигурации. В качестве формата конфигурации выбран PHP-класс. Это удобно с точки зрения кэширования, т.е. выгодней хранить конфиги непосредственно в коде приложения, чем использовать отдельные файлы форматов xml, json, ini и прочее.
Использование классов в качестве конфигов также удобно и для разделение конфигураций отдельных компонентов и частей приложения. А также удобно для переопределения настроек фреймворка по-умолчанию внутри приложения, или настроек приложения внутри конкретной среды исполнения. По задумке каждый модуль или приложение содержит внутри себя пространство имен Config – в котором и складываются классы конфигураций. Каждый класс имеет свое пространство имен, а параметры конфигурации хранит в виде массива в защищенном свойстве.
Доступ к конфигам предполагается через dot-нотацию $config->get('name1.name2.param1')
.
После того, как мы инициализировали конфигурацию приложения, можно приступать к обработки запроса. Для работы с запросами и ответами веб-приложений выделен отдельный набор компонетов Http
Bun\Core\Http
Набор компонетов Http будет отвечать за абстрагирование от работы с суперглобавльными переменными $_SERVER, $_GET, $_POST и пр. За это будет отвечать сервис Bun\Core\Http\Request. Кроме того, в Http войдет класс Response - который будет стандартом результата работы приложения, т.е. запуск контроллера должен завешать получение объекта Bun\Core\Http\Response. Данный класс абстрагирует нас от работы с http-заголовками и пр. Кроме того, удобно использовать производные классы, типа AjaxResponse, DownloadResponse, ConsoleResponse и т.п.
Когда мы готовы получить информацию о запросе, можем переходить к роутингу: следующий компонент – Router
Bun\Core\Router
Router - стандартный компонент современных веб-приложений на php. В Bun Framework роутере не предвидится ничего экстра ординарного, простой конфиг в виде массива шаблонов url-запросов, который маппится на классы контроллеров и их экшенов. Я планирую реализовать возможность разбирать параметры из url вида /page/view/:page_id
– которые будут передаваться в экшен контроллера в виде аргументов. Также планирую сделать разделение запросов по методу (удобно, когда некоторые методы можно вызывать только через POST - не нужно делать лишних проверок в коде бизнес-логики)
От одного стандартного компонента php приложений переходим к другому – роутинг запросов тесно связан с реализацией паттерна MVC (Model View Controller). Здесь предполагает разделение логики приложения, данных и отображения.
Bun Framework MVC
В реализации MVC трудно особо отличиться – поэтому тут тоже все довольно прозаично. Внутри Core я выделаю пространство имен Controller – здесь создаю базовый класс контроллера, который имеет доступ ко всем копонентам системы, хранит в себе объект запустившего его приложения, сервис конфигов. При инизациализации контроллер получает параметры запуска: имя метода (экшена) и его аргументы.
Код отображения я выделяю в отдельную директорию View внутри Core. Bun Framework не предусматривает каких-то определенных компонентов типа View – подразумевается, что вы просто дергаете нужный шаблон внутри контроллера, передавая туда данные. По-умочанию я предполагаю запить в фреймворк поддержку шаблонизатора Twig и поддержку нативных шаблонов *.phtml
Последний компонент – Модель. Для нее также я выделяю отдельный namespace внутри Core: Model. Здесь в дело будет вступать еще один базоый компонент фреймворка – ObjectMapper. Сами по себе модели – просто классы, т.е. не являются ActiveRecord, но реализуют при это определенный Bun\Core\Model\ModelInterface. Именно с такими классами умеет работать ObjectMapper и сохранять их в какое-нибудь хранилище.
Теперь придется рассказать и про маппер объектов и про хранилище, начнем с первого.
Bun\Core\ObjectMapper
Маппер объектов – пожалуй, самая сложная часть модуля Core фреймворка. Он будет представлять собой сервис, который умеет превращать объект модели в записи в какой-нибудь базе данных, а также делать обратное действие – брать запись из хранилища данных и маппить ее в объект модели. По-умолчанию в модуль Core войдет сервис, реализующий файловое хранилище объектов.
Bun\Core\Storage
Набор компонентов Storage (Хранилище) представляет собой абстракцию и интерфейсы, которым дожно следовать реализации любых хранилищ в приложении. Первым таким хранилищем будет Bun\Core\Storage\FileStorage. Для своей работы файловое хранилище будет использовать набор вспомогательных классов для работы с файлами, а также для построение запросов на поиск записей в хранилище файлов.
Описанный выше маппер объектов сможет работать с любой реализацией Storage, чтобы сохранять там свои объекты. Здесь стоит выделить еще один важный компонент модуля Core – Repository.
Bun\Core\Repository
Репозитории – слой доступа к объектом. Чтобы не перегружать маппер объектов функционалом поиска и различных выборок объектов из хранилища - данная работа передана в репозитории. Репозиторий может работать напрямую с хранилищем, выбирать от туда данные, а затем через ObjectMapper превращать их в объекты и передавать приложению.
Сразу умпомяну связанный с вышеописанными компонентами – кэш
Bun\Core\Cache
Cache – будет содержать набор абстракций для реализации взаимодействия с различными кэш-хранилищями, типа Redis, Memacached и т.п. Кроме того, в модуль Core будет включен компонент FileCacheDriver, который реализует кэшированние данных в файлах.
Тут переходим к важному и, как я считаю, определяющему архитектуру, компоненту фреймворка. Гибкость и заменяемость компонентов достигается, когда ваш код не привязан жестко к опереденным сервисам, а может быстро между ними переключаться. Помимо того, следующий компонент делает огромную работу по грамотной организации кода фреймворка и приложения.
Bun\Core\Container
Container – реализует один из моих любимых паттернов в программировании – Dependency Injection. Внедрение зависимостей хорошо во всем – части приложения являются слабо зависимыми от конкретных компонентов, отдельные классы легко тестировать, подменяя их зависимости. Удобно реализовывать альтернативные варианты реализации в нескольких сервисах, а потом легко переключаться между ними даже без изменений в коде приложения. Плюс ко всему ваши классы, использующие внедрение зависимостей наглядно показывают свои зависимости – вы даже можете построить граф зависимостей компонентов вашего приложения.
Container инициализирует и хранит сконфигурированные сервисы в ходе рантайма приложения, когда к этим сервисам обращается приложение. Внутри одного класса вы получаете по сути контроль за всеми частями приложения.
На этом базовые компоненты фреймворка в модуле Core пока закончены. В ходе реализации в Core модуль возможно будут включены реализации управления событиями, FormBuilder. Среди вспомогательных комопнентов стоит отметить типизацию исключений. Базовый класс исключений Bun\Core\Exception\Exception - предусмотрен для того, чтобы все остальные типизированны исключения внутри приложения и фреймворка наследовались от него. Это обеспечивает централизованный перехват исключений на уровне приложения и препятствует возникновению не перехваченных исключений и авварийному падению приложения.
Автор Yakov Akulov
Комментарии (1) написать
Yakov Akulov
Тестирую комментарии через oAuth GitHub'а.
Теги тоже работают ок jakulov.ru
Ответить
Написать комментарий: