15

In Slim is it possible to get the current route within middleware?

class Auth extends \Slim\Middleware{
  public function call(){ 
    $currentRoute = $this->app->getRoute(); // Something like this?
  }
}

I know you can call $app->router()->getCurrentRoute() after the slim.before.dispatch hook is called, but when you call this from middleware it returns a non-object. Any help would be greatly appreciated.

3 Answers3

19

Yes and no. If you look at the source code for Slim, you will see that registered Middlewares are called in LIFO order when the Slim::run method is called, and then Slim runs it's own "call" method where the processing of the request begins. It is in this method that Slim parses and processes the route. In which case, you cannot access $app->router()->getCurrentRoute() in the Middleware::call method because it won't have been parsed and defined yet.

The only way to do this is to register a listener on slim.before.dispatch inside your Middleware, and implement whatever you want to do in that method.

From the name of your class I assume you are trying to create a basic authentication module? I've done something similar to this before, and it went something like this:

class AuthMiddleware extends \Slim\Middleware
{
    public function call()
    {
        $this->app->hook('slim.before.dispatch', array($this, 'onBeforeDispatch'));

        $this->next->call();
    }

    public function onBeforeDispatch()
    {
        $route = $this->app->router()->getCurrentRoute();

        //Here I check if the route is "protected" some how, and if it is, check the
        //user has permission, if not, throw either 404 or redirect.

        if (is_route_protected() && !user_has_permission())
        {
            $this->app->redirect('/login?return=' . urlencode(filter_input(INPUT_SERVER, 'REQUEST_URI')));
        }
    }
}

In this example, the onBeforeDispatch method will be run before of the route handlers are invoked. If you look at the source code, you can see the events are fired inside a try/catch block that is listening for the exceptions thrown by $app->redirect() and $app->pass(), etc. This means we can implement our check/redirect logic here just as if this was a route handler function.

Above is_route_protected and user_has_permission are just pseudo-code to illustrate how my auth middleware worked. I structured the class so that you could specify a list of routes or regex for routes in the Middleware constructor that were protected, as well as passing a service object that implemented the user permission checking, etc. Hope this helps.

Ram
  • 143,282
  • 16
  • 168
  • 197
Steve Whitfield
  • 1,981
  • 3
  • 21
  • 30
  • Nice one. Thanks for the tidy solution and detailed explanation. Is registering the hook in the middleware's call() function a good spot for it? It seems like it because it keeps it tidy, just wondering if that might cause the hook to be registered/called more than once? – plong0 Feb 18 '15 at 05:45
  • 3
    I think it's safe to assume the call method will only be invoked once per request. In reality, it is down to the implementation of the previous middleware in the chain, since the design uses a singly linked list where each middleware calls the next. If a middleware calls the next in the chains call method twice though I'd suggest its Doing It Wrong™ and you should probably stop using it :). – Steve Whitfield Feb 18 '15 at 13:59
3

There is an alternative method of doing this, as I've been in the same situation. What I wanted to avoid was matching anything by route and wanted to use route names instead, so you could try the following:

public function call() {

    $routeIWantToCheckAgainst = $this->slimApp->router()->urlFor('my.route.name');
    $requestRoute = $this->slimApp->request()->getPathInfo();
    if ($routeIWantToCheckAgainst !== $requestRoute) {
        // Do stuff you need to in here
    }

    $this->next->call();
}

You could even have an array of routes you DON'T want the middleware to run on and then just check if it's in_array() etc and if not, do what you need to.

Crags
  • 41
  • 4
2

You should use app->request()->getPathInfo() instead of app->getRoute().

class Auth extends \Slim\Middleware{
    public function call(){ 
        $currentRoute = $this->app->request()->getPathInfo();
    }
}
Deemoe
  • 931
  • 10
  • 12