Блог


Middleware в PHP

Последнее время становится модным и популярным новое течение - мидлвары (middleware). Набирают обороты целые фреймворки, ориентированные на них, такие как Slim, Silex, Zend-expressive и другие. Одним из них, кстати, является наша разработка,  ABC-framework.

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

В википедии довольно сухое и невнятное определение:

Промежуточное программное обеспечение (middleware) — это класс программного обеспечения, предназначенного для объединения компонентов распределенного клиент-серверного приложения или целых сетевых приложений в единую информационную систему

 В других местах иначе, но не многим лучше. Допустим в документации Slim:

Slim добавляет промежуточное ПО как концентрические слои, окружающие ваше основное приложение. Каждый новый слой промежуточного программного обеспечения окружает любые существующие уровни промежуточного программного обеспечения. Концентрическая структура расширяется наружу, когда добавляются дополнительные слои промежуточного слоя.

Давайте попробуем разобраться на простой ассоциации.

Представим себе бассейн. Не спортивный комплекс, а сам пруд с водой в этом комплексе.

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


Себя представим запросом: мы же приперлись и хотим плавать. 

А теперь представим гардероб в этом комплексе. Это и есть первый мидлвар. Ты должен раздеться, сдать пальто.

Потом второй мидлвар - раздевалка, где ты останешься в одних трусах. Но в раздевалку нельзя в пальто. И даже если ты оставил его в машине, без гардероба не обойтись - там выдают ключ от шкафчика. А потому его обязательно нужно запустить (пройти).

Затем третий - вахта с теткой в очках и белом халате. Которая проверяет абонимент и наличие шапочки. К ней только в трусах, а значит первые два нужно пройти, никуда не денешься.

Затем четвертый - душ. Грязному плавать - моветон. И чесотка.

И только потом основное действие.

Начинается все не с основного действия, а с последнего. С гардероба. Именно из него ты выйдешь на улицу. Когда закончил плавание, возвращаться будешь в обратном порядке. Бассейн -> душ  -> тетка (забрать абонимент) -> раздевалка (теперь одевалка) -> гардероб. Логично запускать гардероб первым, хотя в списке действий он же и последний.

Вот так примерно работают мидлвары. Они окружают основное приложение (бассейн). И каждый выполняет свое действие. Тетка может тебя не пустить, если нет абонимента, а может отправить в ларек за шапочкой, если её нет. В раздевалке может не хватить шкафчиков, и тебе придется ждать. И так далее. Важно то, что тебе самому, никому другому, придется пройти через это окружение туда и обратно.

Только туда ты идешь как Request, а обратно, как мокрый и довольный Response.

Теоретически ты можешь занырнуть прям в пальто, но для того и делаются мидлвары, чтобы было все красиво и по правилам. Принцип мидлваров - где зашел, там и вышел. Потому что они как бы вокруг основного действия.

В классическом случае все не так. Там нет "закольцованности", команды выполняются последовательно. Вход в одном месте, выход в другом, всё, что между ними, и есть приложение. Графически можно представить это так:


Ну а теперь немного кода, чтобы было совсем понятно. Для начала сделаем два класса, основу. Запрос - ответ, Rquest, Response.

1
2
3
4
5
6
7
8
9
10
11
12
<?php 

class Request
{
    public 
$name;
}

class 
Response
{
    public 
$content;
}


 


Это все очень упрощенно, для наглядности. Вообще эти два класса регламентируются "стандартом" PSR-7, вот тут подробнее.

Теперь изобразим подобие приложения. Оно должно принимать на вход Request и возвращать Response. Можно допустим так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php 

class App
{
    public function 
run($request)
    {
        
$response = new Response;
        
$response->content 'Меня зовут '$request->name;
        return 
$response;
    }
}

    
$request = new Request;
    
$request->name 'Вася';
    
    
$app = new App;
    
$response $app->run($request);
    echo 
$response->content;


 



А теперь нам захотелось себя похвалить. Для этого можно использовать мидлвар. Причем совершенно не касаясь кода приложения.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


class Middleware 

    protected 
$app;

    public function 
__construct($app)
    {
        
$this->app $app;
    }

    public function 
process($request
    { 
        
$response $this->app->run($request); 
        
$response->content .= ', я - молодец!'
        return 
$response
    } 



    
$request = new Request
    
$request->name 'Вася'
    
    
$app = new App;
    
// Оборачиваем приложение мидлваром 
    
$middleware = new Middleware($app); 
    
$response $middleware->process($request); 
    
// теперь это модифицированный Response 
    
echo $response->content



Можно продолжать в том же духе, пока не появится нимб :) Оборачивая один мидлвар другим. Но нужно помнить, что выполняться они должны в обратном порядке. Первым выполнится последний, запуская перыдущий. Результат вернется обратно по цепочке:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


// Добавим еще мидлвар, себя не похвалишь, сидишь как оплеваный 
class MoreMiddleware 

    public function 
process($request$handler
    { 
        
$response $handler->process($request); 
        
$response->content .= ' Ух какой молодец!!!'
        return 
$response
    } 



    
$request = new Request
    
$request->name 'Вася'
    
$app = new App;

    
// Продолжаем хвалить себя любимого. 
    // Оборачиваем первый миддлвар еще одним:         
    
$middleware = new MoreMiddleware;     
    
$response $middleware->process($request, new Middleware($app)); 

    echo 
$response->content




И так далее. Немного похоже на декораторы, но с более жесткими условиями.

Здесь показаны мидлвары сигнатуры PSR-15. Этот "стандарт" пока не принят и имеет хождение только в черновиках. Так что зачастую можно встретить различные реализации: замыканиями, invokable  (объект класса, содержащего метод __invoke(), с тремя аргументами и так далее. Но принцип у них один. Они должны передавать друг другу Request и возвращать Response. Которые регламентируются интерфейсами PSR-7.


Мидлвары удобны для различных фильтров, авторизаций, логирования, заглушек, и много еще для чего. Сейчас на фреймворках типа Slim и иже с ними, пишут приложения полностью на мидлварах, хотя это многими порицается и считается на грани говнокодинга. Оно и верно, все хорошо на своем месте, чрезмерное увлечение мидлварами ни к чему хорошему не приведет.







Теги:

Комментарии (1)

Михаил
03-04-2018
Спасибо!

 
Наверх