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.