Location: PHPKode > projects > Maintainable PHP Framework > vendor/Mad/Controller/Dispatcher.php
<?php
/**
 * @category   Mad
 * @package    Mad_Controller
 * @copyright  (c) 2007-2009 Maintainable Software, LLC
 * @license    http://opensource.org/licenses/bsd-license.php BSD
 */

/**
 * Dispatch a request to the appropriate controller and execute the response
 *
 * @category   Mad
 * @package    Mad_Controller
 * @copyright  (c) 2007-2009 Maintainable Software, LLC
 * @license    http://opensource.org/licenses/bsd-license.php BSD
 */
class Mad_Controller_Dispatcher
{
    /** @var Mad_Controller_Dispatcher */
    private static $_instance;

    /** @var Mad_Controller_Request_Http */
    private $_request;

    /** @var Horde_Routes_Mapper */
    private $_mapper;

    /**
     * Singleton instance
     */
    public static function getInstance()
    {
        if (self::$_instance === null) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    /**
     * Class constructor.  Initialize the mapper and configure it with
     * our controller directory and scan callback.  Load the route
     * definitions into the mapper.
     */
    private function __construct()
    {
        $this->reload();
    }

    /** 
     * Reload the routes for dispatching
     */
    public function reload()
    {
        $options = array('directory' => MAD_ROOT . '/app/controllers',
                         'explicit'  => false
                        );

        $map = new Horde_Routes_Mapper($options);
        include MAD_ROOT . '/config/routes.php';

        $scanner = new Mad_Controller_Scanner($map);
        $map->controllerScan = $scanner->getCallback();

        $this->_mapper = $map;
    }

    /**
     * Get the route mapper for this dispatcher
     *
     * @return  Horde_Routes_Mapper
     */
    public function getRouteMapper()
    {
        return $this->_mapper;
    }

    /**
     * Get the route utilities for this dispatcher and its mapper
     *
     * @return  Horde_Routes_Utils
     */
    public function getRouteUtils()
    {
        return $this->_mapper->utils;
    }

    /**
     * Dispatch the request to the correct controller
     *
     * @param  null|Mad_Controller_Request_Http  $request
     * @param  null|Mad_Controller_Response_Http $response
     * @return void
     */
    public function dispatch($request=null, $response=null)
    {
        $t = new Horde_Support_Timer;
        $t->push();

        if ($response === null) {
            $response = new Mad_Controller_Response_Http();
        }

        if ($request === null) {
            $request = new Mad_Controller_Request_Http();
        }

        // request could not be parsed
        if ($request->isMalformed()) {
            $body = "Your request could not be understood by the server, "
                  . "probably due to malformed syntax.\n\n";

            $exc = $request->getException();
            if ($exc && MAD_ENV != 'production') {
                $body .= $exc->getMessage();
            }

            $response->setStatus(400);
            $response->setBody($body);

        // dispatch request to controller
        } else {
            $controller = $this->recognize($request);
            $response   = $controller->process($request, $response);
        }

        $time = $t->pop();
        $this->_logRequest($request, $time);

        $response->send();
    }

    /**
     * Check if request path matches any Routes to get the controller
     *
     * @return  Mad_Controller_Base
     * @throws  Mad_Controller_Exception
     */
    public function recognize($request)
    {
        // pass a subset of the request environment
        // horde_routes_mapper for route matching
        $environ = array('REQUEST_METHOD' => $request->getMethod());
        foreach (array('HTTP_HOST', 'SERVER_NAME', 'HTTPS') as $k) { 
            $environ[$k] = $request->getServer($k); 
        }
        $this->_mapper->environ = $environ;

        $path = $request->getPath();
        if (substr($path, 0, 1) != '/') { $path = "/$path"; }

        $matchdata = $this->_mapper->match($path);
        if ($matchdata) { $hash = $this->formatMatchdata($matchdata); }
        
        if (empty($hash) || !isset($hash[':controller'])) {
            $msg = 'No routes in config/routes.php match the path: "'.$request->getPath().'"';
            throw new Mad_Controller_Exception($msg);
            return false;
        }
        $request->setPathParams($hash);

        // try to load the class
        $controllerName = $hash[':controller'];
        if (!class_exists($controllerName, false)) {
            $path = MAD_ROOT.'/app/controllers/'.$controllerName.'.php';
            if (file_exists($path)) {
                require_once $path;
            } else {
                $msg = "The Controller \"$controllerName\" does not exist at ".$path;
                throw new Mad_Controller_Exception($msg);
            }
        }
        $controllerClassName = Mad_Support_Inflector::classify($controllerName);

        return new $controllerClassName();
    }

    /**
     * Take the $matchdata returned by a Horde_Routes_Mapper match and add
     * in :controller and :action that are used by the rest of the framework.
     * 
     * Format controller names: my_stuff => MyStuffController
     * Format action names:     action_name => actionName
     * 
     * @param   array   $matchdata
     * @return  mixed   false | array
     */
    public function formatMatchdata($matchdata)
    {
        $ret = array();
        foreach ($matchdata as $key=>$val) {
            if ($key == 'controller') {
                $ret['controller'] = $val;
                $ret[':controller'] = Mad_Support_Inflector::camelize($val).'Controller';
            } elseif ($key == 'action') {
                // Horde_Routes_Mapper->resource() and Python Routes are inconsistent
                // with Rails by using "delete" instead of "destroy".
                if ($val == 'delete') { $val = 'destroy'; }

                $ret['action']  = $val;
                $ret[':action'] = Mad_Support_Inflector::camelize($val, 'lower');
            } else {
                $ret[$key] = $val;
            }
        }
        return !empty($ret) && isset($ret['controller']) ? $ret : false;
    }

    /*##########################################################################
    # Logger
    ##########################################################################*/

    /**
     * Returns the logger object.
     *
     * @return  object
     */
    public static function logger()
    {
        return $GLOBALS['MAD_DEFAULT_LOGGER'];
    }

    /**
     * Log the http request
     *
     * @todo - get total query times
     *
     * @param   Mad_Controller_Request_Http $request
     * @param   int $totalTime
     */
    protected function _logRequest($request, $totalTime)
    {
        $queryTime  = 0; // total time to execute queries
        $queryCount = 0; // total queries performed
        $phpTime = $totalTime - $queryTime;

        // embed user info in log
        $uri    = $request->getUri();
        $method = $request->getMethod();

        $paramStr = 'PARAMS=' . $this->_formatLogParams($request->getParameters());

        $msg = sprintf("$method $uri %.4fs (DB=%.4fs [$queryCount] PHP=%.4fs) $paramStr",
                        $totalTime, $queryTime, $phpTime);
        $msg = wordwrap($msg, 80, "\n\t  ", 1);

        Mad_Controller_Dispatcher::logger()->info($msg);
    }

    /**
     * Formats the request parameters as a "key => value, key => value, ..." string
     * for the log file.
     *
     * @param array $params
     * @return string
     */
    protected function _formatLogParams($params)
    {
        $paramStr = '{';
        $count = 0;
        foreach ($params as $key => $value) {
            if ($key != 'controller'  && $key != 'action' &&
                $key != ':controller' && $key != ':action') {
                if ($count++ > 0) { $paramStr .= ', '; }

                $paramStr .= $key.' => ';

                if (is_array($value)) {
                    $paramStr .= $this->_formatLogParams($value);
                } elseif (is_object($value)) {
                    $paramStr .= get_class($value);
                } else {
                    $paramStr .= $value;
                }
            }
        }
        return $paramStr . '}';
    }

}
Return current item: Maintainable PHP Framework