I thought I might take a little step back here from my previous entry regarding my simplistic, not-all-that-original MVC using auto_prepend_file. I realized that I didn’t really explain why I occasionally get in that “there’s-got-to-be-a-simpler-way” frame of mind.

As I was building Camber, I made it a point to stick with an object-oriented design as closely as possible. The bootstrap script (i.e., index.php) simply sets up the basic environment, creates the main objects that are needed for the application, and starts things flowing. To re-cap, the whole life-cycle for a request in a Camber application is:

  1. Create the Application object
  2. Create the Controller object by passing in the Application
  3. Call $controller->getResource() which returns a Resource object
  4. Call $resource->run(), which returns the Response object
  5. Call $response->send() to send the response back


In general, I like the way the application itself is structured. Although any given Resource class (which is basically just a collection of request handlers, i.e. get(), post(), delete(), put()) might be pretty complex, it’s fairly simple to figure out where each feature of the application should reside. Adding functionality to your application is really just a matter of writing new Resource classes and updating the templates appropriately. The thing that bothers me though is the routing process, i.e., determining which Resource needs to be instantiated for any given request.

Camber’s routing logic is handled within the Controller::getResource() method, but I pretty much ignored building any kind of regular-expression-esque routing language. Instead of having a declarative way of specifying which Resource to create, my neglect requires the author to decide how to perform that instantiation. Maybe a bunch of big, ugly, nested if()s that examine the incoming URL, or a switch() statement that does the same. You could even write your own regular expressions to match the various parts of the URL and decide which Resource needs to be created and run. I know. Shame on me for not building that mechanism into the framework.

Anyway, the reason that this kind of in-framework routing bothers me is that it is implemented in PHP (either pre-built within the framework itself, or (even worse!) by the author of the application). Not only does that seem unnecessarily bulky, but it also requires some kind of loading mechanism that looks up the appropriate request handler. This can be accomplished in a relatively uncomplicated way by putting all of the request handlers in a special directory (e.g., controllers/), then just require()ing the appropriate class file depending on what your routing method returned and instantiating it.

Is that really necessary though? Imagine a simple web application where an HTTP request for:

http://example.com/profile/display?name=drew

will result in the following code being executed (use your imagination to fill in the gaps):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
    // ...routing code runs here...
    // determines the following from the request:
    //   $controller     = 'profile'
    //   $action         = 'display'
    //   $params['name'] = 'drew'
 
    require 'controllers/' . $controller . '.php';
    $controller .= 'Controller';
    $c = new $controller();
 
    // i.e., $c->display(array('name' => 'drew'));
    $c->$action($params);
 
    // ...finish up, send response...
?>

It’s not a whole bunch of code, but I don’t think it needs to exist at all. Basically, instead of mapping requests to request handler classes, loading them, instantiating them, and running them, I propose simply using mod_rewrite and the natural file system structure in the document root to run the scripts that are being requested. This way, instead of declaring and implementing routing rules in PHP, you’d just use the appropriate mod_rewrite rules. Instead of dynamically instantiating a request handler, you just let apache serve up the whichever PHP script happens to match the rewrite rule.

Obviously, this would mean that the request handler isn’t an object at all; it’s, for the most part, a regular old PHP script. Is that so bad though? There are definitely many conveniences that object-oriented request handlers can provide, such as using inheritance to specify things like authentication and authorization in the target request handlers. I believe, however, that it’s possible to abide by the same DRY (Don’t Repeat Yourself) principles for these specific requirements without relying on an unnecessarily complex object-oriented controller design.

The overarching idea here is to use the web server to handle as much of the grunt work of the application as possible before actually handing off control into PHP land.

Posted Tuesday, March 11th, 2008 at 4:09 pm
Filed Under Category: PHP
You can skip to the end and leave a response. Pinging is currently not allowed.

2

Responses to “Object-oriented controllers bug me”

Peter Goodman

The front controller (application class) way of structuring things really loses its values in a language like PHP. In something like Python or Java, it makes a lot of sense because those languages are end up being long-running processes and so for each request the same front controller is used. However, in PHP, each request is a process and so the front controller loses its value.

In my own framework experiments, I’ve come up with a nifty thing that adds usefulness to it by allowing one to break out of a controller/action and into another by specifying the route to go to. The only data shared is that which is already in PHP’s superglobals, _GET, _POST, etc.

Peter Goodman

As a quick clarification, when I say ‘break out’ I don’t mean with an HTTP redirect but with an exception.

Leave a Reply

You must be logged in to post a comment.