The Nife-Based Framework

Nife

is a small PHP library that provides [very lightweight] abstractions for:

What this gives us is the ability to write web request handlers in a very functional way, minimizing the amount of procedural (global state-manipulating) code.

The Nife-based framework provides a pattern to follow in order to keep your code concise and flexible. A description of said pattern follows.

A Bit of Procedural Code

At the top level of the project there is some procedural 'bootstrapping' code. This includes www/bootstrap.php [example], which kicks off processing of incoming requests (either from Apache or the PHP's built-in webserver; you might want to write alternate bootstrap scripts for different servers), which includes init-www-error-handling.php and init-environment.php. These set up any global state needed by your program (error handlers, autoloaders, fixing bad PHP settings) and create a 'registry' object. Following their inclusion, bootstrap.php asks the registry for a 'dispatcher'. The dispatcher is asked to handle the request, returning a response, which is then output using Nife_Util::outputResponse.

To put in list form, the basic process is:

  1. Set up environment
  2. Handle request
  3. Output response

www/bootstrap.php is a reasonable place to put minor customizations to this process (such as logging errored requests).

The Registry

gets passed all over the application (usually as a constructor argument). Its responsibilities are:

This is conventionally done with a getConfig( $config_key ) method and __get method which must be clever enough to find (and instantiate, and probably cache instances of) component classes based on the name of the requested property. If a project's namespace is "Fred_CoolApp", its registry class will be called "Fred_CoolApp_Registry", and "$registry->frobber" would be reasonably expected to return some instance of Fred_CoolApp_Frobber. (I'm using underscore-delimited namespaces in these examples because I think they're easier to deal with; your project can just as well use backslashes and do the full PHP 5.3 namespace thing).

Note that, as with all components, it's possible to have multiple registry objects. If you've never needed an alternate 'global state' this probably doesn't matter to you. If you have, then you're probably well-aware of how difficult other frameworks make this.

The Dispatcher

decodes request parameters, decides what action to take, does it, and returns a Nife_HTTP_Response. This is the point where all the actual work gets done, and may be separated further:

  1. Based on path and request parameters, build an object that represents the action being taken.
  2. Validate any given authentication to determine the identity of the user (immediately returning a 401 error if credentials are invalid).
  3. Determine if that user is allowed to do the action (returning a 403 if not, or 401 if no credentials were given).
  4. Determine if the action actually needs to be performed (because it has important side-effects or If-None-Match, etc).
  5. Invoke the action, generating a response.
  6. Return that response.

Exactly how that is done is completely up to the application, but a conventional approach is to simply write a long if-else chain for mapping routes to actions.

Actions

are an abstraction on the idea of 'something to be done'. They can be thought of as zero-argument functions that may have important side-effects. If you're a Haskeller you might think of IO monads and what I'm calling 'actions' as pretty much the same concept.

Within a Nife-based project, you might define a PageAction class for each different page / form submission handler ("PageAction" being the conventional superclass or interface name for actions that return HTTP responses). This naturally breaks up your application into nicely-sized, independently-maintainable units. You might have action classes like Fred_CoolApp_PageAction_ShowHomePage (which would be instantiated in response to GET /) and Fred_CoolApp_PageAction_SubmitCoolForm (presumably instantiated in reponse to POST /cool-form or something similar). You can, of course, namespace your action classes further if you have a lot of them and there is an obvious organizational scheme. In some cases you may want to build actions programatically, as opposed to defining a new class for each one. The logic for when and how to do so would go in your Dispatcher class alongside that for the class-based actions.

Having a class for each action also has the advantage that you can define additional methods on them. You probably want to leave __invoke() to mean 'unconditionally try to do the action, either returning a Nife_HTTP_Response or throwing some exception', but maybe you want to determine whether the action is allowed separately from doing the action. In that case you could define an isAllowed($user) method on them, and have your dispatcher automatically return an error when that returns false instead of invoking the action. In other cases it may make more sense to have a separate permission-checker object determine whether actions are allowed based on other metadata. Either way, action objects give you the flexibility to de-couple the code that reasons about actions from the code that implements their invocation.