PHP - - By Alejandro Gervasio

An Introduction to the Front Controller Pattern, Part 1

An Introduction to the Front Controller Pattern

It’s interesting to see how people have passed judgement on several software programming concepts in an anthropomorphic fashion. Fat models are all sweet and good, bloated controllers are evil, and singletons are fraught with untameable pitfalls. But the critics don’t stop there; some are now grumbling about newer concepts that have recently made inroads in day-to-day PHP development, saying Front Controllers are a redundant “reinvention of the wheel” which should be discarded ipso facto. Period.

Pitching blame against singletons from time to time, and even being a bit cranky about fat controllers is, to some extent, a fairly predictable and healthy reaction. But what’s wrong with having a nifty front controller up and running?

There’s a pretty substantial argument against front controllers. The primary role of a front controller in web-based applications is to encapsulate the typical request/route/dispatch/response cycles inside the boundaries of an easily-consumable API, which is exactly what the web server does. Indeed the process seems redundant at first blush, but when coupled to an MVC implementation, we’re actually trying to build up a controller that in turn controls other controllers.

Despite the apparent duplication of roles, the implementation of a front controller emerges as a response to the growing complexities of modern web application development. Tunneling all requests through a single entry point is certainly an efficient way to implement a command-based mechanism, which not only allows you to route and dispatch commands to appropriate handlers, but also exposes a flexible structure that can be massaged and scaled without much burden.

Frankly speaking, front controllers are easily-tameable creatures. In the simplest scenario, a naive combination of URL rewriting along with a few switch statements is all we need to route and dispatch requests, though in production it might be necessary to appeal to more complex and granular implementations, especially if we want to distill routing and dispatching processes through finer-grained objects armed with well-segregated responsibilities.

In this two-part article I’ll be exploring in depth a couple of straightforward approaches that you might find appealing, especially if you’re trying to implement an expandable front controller from scratch without sweating excessively during the process or having to cope with the burdens of a bloated framework.

Routing and Dispatching in a Straightforward Way

In reality, there are so many nifty options that can be used for building a functional front controller, but I’ll start by letting my pragmatic side show (yes, I have one). The first front controller implementation that I’ll go through will be charged with routing/dispatching URIs that conform to the following format:

basepath/controllername/actionname/param1/param2/.../paramN

If you’ve ever laid your hands on a framework that uses the notion of parameterized action controllers, the above URI should familiar to you. It is a fairly ubiquitous pattern. Of course, the most challenging task here is designing a flexible mechanism capable of parsing the URIs in question without much fuss. This can be achieved in all sort of creative ways, either by plain procedural code or appealing to object-oriented code. I’ll be encapsulating the nuts and bolts of the routing/dispatching logic beneath the shell of a single class:

<?php
namespace LibraryController;

interface FrontControllerInterface
{
    public function setController($controller);
    public function setAction($action);
    public function setParams(array $params);
    public function run();
}
<?php
namespace LibraryController;

class FrontController implements FrontControllerInterface
{
    const DEFAULT_CONTROLLER = "IndexController";
    const DEFAULT_ACTION     = "index";
    
    protected $controller    = self::DEFAULT_CONTROLLER;
    protected $action        = self::DEFAULT_ACTION;
    protected $params        = array();
    protected $basePath      = "mybasepath/";
    
    public function __construct(array $options = array()) {
        if (empty($options)) {
           $this->parseUri();
        }
        else {
            if (isset($options["controller"])) {
                $this->setController($options["controller"]);
            }
            if (isset($options["action"])) {
                $this->setAction($options["action"]);     
            }
            if (isset($options["params"])) {
                $this->setParams($options["params"]);
            }
        }
    }
    
