Dispatching Requests | Error Handling

So, what should be included inside every file on your site? The usual list is quite long:

  • Loading configuration data from files in the “config/” directory.
  • Connecting to the database.
  • Setting up various PHP configuration elements (reacting to exceptions, autoloading…)
  • Including utility files that might come in handy later on, such as request builders.

In fact, I’m going to split these into two groups. On the one hand, there are the bits of behavior that are to be requested by the programmer (for instance, he will ask for the database explicitly whenever he performs a query). All of these, without exception, can be handled by autoloading instead. This lightens the load by reducing the number of things to be initialized when the script starts without reducing expressiveness.

Then, on the other hand, there’s all the configuration that changes how PHP reacts as it runs without being explicitly requested by the programmer. For instance:

  • What happens when an exception is thrown but not caught?
  • What happens when an assertion failure happens?
  • What happens when a generic PHP error happens?
  • How are undefined classes auto-loaded?

For instance:

<?php // utils/pervasive.php

  // Autoloading configuration
  function __autoload($classname)
  {
      $classname = strtolower($classname);

      $folders   = array(
        'utils'  => 'utils',
        'model'  => 'models',
        'view'   => 'views',
        'obj'    => 'objects', 
        'config' => 'config'
      );

      $pattern = implode('|', array_keys($folders));

      if (preg_match('/^([a-z0-9_]*)('.$pattern.')$/', $match))
      {
          require_once($folders[$match[2]].DIRECTORY_SEPARATOR.$match[1].'.php');
      }
  }

  // Responding to unhandled exceptions
  function on_unhandled_exception($exception)
  {
      ErrorUtils::UncaughtException($exception);
  }

  set_exception_handler('on_unhandled_exception');

  // Responding to assertion failures
  function on_assertion_failure()
  {
      ErrorUtils::AssertFailure();
  }

  assert_options(ASSERT_ACTIVE, true);
  assert_options(ASSERT_BAIL, true);
  assert_options(ASSERT_WARNING, false);
  assert_options(ASSERT_CALLBACK, 'on_assertion_failure');  

  // Responding to errors
  function on_error($no, $str, $file, $line, $context)
  {
      return ErrorUtils::Error($no, $str, $file, $line, $context);
  }

  set_error_handler(on_error);

  // Start output buffering
  ob_start();

The __autoload function is called whenever the script attempts to use a class that does not exist. This happens in three distinct circumstances:

  1. When creating an instance of the class (new Foobar).
  2. When accessing a static member (Foobar::Frobnicate()).
  3. When deserializing an instance of the class.

The autoload function is called with the name of the class, and is expected to read a file that somehow manages to define that class. Once this has happened, PHP checks whether the class has indeed been defined. If it has, execution resumes as expected (as if the class had always existed). Otherwise, execution is interrupted with an error.

The benefit of using autoloading instead of normal file-inclusion is twofold: first, you don’t have to remember to include everything (or even where some element has to be included from), and second, you don’t have to include things ahead of time at all.

For instance, in the code above, I do not include any code for handling the various errors. Instead, every error handling function forwards its arguments to a static member function of the ErrorUtils class. This means that, until they encounter an error (which, hopefully, should never happen), the scripts will never have to parse any code related to error handling. When an error does happen, the ErrorUtils class will be loaded (in this case, from “utils/error.php“) and will be allowed to include any amount of code it wishes to perform its task.

exclamationAs mentioned on the PHP website, the typical use of the autoload function is a security risk, because it will load an arbitrary file from the filesystem. It is therefore necessary to ensure that the class name is sufficiently sanitized to avoid any dangerous situations where user input would find its way into a class name. Of course, listing all the possible errors (such as class names that contain “../”) is difficult and hardly efficient, so I choose a different course: decide on a safe subset of all class names and check that the provided name is in that subset.

So, for this article, a class name will be any alphanumeric word (consists of letters, digits and underscores) followed by one of utils, model, view, obj or config. The autoload function uses preg_match to verify that the format is respected and extract the alphanumeric word (which will be the class name) and the suffix (which will determine the folder to look in). A few examples:

  • ErrorUtils is loaded from “utils/error.php
  • UserModel is loaded from “models/user.php
  • TabbedView is loaded from “views/tabbed.php
  • DatabaseConfig is loaded from “config/database.php
  • UserObj is loaded from “objects/user.php
  • GoogleMapsModule is not loaded.

This is an acceptable file distribution for small projects. As projects get increasingly large, it might become necessary to use namespace-like constructs that allow storing classes in sub-folders. A common approach is to allow underscores within class names, and map every underscore to a path separator (so that GuideMichelin_Cook_Model would map to “models/guidemichelin/cook.php“). This is the default for the Zend Framework, for instance.

Dispatching Requests | Error Handling

0 Responses to “3. Pervasive code”


  1. No Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



1208 feed subscribers
(readers who polled a feed this week)