Meet speakers from Tinder, IBM, Siemens, Indeed and other QA Rockstar’s on …Testing Stage, Save $30 till 28/02
×Закрыть

Реализовываем простенький mvc-фреймворк (Часть 1)

Думаю, не стоит говорить, что каждый php-разработчик (без разницы с каким опытом) писал (пишет?) велосипеды фреймворки штучку под названием mvc.

Уверен, что многие все же смогли это сделать и теперь они довольны. Если это не про Вас — можно читать дальше.

В данном цикле постов я хотел бы поделиться с Вами своим опытом по созданию такого велосипеда.

За идею я взял laravel (если точнее — методы, классы, названия и другое) и попытался сделать что-то подобное.

P.s: наш mvc проект уже готов, поэтому вы будете встречать названия классов, методов которых еще не создавали. Все будет создано в дальнейшем. Не переживайте.

В комментариях к методу может быть @throws ModuleName — Не обращайте внимание, мы будем использовать \Exception

Приступим

Рассказывать Вам теорию я не особо хочу, любой, кому это интересно, может прочесть википедию, так что, приступаем.

Начальная архитектура:
создаем папки: app — папка нашего приложения, тут будут лежать контроллеры, модели и т.п, core — «сердце» нашего фреймворка (его система), public — точка входа.

Папки создали, теперь нужно инициализировать composer.
Открываем консоль/терминал, заходим в папку с проектом и пишем composer init.
Package name (я привык использовать свой логин и название репозитория на гитхабе): login/repository-name;
Description: enter;
Author: n,
Minimum Stability: dev
Package Type: project
License: enter,
Would you like to define...: n
Do you confirm generation: enter

Итак, у нас должен был появится файлик composer json, заходим в него и ниже «require»: {} пишем следующее:

"autoload": {
   "psr-4": {
      "app\\": "app/",
      "core\\": "core/"
   }
}
Теперь нужно запустить команду composer update. У нас появится папка vendor.
Отлично. Теперь мы можем приступать к написанию нашего приложения :)

Ну что же, наш сервис контейнер будем таким:

public static function getInstance(): ServiceContainer; // Реализация синглтона
public function set(string $key, $object): void; // Метод для добавления нового класса
public function createAlias(string $key, string $alias): mixed; // Реализация class_alias php
public function onlyLoadClass(array $classes): void; // Будем использовать в дальнейшем
public function bildClass(string $key, $params = null): object; // Возвращаем объект класса по названию
private function instance($key, $parameters = null): object; // Занимается созданием объектов

А вот так наш класс будет выглядеть с реализованными методами:
просмотр

Отлично, сервис контейнер готов. Теперь идем в папку public и создаем там файл index.php с таким содержанием:

require '../vendor/autoload.php';

(new \core\Application)->run();
Затем: гугл -> .htaccess index.php -> копируем -> вставляем в public/.htaccess. Для тех кто не понял: создаем .htaccess в public и суем туда правила редиректа на index.php (входная точка).

Займемся главным файлом нашего приложения (запуск нашего приложения).
Предлагаю вам такую версию:

public function run(): void;
private function loadAliases(): void; // Будем использовать в дальнейшем
Реализация класса

И тут у нас появляется множество не понятных методов и классов. Надеюсь, вы прочли " p.s «?

Теперь создаем папочку ’config’ в главной директории нашего приложения. Создаем файл app.php и пишем следующее:

<?php

return [
    // Классы которые будут загружены при запуске приложения
    'required' => [
        \core\collection\Collection::class,
    ],

    // Алиасы для классов
    'aliases'  => [
    ]
];
В дальнейшем у нас тут появится еще множество других настроек, но сейчас будет достаточно этого. Для тех кто не понял — ’required’ и ’aliases’ используются сервис контейнером при запуске. Аналогичная логика и у Laravel.

