Цель данной статьи — поделиться опытом по написанию простого ООП MVC PHP фреймворка. Так же хочу предоставить сообществу исходный код и попросить критики, одобрения, замечаний и поддержки.
Введение
По ходу эксплуатации различных современных фреймворков я понял, что недостаточно понимаю, как все устроено внутри; не осознаю, почему разработчики выбрали то или иное решение; обращаю внимание только на текущую задачу и не смотрю «выше, глубже и дальше». И, как вариант для профессионального роста, я выбрал создание собственного проекта.
Как показала дальше практика: читать, знать, слышать о чем-либо, и уметь самому это реализовать — совершенно разные вещи. Теоретизировать можно бесконечно, но только настоящее практическое задание позволяет понять, на каком уровне ты находишься. Всвязи с этим и было начато «написание собственного велосипеда». Каким он получился — судить вам )
Процесс разработки
Разработка фреймворка велась следующим образом: изначально было некое простейшее веб-приложение, которое дорабатывалось, переписывалось, меняло структуру, обрастало новыми классами и компонентами и т. д. Несколько месяцев назад у меня появилось свободное время, много энтузиазма и решительности таки доделать фреймворк «по-серьезному». На написание данной версии я потратил в совокупности 3-4 недели (работая в среднем 1-3 часа в день, 3-4 дня в неделю).
По мере разработки стандарты и требования я сознательно завышал для себя, искал оптимальные решения много раз переписывал код. Так, например, работу с конфигурацией я переделывал раз 5-6 (причем несколько раз кардинально), роутинг — 3-4 раза. В качестве примеров я брал код из статей, публикаций, руководств, фреймворков (Yii2, CodeIgniter, Zend, Phalcon, Bun) и т. п.
Анализ требований
Все начинается с анализа требований и пожеланий к итоговой системе.
Фреймворк должен:
- позволять быстро создать сайт «с нуля»
- иметь в себе ряд уже реализованных базовых технических решений и инструментов
- содержать разделенный frontend и backend
- отвечать современным требования по коду, технологиям, применяемым техническим решениям и т.п.
- содержать уже в базовой комплектации демо-приложение, на основе которого можно вести свою разработку
- быть модульным и расширяемым
- иметь понятную документацию, техподдержку (в идеале — сообщество)
Применяемые технологии
Список таких решений напрямую зависит от того, о чём знаете и что применяете в своей реальной практике. Т.е. подходим к необходимости постоянного мониторинга новинок и изменений в сфере ИТ.
Практики и технологии:
- Язык программирования: PHP >= 5.5.* или PHP >= 7.*
- База данных: MySql >=5.4.*
- Менеджер пакетов: Composer
- Автозагрузка: PSR-4, кодирование: PSR-2, логирование: PSR-3
- Используем функционал языка: неймспейсы, трейты, магические функции и т.п.
- Применяем паттерны при построении структуры классов, реализации задач
- Верстаем html-код с использованием Twitter Bootstrap
- Пользуемся лучшими подходами в программировании: SOLID, DRY, KISS, YAGNI
- Покрываем код PhpUnit-тестами (тестирование работы базовых классов приложения)
- покрываем функционал Codeception-тестами (приемочное тестирование)
Структура папок
Приведу структуру файлов и папок в фреймворке (также можно посмотреть код на GitHub):
Код
В приложении есть одна единственная точка входа. Привожу код файла index.php из корневой публичной папки веб-сервера.
session_start();
$loader = require(__DIR__ . '/../../vendor/autoload.php');
$loader->addPsr4('framework\', __DIR__ . '/../../system/');
$loader->addPsr4('frontend\', __DIR__ . '/../');
$loader->addPsr4('common\', __DIR__ . '/../../common/');
$config = array_merge(
require(__DIR__ . '/../config/main.php'),
require(__DIR__ . '/../../common/config/main.php')
);
$appication = new frameworkcoreApplication();
$appication->run($config);
Код метода run($config) из класса frameworkcoreApplication(). Производится загрузка необходимых классов приложения и производится вызов соответствующего контроллера (в методе execute()).
/**
*
* @param array $config
*/
public function run($config = [])
{
$this->benchmark = new Benchmark();
$this->environment = Environment::get();
$this->config = new Registry($config);
$this->response = new Response();
$this->request = Request::getInstance();
$this->assets = new Asset($this->config->assets);
$this->setParams();
$this->router = new Router($this->config->routes);
$this->execute();
}
Код метода execute() из класса frameworkcoreApplication(). Нужный контроллер на данном этапе уже выбран, производим инициализацию этого контроллера, обработку хеадеров, вывод контента. В случае ошибки — бросаем 404 Not Found.
public function execute()
{
$controllerName = $this->router->getControllerName();
try {
$controllerClass = '\' . $this->config->name . 'controllers\' . $controllerName . 'Controller';
if (class_exists($controllerClass)) {
$controller = new $controllerClass;
if ($controller instanceof Controller) {
$controller->setApplication($this)->run();
}
} else {
throw new CoreException('Controller "' . $controllerName . '" not exists: ' . Request::getInstance()->server["REQUEST_URI"]);
}
} catch (CoreException $e) {
$e->logError();
$this->response->setHeader("HTTP/1.1 404 Not Found");
$this->router->error404();
$this->execute();
exit();
}
foreach ($this->response->getHeaders() as $header) {
header($header);
}
echo $this->response->getContent();
}
Улучшения и планы на будущее
В качестве адаптера для коннекта к БД я использовал PDO. В ходе работы PDO мне не очень понравился — сложно отлаживать запросы, хочется комфорта использования ORM. Можно установить Eloquent ORM — это современное и готовое решение (применяется в фреймворке Laravel), да и к тому же оно хорошо документировано и может быть установлено из composer за несколько минут.
Так же думал о расширении базового функционала фреймворка: хотел добавить поддержку модулей. Т.е. чтобы можно было написать например, блог как отдельный модуль (со своими контроллерами, вьюверами, моделями и т.п.). И потом подключать этот модуль в любом месте приложения.
Можно расширять и базовый «джентельменский» набор классов в ядре, усложнять систему логирования, обработки ошибок, конфигурирования, писать полноценный демо-сайт со всем функционалом и т.д.
Заключение
Буду рад услышать критику кода, архитектуры, изначальных требований и прочего. Комментарии буду активно читать, постараюсь ответить на вопросы.
Так же хотелось бы реализовать какой-либо проект на базе данного фреймворка. Так сказать опробовать инструмент в работе. Понятно, что поиск заказчиков — это совсем не к данному разделу, но хотел бы услышать, можно ли на данном решении стартовать реальный проект? Популярные фреймворки «из коробки» дают функционал в 50-100 раз качественнее и масштабнее, конкурировать с ними сложно.
Автор: volmir