If you need a simple application maintenance mode, fear not! CakePHP has you covered!

We’ll need to create a custom Dispatch Filter:

<?php
// In Lib/Routing/Filter/MaintenanceMode.php
App::uses('DispatcherFilter', 'Routing');
class MaintenanceMode extends DispatcherFilter {
  public function beforeDispatch(CakeEvent $event) {
    if (!Configure::read('MaintenanceMode.enabled')) {
      return;
    }
    $event->data['response']->statusCode(503);
    $event->data['response']->body('503 Service Unavailable');
    $event->stopPropagation();
    return $event->data['response'];
  }
}
?>

Now we can attach it to our existing dispatch filters:

<?php
// in our bootstrap.php
App::uses('MaintenanceMode', 'Lib/Routing/Filter');
Configure::write('Dispatcher.filters', array(
  'AssetDispatcher',
  'CacheDispatcher',
  'MaintenanceMode', // our new filter here!
));
?>

Tada! Now turning on maintenance mode is as simple as Configure::write('MaintenanceMode.enabled', 1)!

Using custom views

The above is lame in that it only works for a simple 503 error page. What if you want to use a custom status code, or maybe a custom view file?

To do this, lets change our filter a bit:

<?php
// In Lib/Routing/Filters/MaintenanceMode.php
App::uses('DispatcherFilter', 'Routing');
class MaintenanceMode extends DispatcherFilter {
  public function beforeDispatch(CakeEvent $event) {
    if (!Configure::read('MaintenanceMode.enabled')) {
      return;
    }
    $statusCode = Configure::read('MaintenanceMode.code');
    $statusMessage = Configure::read('MaintenanceMode.message');
    if (!$statusCode) {
      $statusCode = 503;
      $statusMessage = '503 Service Unavailable';
    }
    if (!$statusMessage) {
      $statusMessage = $statusCode . ' Currently undergoing maintenance';
    }
    $event->data['response']->statusCode($statusCode);
    $event->data['response']->body($statusMessage);
    $event->stopPropagation();
    return $event->data['response'];
  }
}
?>

At this point, we can configure a custom status code and a custom message. Lets add the ability to use a custom view:

<?php
  protected function _getView() {
    $helpers = Configure::read('MaintenanceMode.helpers');
    if (empty($helpers) || !is_array($helpers)) {
      $helpers = array('Html');
    }
    $View = new View(null);
    $View->viewVars = Configure::read('MaintenanceMode');
    $View->helpers = $helpers;
    $View->loadHelpers();
    $View->hasRendered = false;
    $View->viewPath = 'MaintenanceMode';
    return $View;
  }
?>

We’ll need to modify our beforeDispatch, where we set the response body:

<?php
    $template = Configure::read('MaintenanceMode.template');
    if ($template) {
      $View = $this->_getView();
      $body = $View->render($template, Configure::read('MaintenanceMode.layout'));
      $event->data['response']->body($body);
    } else {
      $event->data['response']->body($statusMessage);
    }
?>

And add the App::uses('View', 'View'); call to the top of your MaintenanceMode class. Now to use a custom view, we’d create a file in app/View/MaintenanceMode/index.ctp with our content, a app/View/MaintenanceMode/maintenance.ctp layout, and then configure our filter as follows:

<?php
Configure::write('MaintenanceMode', array(
  'enabled' => true,
  'helpers' => 'Html',
  'layout' => 'maintenance',
  'template' => 'index'
));
?>

You can extend this as far as you need, and here are a couple of ideas:

  • Add ajax request support
  • Enable custom view classes
  • Allow maintenance mode to be enabled via the existence of a file, or an environment variable
  • Have this affect background processes
  • Add unit tests!

If you need some downtime this Christmas, CakePHP has your back!