Время для функций :)
Создаем папку helpers в core и создаем файл functions.php
Идем в composer.json и ниже «psr-4» пихаем:

"files": [
  "core/helpers/functions.php"
]

А вот и само содержание файла functions.php

<?php

function app()
{
    return \core\container\ServiceContainer::getInstance();
}

function config()
{
    return app()
        ->set('config', '\core\config\Repository')
        ->bildClass('config');
}

function route()
{
    return app()->bildClass('Route');
}

function collect(array $data = [])
{
    return app()->bildClass('Collection', $data);
}

Догадались что это? Да, правильно. Это аналогия app(), config(), route() и т.п в Laravel’e.
Теперь многое выше стало проясняться, не так ли?

Сейчас создадим один файлик и опишем его, для чего он — сами поймете, он потребуется нам в дальнейшем.
core\collection\Collection.php
Реализация класса

Этот файл мы обсудим позже, сейчас просто скопируйте его.

Ну что же, самое время для системы роутеров.
Начнем с файлов
core\routers\BaseRoute.php;

protected static $routers = []; // Все роутеры
protected static $patterns = [
        '{integer}' => '[0-9]+',
        '{string}'  => '[a-zA-Z]+',
        '{any}'     => '[^/]+',
    ];

public static function get(...$arguments): void;
public static function post(...$arguments): void;
private static function addRouter(string $method, $arguments): void;
public function startRoute();
private function initRout($matches);
private function initNotFoundRout();

Реализация класса

core\routers\Router.php;

Реализация класса

core\traits\Router\ParseUrl.php

public static function parse(string $key): array;
public static function replaceUrl(string $key);

Реализация класса

core\traits\Router\RouteHelper.php

public function returnCurrentUrl($uri = false): string;
public function removeSlashes(string $uri, $currentUrl): string;
private function replacePreg($uri);

Реализация класса

Возвращаемся в config/app.php и в aliases пишем:

'Route' => \core\routers\Router::class,

Ну что же, теперь самое время тестировать нашу систему роутеров.
Создаем папку routers в app и создаем файл main.php (app\routers\main.php)

Проверяем работу:

Route::get('/', function() {
    echo 'Главная страница';
});

Можете поиграться

Route::get('/{string}/name{integer}-{any}');

На этом все. В дальнейшем займемся остальными компонентами системы.

LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Я, конечно, дико извиняюсь, но пока Ваш туториал по MVC без MVC больше напоминает туториал по Ларавелю без Ларавеля.

Представим на сек, что laravel не существует.

Итак, у нас должен был появится файлик composer json, заходим в него
Если уж статья для новичков, то хотя-бы дайте ссылку на composer, не говоря уж о кратком описании.
public static function getInstance(): ServiceContainer; // Реализация синглтона
private function instance($key, $parameters = null): object; // Занимается созданием объектов
Прежде чем учить, как сделать singleton, надо его нормально сделать, хотя-бы по общепринятым правилам. В приведенном примере нет его, серьезно.
Отлично, сервис контейнер готов.
Ээ, так это service container, или все-таки singleton?
// Классы которые будут загружены при запуске приложения
А зачем тогда вообще autoload из psr? Может ну его, и так все ручками подключаем.
Время для функций :)
В общем делаем такой красивый mvc oop framework, потом короче психанули и решили нагадить вот такими функциями, чтобы жизнь медом не казалась.
bildClass
Немецким увлекаетесь?
’{string}’ => ’[a-zA-Z]+’,
С кодировками пусть слабаки разбираются. А если серьезно, string — не к месту, у этого типа определение другое.
function collect(array $data = []) { return app()->bildClass(’Collection’, $data); }
Читаю название метода — думаю, что-то собирает. Читаю тело метода — оказывается лох я.
public function returnCurrentUrl($uri = false): string; public function removeSlashes(string $uri, $currentUrl): string; private function replacePreg($uri);
Если уж начали использовать функционал строгой типизации, то надо везде. И по поводу false в аргументах подсказка — method(?Foo $foo): ?Bar {} (все-равно в примерах 7.1)
Алиасы для классов
Просто вопрос: а зачем? Какая-то обратная совместимость?

