slimphp / Slim

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs.
http://slimframework.com
MIT License
11.98k stars 1.95k forks source link

PSR-7 Middleware being added out of order on individual routes? #1771

Closed G4MR closed 8 years ago

G4MR commented 8 years ago

So I've been testing some code and I've noticed my middleware is being called out of order for example:

$app->any('/page', $controllers->make('class@method'))
    ->add($injector->make(SomeMiddleware::class))
    ->add($injector->make(Authenticated::class))
;

When I load the page it calls Authenticated::class before SomeMiddleware. Is this a bug or intended because that doesn't make sense. I wanted to write it like so originally:

$app->any('/page', $controllers->make('class@method'))
    ->add($injector->make(Authenticated::class))
    ->add($injector->make(SomeMiddleware::class))
;

Kind of odd :/

mathmarques commented 8 years ago

See: http://www.slimframework.com/docs/concepts/middleware.html

Slim adds middleware as concentric layers surrounding your core application. Each new middleware layer surrounds any existing middleware layers. The concentric structure expands outwardly as additional middleware layers are added.

LIFE: Last In First Executed

G4MR commented 8 years ago

Thanks for the response @mathmarques, clears that up. Though I wish there was a way to reverse it from inner to outer.

schnittstabil commented 8 years ago

Though I wish there was a way to reverse it from inner to outer.

@G4MR Why? Maybe I'm too used to the onion style, but it makes perfect sense to me:

$app->group('/product/{id:[0-9]+}', function () {

    $this->get('', function ($request, $response, $args) {
        // no auth needed
    });

    $this->delete('', function ($request, $response, $args) {
        // auth needed
    })->add($injector->make(Authenticated::class));

})->add($injector->make(SomeMiddleware::class));
G4MR commented 8 years ago

@schnittstabil options are nice

betweenbrain commented 8 years ago

LIFE: Last In First Executed

I'd like to suggest adding that to the docs. Thinking in terms of concentric layers, I would expect to be able to do the following:

<?php

require 'vendor/autoload.php';

$app = new \Slim\App();

$app->add(function ($request, $response, $next) {
    $response->getBody()->write(' Foo ');

        return $next($request, $response);
});

$app->get('/', function ($req, $res, $args) {
    echo ' Bar ';
});

$app->add(function ($request, $response, $next) {
    $response->getBody()->write(' Baz ');

    return $next($request, $response);
});

$app->run();

and get Foo Bar Baz as a result, but instead get Baz Foo Bar. To get my desired result, and effectively ensure that I can guarantee that a specific middleware is executed last, I need to do:

<?php

require 'vendor/autoload.php';

$app = new \Slim\App();

$app->add(function ($request, $response, $next) {
    $response = $next($request, $response);
    $response->getBody()->write(' Baz ');

    return $response;
});

$app->get('/', function ($req, $res, $args) {
    echo ' Bar ';
});

$app->add(function ($request, $response, $next) {
    $response->getBody()->write(' Foo ');

    return $next($request, $response);
});

$app->run();
schnittstabil commented 8 years ago

@betweenbrain Slim also supports:

$app->add(function ($request, $response, $next) {
    return $next($request, $response)->write(' Baz ');
});

$app->get('/', function ($req, $res, $args) {
    return $res->write(' Bar ');
});
akrabat commented 8 years ago

Don't ever echo. Return the $response with the additional text that you want.

mathmarques commented 8 years ago

First things first, $app->get | post | put | map | ... will always be the first layer(so it will always be the last executed). Slim have App level Middlewares and Route level Middlewares, and app level middlewares are always executed first than route level middleware.

If you take a look here: https://github.com/slimphp/Slim/issues/1622 (now slim execute order is what I was expecting) I think you could understand better how this works.

betweenbrain commented 8 years ago

@schnittstabil thanks! That's a great tip.

betweenbrain commented 8 years ago

@akrabat the example code I cited is based on the example at http://www.slimframework.com/docs/concepts/middleware.html#application-middleware. I'll create a PR against Slim-Website to correct it.

I'll also include the addition of LIFE: Last In First Executed as I think that's an import statement to make as it very eloquently sums up the middleware execution order.