CodeIgniter hooks run code the core never calls

CodeIgniter exposes seven points in its boot sequence where you can attach a class of your own, and almost nobody uses them, because the usual answer to “run this on every request” is a MY_Controller that every controller then has to remember to extend. A hook needs no cooperation from the controllers at all.

// application/config/config.php — off by default, so the hook silently does nothing
$config['enable_hooks'] = true;

// application/config/hooks.php
$hook['post_controller_constructor'] = array(
    'class'    => 'Maintenance',
    'function' => 'gate',
    'filename' => 'Maintenance.php',
    'filepath' => 'hooks',
    'params'   => array('allow' => array('admin', 'support')),
);

// application/hooks/Maintenance.php
class Maintenance
{
    public function gate($params)
    {
        $CI =& get_instance();

        if (!$CI->config->item('maintenance')) {
            return;
        }

        if (in_array($CI->session->userdata('role'), $params['allow'])) {
            return;
        }

        $CI->output->set_status_header(503);
        $CI->load->view('maintenance');
        exit;
    }
}

The choice of hook point is the whole decision. post_controller_constructor runs after the controller has been instantiated and before its method is called, which is the first moment get_instance() returns anything — in pre_controller it is null, and discovering that is what sends most people back to a base controller. pre_system runs before the config class exists, so nothing there may read a setting. What this costs is visibility: nothing in the controller says that a hook ran before it, so the technique suits genuinely cross-cutting concerns — maintenance mode, a request id in the log, a profiling timer — and is a poor fit for anything a reader would reasonably expect to find in the controller itself. It also survives a framework upgrade, which is more than can be said for editing CodeIgniter.php.