P.S. Да, хам. По ссылкам код не смотрел, описал что на глаза попалось.

Вы странные, те, кто пишет на пхп... Вот было у вас echo($shljapa). Ну, отлично. Полно простеньких задач под это дело. Пользуйтесь. Нет же! Смотришь на это нагромождение, которое ниже, и не понимаешь, зачем они слона в холодильник трамбуют.
Для сложных вещей есть соответствующие инструменты. Зачем вам в пхп все это? Чтобы не учить другой синтаксис?
MVC.. Json отдали по-быстрому — клинтская часть разберется, что с ней делать. У вас всякие компиляции и акселерации не для того, чтобы вы с mvc извращались имхо )))

точно с той же целью что и в Java.

на хабре подобных статей уже как говна. Еще и тут. Такое впечатление что если не о чем написать пишут про очередной mvc фреймворк.

Думаю, не стоит говорить, что каждый php-разработчик (без разницы с каким опытом) писал (пишет?) велосипеды фреймворки штучку под названием mvc.
Не все. Не считаю MVC удачным патерном для веба. Именно поэтому все пытаются написать свой и ни у кого толком ничего не выходит.

esli JS developeru nehren delat’, on pishet framework

в данном случае PHP. Нынешнее яваскриптовое безумие особенно с клиенскими mvc фреймворками которые в свою очередь вешаются на серверные mvc фреймворки — отдельная клиника.

Где MVC? (ни модели ни вьюхи ни контроллера)
Есть фронтконтроллер — паттерн такой — почитай

Route::get(’/’, function() {
echo ’Главная страница’;
});

Ахтунг))
Статические вызовы это вообще ахтунг, а каллбак должен быть только в такси! Анонимные классы и анонимные интерфейсы? Не не слышал?
Где DI? Почему оно пытается ручками конфигурится?

->set(’config’, ’\core\config\Repository’)
->bildClass(’config’);

Хз что значит bild )
Изменение стейта объекта ручками через set -> зло

Все это будет позже. Насчёт стат — Аля ларка , вся идея на ней ведь.
Остальное не понимаю )

Никогда не понимал зачем делать клон фреймворка — ничего нового не вносит, а для учебы, если уж совсем делать нечего можно глянуть в исходники того же ларавел.

Не понимали — ну значит не Ваше :)
Для практики и понимания в целом (пхп) — очень хорошо.

Догадались что это? Да, правильно.
FALSE
Ты всерьёз считаешь, что те кому это нужно — с ходу в памяти представляют всю иерархию проекта? Садись, 0b10. Не умеешь объяснять. Человеческая память устроена иначе, чем хотелось бы. Там нет функции копировать-вставить, кнопки «всё понять быстро», и разумеется нет файловой системы. А есть меедленная и ненадёжная для записи шаблонная память, работающая по многофакторным асоциациям, генерируемых автоматически; куда более быстрая (но тоже ненадёжная) система узнавания (сравнения) шаблонов; и мощная система отсева мусора — считающая мусором всё что быстро не ассоциируется и не ожидается.

Если русским понятным языком, сначала покажи всё, потом объясняй детали. Мозг из деталей твой проект не соберёт, точно говорю, даже самый гениальный. А ты, я напомню, пишешь материал для начинающих. Этим ты напоминаешь мне преподов большинства ВУЗов — они тоже верят, что дают таким образом полезные знания.

Насчет «догадались» — те кто работали с laravel запросто угадают.
Да и я дальше даю пояснение что это такое (с точки зрения laravel’я).
Насчет всего остального — спасибо, учту.

То есть ваша статья только для знатаков laravel? Стоило бы вначале об этом упомянуть.

Подписаться на комментарии