Welcome Guest, Not a member yet? Register   Sign In
Custom 404 handling
#1

[eluser]Mark van der Walle[/eluser]
After almost 2 years of using CI and posting a bit on these forums I present my first contribution to the community. It is a simple custom 404 handler that can be used to handle any path that cannot be routed to a controller. It uses an overrided Router, base controller and the _remap function. Suggestions, critics are welcome Smile

Base controller:
Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_Controller extends Controller
{
    /**
     * Constructor
     */
    public function __construct()
    {
        parent::Controller();
    }

    /**
     * @param   string          $method the method CI would usually call
     */
    public function _remap($method)
    {
        global $URI;

        if (method_exists($this, $method)) {
            call_user_func_array(array(&$this, $method), array_slice($URI->rsegments, 2));
        } else {
            $this->_handle_404();
        }
    }

    /**
     * Handle 404 using a custom controller. Will call default show_404() when it cannot resolve to a valid method.
     */
    protected function _handle_404()
    {
        $errorconfig = $this->config->item('error');

        if (!$errorconfig) {
            show_404();
        }

        $path = APPPATH . 'controllers/' . $errorconfig['directory'] . '/' . $errorconfig['controller'] . EXT;
        if (!file_exists($path)) {
            show_404();
        }

        require_once $path;
        if (!class_exists($errorconfig['controller'])) {
            show_404();
        }

        $class = new $errorconfig['controller'];
        if (!method_exists($class, $errorconfig['method'])) {
            show_404();
        }

        call_user_func(array(&$class, $errorconfig['method']));
    }
}
?>

Router:
Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_Router extends CI_Router
{
    /**
     * Validates the supplied segments.  Attempts to determine the path to
     * the controller. This is an extension so we can support 404 handlers
     *
     * @access    private
     * @param    array
     * @return    array
     */    
    function _validate_request($segments)
    {
        // Does the requested controller exist in the root folder?
        if (file_exists(APPPATH.'controllers/'.$segments[0].EXT))
        {
            return $segments;
        }

        // Is the controller in a sub-folder?
        if (is_dir(APPPATH.'controllers/'.$segments[0]))
        {        
            // Set the directory and remove it from the segment array
            $this->set_directory($segments[0]);
            $segments = array_slice($segments, 1);
            
            if (count($segments) > 0)
            {
                // Does the requested controller exist in the sub-folder?
                if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
                {
                    return $this->_custom_404();    
                }
            }
            else
            {
                $this->set_class($this->default_controller);
                $this->set_method('index');
            
                // Does the default controller exist in the sub-folder?
                if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
                {
                    $this->directory = '';
                    return array();
                }
            
            }
                
            return $segments;
        }
    
        // Can't find the requested controller...
        return $this->_custom_404();    
    }

    function _custom_404()
    {
        $errorconfig = $this->config->item('error');

        if ($errorconfig) {

            $path = APPPATH . 'controllers/' . $errorconfig['directory'] . '/' . $errorconfig['controller'] . EXT;
            if (file_exists($path)) {
                $this->set_directory($errorconfig['directory']);
                $this->set_class($errorconfig['controller']);
                $this->set_method($errorconfig['method']);
            } else {
                show_404();
            }
        } else {
            show_404();
        }

        return array();
    }
}
?>

Errorhandler controller:
Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

class ErrorHandler extends MY_Controller
{
    function index()
    {
        // You could do basicly anything here. Pull content from databases
        // using the supplied path and/or read text files. or just show your
        // own fancy 404 page. For now we just:

        echo '404';
    }
}
?>

And finally some config:
Code:
$config['error']['directory'] = '';

$config['error']['controller'] = 'errorhandler';

$config['error']['method'] = 'index';
#2

[eluser]Unknown[/eluser]
I would like to ask where you put that base controller? I have tried to use your script but it doesn't seem to work. Beside if you extend the controller, shouldn't the constructor have to be the same name of the class "MY_Controller"?
#3

[eluser]Christian Haller[/eluser]
Hi,
I have the same question. To which folder belong which file and are any settings required?
It doesn't work here (1.6.3).
#4

[eluser]lmv4321[/eluser]
I believe MY_Controller and MY_Router should be placed in the system/application/libraries folder. Note that I haven't actually tried to use any of this code.
#5

[eluser]kRON[/eluser]
Fantastic contribution Mark! This just saved me from having to hack a way around my dynamic header and footer for error pages. I'm using the ErrorHandler class to handle all my exceptions now. Thanks again Smile
#6

[eluser]Randell Benavidez[/eluser]
It works! Awesome! Maybe you could document the parts of the code so others can find ways to make it simpler. Also, have you done any updates with your code?
#7

[eluser]TheFunker[/eluser]
Works a treat.

For those of you who want to know where to put things:

1. place config items in system/application/config/config.php (don't put them in a custom config file)

2. create MY_router.php, paste in code above, and place in system/application/libraries folder

3. create MY_controller.php, paste in code above, and place in system/application/libraries folder

4. create ErrorHandler.php, paste in code above, and place in system/application/controllers folder

Use ErrorHandler.php to generate your custom error page; use it like any other controller (i.e. load models to query DB, load views to display custom page layout/message/etc, call other methods, etc.

Bob's your Uncle. ;-)

http://www.daveheslop.com
#8

[eluser]Unknown[/eluser]
With the code as it is, the source of a resulting 404 is:

Code:
404

There are 3 spaces just before the "404" -- causing troubles with XML. Any idea where that space is being generated from?
#9

[eluser]JE[/eluser]
I am having problem in my session when I implemented your 404 library.

A PHP Error was encountered

Severity: Warning

Message: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at C:\xampp\htdocs\petclinicsupport\system\application\libraries\MY_Router.php:79)

Filename: controllers/welcome.php

Line Number: 2


I included a session_start() in my welcome.php controller. How can I fix this error. Thanks
#10

[eluser]markup2go[/eluser]
[quote author="JE" date="1252528654"]I am having problem in my session when I implemented your 404 library.

A PHP Error was encountered

Severity: Warning

Message: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent (output started at C:\xampp\htdocs\petclinicsupport\system\application\libraries\MY_Router.php:79)

Filename: controllers/welcome.php

Line Number: 2


I included a session_start() in my welcome.php controller. How can I fix this error. Thanks[/quote]

Don't end your PHP files with ?> or have empty lines at the end of your php files. See the PHP STYLES section of the user guide.




Theme © iAndrew 2016 - Forum software by © MyBB