    protected function parseUri() {
        $path = trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), "/");
        $path = preg_replace('/[^a-zA-Z0-9]//', "", $path);
        if (strpos($path, $this->basePath) === 0) {
            $path = substr($path, strlen($this->basePath));
        }
        @list($controller, $action, $params) = explode("/", $path, 3);
        if (isset($controller)) {
            $this->setController($controller);
        }
        if (isset($action)) {
            $this->setAction($action);
        }
        if (isset($params)) {
            $this->setParams(explode("/", $params));
        }
    }
    
    public function setController($controller) {
        $controller = ucfirst(strtolower($controller)) . "Controller";
        if (!class_exists($controller)) {
            throw new InvalidArgumentException(
                "The action controller '$controller' has not been defined.");
        }
        $this->controller = $controller;
        return $this;
    }
    
    public function setAction($action) {
        $reflector = new ReflectionClass($this->controller);
        if (!$reflector->hasMethod($action)) {
            throw new InvalidArgumentException(
                "The controller action '$action' has been not defined.");
        }
        $this->action = $action;
        return $this;
    }
    
    public function setParams(array $params) {
        $this->params = $params;
        return $this;
    }
    
    public function run() {
        call_user_func_array(array(new $this->controller, $this->action), $this->params);
    }
}

The FrontController class’ duties boil down to parsing the request URI, or eventually assembling a brand new one from scratch through a few basic mutators. Once this task has been carried out, the run() method neatly dispatches the request to the appropriate action controller, along with the supplied arguments, if any.

Given its minimal API, consuming the class is a simple two-step process. First, drop into the web root a typical .htaccess file, like this one:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php

Second, set up the following code snippet as index.php:

<?php
use LibraryLoaderAutoloader,
    LibraryControllerFrontController;
    
require_once __DIR__ . "/Library/Loader/Autoloader.php";
$autoloader = new Autoloader;
$autoloader->register();

$frontController = new FrontController();
$frontController->run();

In this case, the controller simply parses the request URI and feeds it to the given action controller, which is its default behavior by the way. It’s feasible, though, to explicitly provide a URI by calling the corresponding setters, as follows:

<?php
$frontController = new FrontController(array(
    "controller" => "test", 
    "action"     => "show", 
    "params"     => array(1)
));

$frontController->run();

The front controller here is pretty malleable, easily configurable either for internally parsing requests or for routing/dispatching custom ones supplied directly from client code. Moreover, the previous example shows in a nutshell how to call the show() method of an hypothetical TestController class and pass around a single numeric argument to it. Of course, it’s possible to use different URIs at will and play around with the controller’s abilities. Therefore, if you’re bored and want to have some fun for free, just go ahead and do so.

Though I have to admit that my carefully-crafted FrontController class has pretty limited functionality, it makes a statement on its own. It demonstrates that building up a customizable front controller is in fact a straightforward process that can be tamed successfully without having to use obscure and tangled programming principles.

On the flip side, the bad news is that the class has way to many responsibilities to watch over. If you’re skeptical, just check out its run() method. Certainly its implementation is clean and compact, and can be even accommodated to intercept pre/post dispatch hooks. But it does multiple things at the same time and behaves pretty much as a catch-all point for routes and dispatches. It’s preferable to have a front controller dissected in more granular classes, whose responsibilities are narrowed to perform discrete tasks.

Needless to say that getting such a distilled front controller up and running as expected requires traveling down the road to a fairly prolific number of classes and interfaces. I’m reluctant to make this installment excessively lengthy, so I’ll be covering in depth the details of the whole implementation process in the next article. This way you can have some time to twist and bend my sample front controller and make it suit the taste of your delicate palate.

Closing Thoughts

Front controllers are ridiculously simple to implement from scratch, regardless if the approach makes use of procedural code or object-oriented code. And because of its easy-going nature, it’s fairly easy to scale up a naïve, primitive front controller and pitch over its shoulders the whole shebang required for handling RESTful resources behind the scenes.

Quite possibly, the most tangled aspect of writing a front controller is solving the implicit dilemma when it comes to deciding if the requests must be either statically or dynamically routed and dispatched to the appropriate handlers. There’s no formal principle that prescribes all the routing/dispatching logic should be encapsulated within the controller’s boundaries or broken down into standalone modules that can be reused independently.

Precisely, the latter is the form of implementation that I’ll be discussing over the course of the next article. So, stay tuned!

Image via Fotolia

Sponsors