What is the difference between URL Router and Dispatcher?
Asked Answered
F

2

36

I want to know the difference between a URL Router and a Dispatcher. What are they and what each of them do?

Fuss answered 28/7, 2012 at 11:27 Comment(3)
Which framework are you referring to?Hewes
Actually, I'm asking the question in general, ie, is not specifically a framework, but the functionality of the URL Router and Dispatcher only.Fuss
As these are not trademarked names, you need to provide more clarificationFabrizio
I
97

How frameworks and libraries interpret the responsibilities of the Router and Dispatcher are going to be different. What I detail below is how I interpret the responsibilities of these two services. It is not to say that this is the only way to interpret it or that other interpretations are wrong.

The Concepts

Routing

This is kinda like asking for directions at a gas station or convenience store. You're going through town and you need to figure out how to get to the nearest hotel. You go in and ask the attendant and they point you off in the correct direction, or at least you hope the directions are correct. Routing is exactly this. A request comes in for a resource, the Router provides the directions necessary for the request to reach the correct resource.

In most major frameworks you're going to be routing a specific request URL to an object and method that will be invoked to complete the request. Often times you'll see the Router also parsing out dynamic arguments from the URL. For example, if you accessed users via /users/1234 where 1234 is the user ID the Router would parse out the ID portion and provide this as part of the directions to the resource.

Dispatching

Dispatching uses the information from the Routing step to actually generate the resource. If the Routing step is asking for directions then Dispatching is the actual process of following those directions. Dispatching knows exactly what to create and the steps needed to generate the resource, but only after getting the directions from the Router.

The Implementations

These example implementations are intentionally very simple and naive. You would not want to use this in any kind of production environment without drastic improvements.

In this example instead of routing to an object and method we're just gonna route to a callable function. This also demonstrates that you don't need to route to an object; as long as the dispatcher can properly get the correct resource you can route to whatever data you want.

First we need something to route against. Let's create a simple Request object that we can match against.

<?php

class Request {

    private $method;
    private $path;

    function __construct($method, $path) {
        $this->method = $method;
        $this->path = $path;
    }

    function getMethod() {
        return $this->method;
    }

    function getPath() {
        return $this->path;
    }

}

Now that we can match against something let's take a look at a simple Router implementation.

<?php

class Router {

    private $routes = [
        'get' => [],
        'post' => []
    ];

    function get($pattern, callable $handler) {
        $this->routes['get'][$pattern] = $handler;
        return $this;
    }

    function post($pattern, callable $handler) {
        $this->routes['post'][$pattern] = $handler;
        return $this;
    }

    function match(Request $request) {
        $method = strtolower($request->getMethod());
        if (!isset($this->routes[$method])) {
            return false;
        }

        $path = $request->getPath();
        foreach ($this->routes[$method] as $pattern => $handler) {
            if ($pattern === $path) {
                return $handler;
            }
        }

        return false;
    }

}

And now we need some way to invoke a configured $handler for a given Request.

<?php

class Dispatcher {

    private $router;

    function __construct(Router $router) {
        $this->router = $router;
    }

    function handle(Request $request) {
        $handler = $this->router->match($request);
        if (!$handler) {
            echo "Could not find your resource!\n";
            return;
        }

        $handler();
    }

}

Now, let's bring it all together and show how to use these simple implementations.

<?php

$router = new Router();
$router->get('foo', function() { echo "GET foo\n"; });
$router->post('bar', function() { echo "POST bar\n"; });

$dispatcher = new Dispatcher($router);

$dispatcher->handle(new Request('GET', 'foo'));
$dispatcher->handle(new Request('POST', 'bar'));
$dispatcher->handle(new Request('GET', 'qux'));

You can see an example of this implementation in action by checking out http://3v4l.org/gbsoJ.

The Wrap Up

This example implementation is supposed to communicate the concept of routing and dispatching. Really there's a lot more to performing these actions than my example. Often the Router will use regex to match against a Request and may look at other Request attributes when matching. Additionally you'll see some libraries utilizing a resolver that interacts with the Router so that you can pass more than just callable functions. Basically, the Resolver would ensure that the $handler matched against can be turned into an invokable function.

Also, there's plenty of examples and implementations for this that you should look at using instead. For my personal projects I like FastRoute for its ease of use and performance. But, nearly all major frameworks have their own implementations. You should check those out too.

Intercalary answered 29/7, 2012 at 17:50 Comment(3)
I like this example. You could replace the foreach loop at the end of class Router with if (isset($this->routes[$method][$path])) {return $this->routes[$method][$path]}. Thank you very much.Racing
Very interesting example, but what if I want to register a route more complex than a simple anonymous function?Cannonball
@Cannonball you could make your Dispatcher more robust and capable of executing more than simply anonymous functions. Imagine a CallableResolver interface that the Dispatcher could accept as a constructor dependency. The CallableResolver has a resolve($routeData) : ?callable method on it that the Dispatcher passes the resolved route data to; it will always return a callable or null. A CallableResolverChain could loop through a series of CallableResolver until one matches.Intercalary
L
0

Route is a reference to a resource whereas Dispatching is an action of fetching the said resource.

Lavabo answered 23/5, 2021 at 12:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.