- Простой, современный MVC фреймворк на PHP для разработки сайтов «с нуля»
- Введение
- Процесс разработки
- Анализ требований
- Применяемые технологии
- Структура папок
- Код
- Улучшения и планы на будущее
- Заключение
- Разработка своего MVC фреймворка на PHP
- Точка входа
- Настраиваем htaccess
- Автозагрузка классов
- Пишем мини MVC фреймворк на PHP #1
- Маршрутизация
- Итоги
Простой, современный MVC фреймворк на PHP для разработки сайтов «с нуля»
Цель данной статьи — поделиться опытом по написанию простого ООП 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 \framework\core\Application(); $appication->run($config);
Код метода run($config) из класса \framework\core\Application(). Производится загрузка необходимых классов приложения и производится вызов соответствующего контроллера (в методе 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() из класса \framework\core\Application(). Нужный контроллер на данном этапе уже выбран, производим инициализацию этого контроллера, обработку хеадеров, вывод контента. В случае ошибки — бросаем 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 раз качественнее и масштабнее, конкурировать с ними сложно.
Разработка своего MVC фреймворка на PHP
В предыдущих уроках вы тренировались использовать готовый MVC фреймворк. Как правило, этим PHP программисты и занимаются — берут один из популярных готовых фреймворков, изучают его, а потом используют. В настоящее время есть 3 самых популярных фреймворка: Laravel, Yii и Symfony.
Полученных вами знаний уже достаточно для изучения настоящих фреймворков. При желании или при недостатке времени вы можете не проходить дальнейшие уроки данного учебника по ООП, а перейти сразу на фреймворки.
Далее в данном учебнике мы с вами разработаем свой MVC фреймворк на PHP. Еще раз подчеркну, что данный этап уже не обязателен, но поспособствует закреплению ваших знаний ООП на практическом примере.
Итак, приступим к разработке. На самом деле вы будете разрабатывать не какой-то абстрактный фреймворк, а просто реализуете функционал того учебного фреймворка, который мы использовали в предыдущих уроках.
Вы уже знаете, какой функционал у вас должен быть. Осталось его реализовать. Приступим!
Точка входа
Для начала в корневой папке вашего домена сделаем файл index.php со следующим содержанием:
Создайте файл index.php и добавьте в него указанные строки.
Настраиваем htaccess
Теперь сделаем так, чтобы все запросы из адресной строки адресовались на наш файл index.php . То есть всё, что бы не вбил пользователь в браузер будет адресоваться на index.php , а код этого файла будет разбираться с тем, какую именно страницу показать пользователю.
Вот содержимое файла htaccess :
Вспомним теперь, что адреса из папки project/webroot не должны перенаправляться на индекс, так как там мы храним стили, скрипты, картинки и тому подобные вещи. Учтем это в нашем htaccess :
Создайте файл htaccess . Выполните описанную настройку. Проверьте ее работу: все адреса, кроме адресов из папки project/webroot должны редиректится на index .
Автозагрузка классов
Наш учебный фреймворк использует нестандартные правила для автозагрузки классов (расскажите, в чем отличия), поэтому нам придется реализовать свою автозагрузку в файле index.php :
Реализуйте функцию автозагрузки. Проверьте ее работу.
Пишем мини MVC фреймворк на PHP #1
Привет, хабр! В этой статье я хочу написать о том, как сделать PHP-приложение с помощью схемы разделения данных приложения MVC.
Для работы нам нужна функция под названием autoload. Она избавляет нас от бесконечных require’ов. Мы можем вручную написать скрипт, но эта функция есть у знаменитого пакетного менеджера composer.
После установки инициализируем его в главной папке нашего мини-фреймворка командой composer init. на всех вопросах нажимаем ENTER.
Далее заходим в появившийся файл composer.json. Удаляем все и добавляем это:
И наконец, выполняем команду composer update. На этом настройка composer завершена.
Теперь, создадим папку core и файл .htaccess. Еще нужно создать папку public и создать в ней файл index.php — точку входа в приложение.
В файл .htaccess нужно вписать следующее:
RewriteEngine on RewriteRule .* public/index.php
Все, что делает этот файл — переадресовывает любые запросы в index.php.
Потом, в папке core создаем класс Application. Помещаем в него этот код:
Здесь мы создаем пока что пустой класс и пространством имен app\core.
На этом этапе структура должна выглядеть так:
│ .htaccess
│ composer.json
│ composer.lock
│
├───core
│ Application.php
│
├───public
│ index.php
│
└───vendor
Отлично. Теперь давайте выполним первую задачу: маршрутизацию.
Маршрутизация
В папке core создаем класс Router.php и начнем писать код:
routes['get'][$path] = $callback; > public function post($path, $callback) < $this->routes['post'][$path] = $callback; > >
Мы создали переменную routes, в котором будут храниться все маршруты в таком формате:
Теперь нужно создать класс Request.php для получения урлов, методов запроса и так далее:
public function getMethod() < return strtolower($_SERVER['REQUEST_METHOD']); >>
Тут все просто: метод getPath служит для получения url без GET-параметров, а getMethod просто возвращает HTTP-метод. Модифицируем класс Роутера:
request = new Request(); > public function get($path, $callback) < $this->routes['get'][$path] = $callback; > public function post($path, $callback) < $this->routes['post'][$path] = $callback; > public function resolve() < $path = $this->request->getPath(); $method = $this->request->getMethod(); $callback = $this->routes[$method][$path] ?? false; if ($callback === false) < return "404"; >return call_user_func($callback); > >
Тут мы создаем экземпляр Request’а и метод resolve, который возвращает то, что вернул callback.
Возвратимся в класс Application и создаем метод run, который запустит Роутер:
router = new Router(); > public function run() < echo $this->router->resolve(); > >
Теперь, можем протестировать текущий функционал. Создадим папку public и в ней файл index.php:
router->get('/', function () < return "Hello, habr!"; >); $app->run();
В этой же папке запустим команду php -S localhost:8080 (или любой другой порт). Зайдем на localhost:8080 в браузере и о чудо! Мы увидим надпись Hello, habr!
Итоги
На этом первая часть подходит к концу. В ней мы задали каркас нашего будущего фреймворка. В следующей части мы реализуем View и Контроллеры
Это моя первая статья на Хабре, поэтому буду рад любой критике. Спасибо за прочтение и удачи!