Middleware

Оглавление

1. Введение
2. Примеры построения
3. Использование
4. Группировка
4. Список методов




Введение #
К оглавлению

HTTP Middleware (посредники) - это фильтры обработки HTTP-запроса. Вы можете запускать код до и после вашего приложения, чтобы управлять объектами Request и Response по своему усмотрению.

Работают мидлвары несколько непривычно. Если в привычном режиме команды запускаются слева направо, или сверху вниз, то middleware работают наоборот, по принципу стека. Первым вошел - последним вышел. В обычном приложении функции передают результат друг другу по очереди, вместе с запуском. Мидлвары наоборот, получают результат после запуска следующего. При этом они могут обрабатывать результат между вызовами и передавать дальше. Графически можно представить это так:



Результат, передаваемый между мидлварами, упаковывается в объект Response. В нашем фреймворке он отвечает требованиям стандарта PSR-7.

Исторически сложилось так, что многие приложения и фреймворки пользовались достаточно популярной сгнатурой с применением магии __invoke(). Допустим фреймворк Slim использует калбэк с тремя аргументами:

1. \Psr\Http\Message\ServerRequestInterface - Объект запроса PSR7
2. \Psr\Http\Message\ResponseInterface - Объект ответа PSR7
3. callable - Следующее middleware (промежуточное программное обеспечение), подлежащее вызову

Другие используют пока не принятую, но опубликованную в черновиках сигнатуру стандарта PSR-15.

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



Примеры построения #
К оглавлению

Пример реализации middleware по образцу фреймворка Slim на основе замыкания:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

/**
 * Example middleware closure
 *
 * @param   $request  PSR7 request
 * @param   $response PSR7 response
 * @param  callable $next     Next middleware
 *
 * @return $response PSR7 response
 */
function ($request$response$next) {
    
$response->getBody()->write('BEFORE');
    
$response $next($request$response);
    
$response->getBody()->write('AFTER');

    return 
$response;
};




Так же сработает такой мидлвар (тоже по образцу Slim)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class ExampleMiddleware
{
/** 
 * Example middleware closure 
 * 
 * @param   $request  PSR7 request 
 * @param   $response PSR7 response 
 * @param  callable $next     Next middleware 
 * 
 * @return $response PSR7 response 
 */ 
    
public function __invoke($request$response$next)
    {
        
$response->getBody()->write('BEFORE');
        
$response $next($request$response);
        
$response->getBody()->write('AFTER');

        return 
$response;
    }
}




Еще вариант по стандарту PSR-15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

class ExampleMiddleware
{
/** 
 * Example middleware PSR15 
 * 
 * @param   $request  PSR7 request 
 * @param  callable $handler     Next middleware 
 * 
 * @return $response PSR7 response 
 */ 
    
public function process($request$handler)
    {
        
$response $handler($request);
        
$response->getBody()->write('AFTER');

        return 
$response;
    }
}




Возможен вариант анонимной функции с двумя параметрами сигнатуры PSR-15 (как её использует фреймворк Zend Expressive)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/** 
 * Example middleware closure PSR15 
 * 
 * @param   $request  PSR7 request 
 * @param  callable $handler     Next middleware 
 * 
 * @return $response PSR7 response 
 */ 
function ($request$handler)   
{
        
$response $handler($request); 
        
$response->getBody()->write('AFTER'); 

        return 
$response;   
}






Использование #
К оглавлению

Есть три варианта использования middleware в нашем фреймворке. Можно добавить их после отработки приложения, запущенного с обычным роутингом:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php  
 
    
require __DIR__ .'/../vendor/ABC/ABC.php';   
    
$config = require __DIR__ .'/resources/config.php';  
     
    
$app ABC::startApp($config); 
    
// Добавляем middleware с помощью метода add() 
    
$app->add(function ($request$response$next) {  
        
$response->getBody()->write('BEFORE');  
        
$response $next($request$response);  
        
$response->getBody()->write('AFTER');  

        return 
$response;    
    });  
     
    
$app->run(); 




А так же можно использовать их с REST-роутингом. Посредством того же метода add().
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
<?php   

    
require __DIR__ .'/../vendor/ABC/ABC.php';  
    
$config = require __DIR__ .'/resources/config.php'
    
    
$router ABC::Router($config);
    
// Добавление middleware методом add() цепочкой
    
$router->get('/hello/{name}', function ($request$response$arg) {
        
$response->getBody()->write("Hello, "); 
        return 
$response
    })->
add(new NameMiddleware); 
    
    
$router->run();
    
///////////////////////////////////////////////////////    
/**
*   Класс миддлвара
*/    

class  NameMiddleware  
{  
    public function 
process($request$handler)   
    {
        
$response $handler($request);
        
$name $request->getAttribute('name'); 
        
$response->write("$name!");
        return 
$response
    }  
}  




Добавить несколько мидлваров можно либо вызвав метод add() несколько раз (можно цепочкой), либо передав их массивом:
1
2
3
4
5
6
7
8


    
// Так
    
})->add(new NameMiddleware)->add(new SomeMiddleware);
 
    
// Либо так
    
})->add(['NameMiddleware''SomeMiddleware']);     




Кроме того, можно воспользоваться сервисом PIPE, и использовать мидлвары внутри приложения, самостоятельно, используя сервис HTTP:



Группировка #
К оглавлению

Если требуется добавить мидлвары к нескольким маршрутам, можно воспользоваться методом group().
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

 
    $router 
ABC::Router($config);
    
    
// Добавление middleware для нескольких маршрутов
    
$router->group('/hello', function () use ($router) {
        
        
$router->get('/hello/World', new HelloWorld); 
        
        
$router->get('/hello/{name}', new HelloName);

    })->
add(new SomeMiddleware);    
    
    
$router->run();




Он автоматически добавится к любому сработавшему роуту группы.

Методы для работы с мидлварами #
К оглавлению

1 add() Добавляет мидлвары в очередь
2 group() Группирует роуты для добавления мидлваров




add() public method
К списку методов

Добавляет в очередь мидлвары.

Метод добавит во внутреннюю очередь фреймворка один или несколько (массивом) middleware.

public App::add ( $middleware )
$middleware mix Метод принимает параметром:
1. Валидный collback (замыкание или название функции)
2. Invokable class (объект или название класса, содержащего метод __invoke())
3. Объект или название класса, отвечающего интерфейсу PSR-15
4. Массив, элементами которого являются вышеперечисленные аргументы в любых вариантах.
return object Объект приложения




group() public method
К списку методов

Группирует роуты.

Организует группу роутов, при отоработке любого из них, добавляет мидлвары.

public App::group ( $rout, $collback )
$rout string Префикс маски роута, после которого организуется группа для добавления мидлваров
$collback mix Сollback-функция, содержащая сгруппированные роуты.
return object Объект приложения