Простой, современный MVC фреймворк на PHP для разработки сайтов «с нуля»

в 10:47, , рубрики: cmf, framework, 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):

image

Код

В приложении есть одна единственная точка входа. Привожу код файла 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 раз качественнее и масштабнее, конкурировать с ними сложно.

Код на GitHub

Автор: volmir

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js