Как метод handle() промежуточного программного обеспечения Laravel вызывается с использованием «Clousre $next» в другом промежуточном программном обеспечении?

Вот метод handle() из ValidatePostSize в Laravel:

public function handle($request, Closure $next)
{
    $max = $this->getPostMaxSize();

    if ($max > 0 && $request->server('CONTENT_LENGTH') > $max) {
        throw new PostTooLargeException;
    }

    return $next($request);
}

Теперь этот метод вызывается с использованием $next($request) для другого ПО промежуточного слоя. Насколько я понимаю, метод handle() преобразуется в $next. Я хочу знать, как это происходит под капотом.


person Mayank Kumar    schedule 13.02.2017    source источник
comment
посмотреть код?   -  person Vasil Shaddix    schedule 13.02.2017
comment
Это следующее закрытие промежуточного программного обеспечения в стеке.   -  person Magnus Eriksson    schedule 13.02.2017
comment
@MagnusEriksson посмотрите описание вопроса. Я знаю его закрытие, как он намекнул в методе. Я хочу знать, как метод handle() преобразуется в закрытие $next.   -  person Mayank Kumar    schedule 13.02.2017
comment
@MayankKumar мой ответ может помочь вам узнать, как работает Closure.   -  person Rendy Eko Prastiyo    schedule 13.02.2017
comment
@RendyEkoPrastiyo Я не спрашиваю, как работает закрытие. На самом деле это личиночный вопрос, и $next сопоставляется с методом handle() промежуточного программного обеспечения. Я хочу знать, как работает это отображение   -  person Mayank Kumar    schedule 13.02.2017
comment
Если вы понимаете, как работает Closure, вы поймете, почему он используется. Цель $next внутри handle состоит в том, чтобы делать определенные вещи, которые не должен делать сам handle. В случае ValidatePostSize next отвечает за выполнение условной проверки. Проверка — Laravel (см. раздел Комплексная условная проверка).   -  person Rendy Eko Prastiyo    schedule 13.02.2017
comment
Еще раз, на самом деле здесь происходит то, что в laravel есть много промежуточного программного обеспечения, и все они имеют метод handle(request $request, closure $next). Теперь $next в каждом из них сопоставляется с handle() другого. т. е. когда $next запускается, он запускает метод handle() другого промежуточного программного обеспечения.   -  person Mayank Kumar    schedule 13.02.2017


Ответы (2)


Чтобы передать запрос глубже в приложение (позволив промежуточному программному обеспечению «пройти»), просто вызовите обратный вызов $next с $request. https://laravel.com/docs/5.4/middleware#defining-middleware< /а>

Когда Laravel обрабатывает запрос, он запускает все применимое промежуточное ПО в стеке. Промежуточное ПО можно настроить для запуска до и/или после метода маршрута/контроллера.

Чтобы сделать это, Laravel использует Illuminate\Pipeline\Pipeline. По сути, он использует array_reduce для перебора стека промежуточного программного обеспечения, которое затем возвращает Closure для выполнения этого промежуточного программного обеспечения. Красота этого заключается в использовании array_reverse, позволяющего передать следующее выполнение промежуточного программного обеспечения предыдущему.


Чтобы уточнить немного больше:

Когда вызывается Illuminate\Foundation\Http\Kernel@handle, он создает ответ с sendRequestThroughRouter, в котором есть следующее:

return (new Pipeline($this->app))
            ->send($request)
            ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
            ->then($this->dispatchToRouter());

Pipeline — это Illuminate\Routing\Pipeline, который расширяет Illuminate\Pipeline\Pipeline.

Метод then() выше по существу:

->then(function ($request) {
    $this->app->instance('request', $request);

    return $this->router->dispatch($request);
})

Then означает, что мы начинаем с замыкания, которое принимает конечные результаты (вспомните, что замыкание еще не было вызвано).

Затем в методе then() происходит секция array_reduce и array_reverse, как указано выше.


Вот упрощенный пример того, что на самом деле происходит в методе then() (предполагается, что вы знаете, как работает array_reduce):

function then(Closure $destination)
{
    $pipeline = array_reduce(

        array_reverse($this->middlewares),

        //Remember $nextClosure is going to be the closure returned 
        //from the previous iteration

        function ($nextClosure, $middlewareClass) {

            //This is the $next closure you see in your middleware
            return function ($request) use ($nextClosure, $middlewareClass) {

                //Resolve the middleware
                $middleware = app($middlewareClass);

                //Call the middleware
                return $middleware->{$this->method}($request, $nextClosure);
            };
        },

        //The finial closure that will be called that resolves the destination

        function ($request) use ($destination) {
            return $destination($request);
        }
    );

    return $pipeline($this->request);
}

Скажем, у нас есть 3 промежуточных ПО:

[
    One::class,
    Two::class,
    Three::class,
];

Переменная $pipeline выше будет в основном:

function ($request) {

    return app(One::class)->handle($request, function ($request) {

        return app(Two::class)->handle($request, function ($request) {

            return app(Three::class)->handle($request, function ($request) {

                return $destination($request);

            });
        };);
    };);
};

Надеюсь это поможет!

person Rwd    schedule 13.02.2017
comment
Не могли бы вы уточнить немного больше, пожалуйста? Я думаю, что вся магия происходит в Illuminate/Pipeline/Pipeline.php. Простой пример будет отличным. - person Mayank Kumar; 13.02.2017
comment
@MayankKumar Я добавил еще немного. - person Rwd; 13.02.2017
comment
Если я хочу реализовать это без использования конвейера, как мне подойти? - person Mayank Kumar; 13.02.2017
comment
@MayankKumar Я не понимаю твоего вопроса. Вы имеете в виду проект без laravel? - person Rwd; 13.02.2017
comment
@SteveChamaillard Рад, что смог помочь! :) - person Rwd; 13.02.2017
comment
@RossWilson Ваше объяснение великолепно в контексте laravel. Я нашел эту функцию довольно мощной и хотел бы обобщить ее. Как я могу реализовать это в простейшей форме (без конвейера и laravel)? - person Mayank Kumar; 13.02.2017
comment
@MayankKumar Это выходит за рамки этого вопроса, и это будет полностью зависеть от того, как вы обрабатываете запросы и ответы на свое приложение, как вы будете назначать промежуточное программное обеспечение для разных маршрутов/запросов и т. д. При этом, на очень базовом уровне, вы просто создадите массив классов/функций, которые вы хотите вызвать, убедитесь, что каждый из них получает замыкание, и в основном используйте упрощенный пример выше. - person Rwd; 13.02.2017
comment
@RossWilson Большое спасибо. Отличное объяснение!! - person Mayank Kumar; 14.02.2017

Далее идет Closure, переменная анонимной функции. В вашем коде у вас есть return $next($request);. $next — это замыкание, основанное на вашем втором параметре метода. Это означает, что возвращаемое значение вашего метода — это то, что возвращает анонимная функция.

Например:

// this is the Closure.
$next = function ($parameter) {
    return $parameter . ' This message is modified by $next';
};

public function handle($message, Closure $next)
{
    return $next($message);
}

// test the output
$message = 'Hello World!';
echo handle($message, $next); // output will be 'Hello World! This message is modified by $next'
person Rendy Eko Prastiyo    schedule 13.02.2017