dispatcher.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: dispatcher.php 8237 2009-07-21 01:31:50Z gwoo $ */
00003 /**
00004  * Dispatcher takes the URL information, parses it for paramters and
00005  * tells the involved controllers what to do.
00006  *
00007  * This is the heart of Cake's operation.
00008  *
00009  * PHP versions 4 and 5
00010  *
00011  * CakePHP(tm) : Rapid Development Framework (http://www.cakephp.org)
00012  * Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00013  *
00014  * Licensed under The MIT License
00015  * Redistributions of files must retain the above copyright notice.
00016  *
00017  * @filesource
00018  * @copyright     Copyright 2005-2008, Cake Software Foundation, Inc. (http://www.cakefoundation.org)
00019  * @link          http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00020  * @package       cake
00021  * @subpackage    cake.cake
00022  * @since         CakePHP(tm) v 0.2.9
00023  * @version       $Revision: 8237 $
00024  * @modifiedby    $LastChangedBy: gwoo $
00025  * @lastmodified  $Date: 2009-07-20 21:31:50 -0400 (Mon, 20 Jul 2009) $
00026  * @license       http://www.opensource.org/licenses/mit-license.php The MIT License
00027  */
00028 /**
00029  * List of helpers to include
00030  */
00031 App::import('Core', array('Router', 'Controller'));
00032 /**
00033  * Dispatcher translates URLs to controller-action-paramter triads.
00034  *
00035  * Dispatches the request, creating appropriate models and controllers.
00036  *
00037  * @package       cake
00038  * @subpackage    cake.cake
00039  */
00040 class Dispatcher extends Object {
00041 /**
00042  * Base URL
00043  *
00044  * @var string
00045  * @access public
00046  */
00047     var $base = false;
00048 /**
00049  * webroot path
00050  *
00051  * @var string
00052  * @access public
00053  */
00054     var $webroot = '/';
00055 /**
00056  * Current URL
00057  *
00058  * @var string
00059  * @access public
00060  */
00061     var $here = false;
00062 /**
00063  * Admin route (if on it)
00064  *
00065  * @var string
00066  * @access public
00067  */
00068     var $admin = false;
00069 /**
00070  * Plugin being served (if any)
00071  *
00072  * @var string
00073  * @access public
00074  */
00075     var $plugin = null;
00076 /**
00077  * the params for this request
00078  *
00079  * @var string
00080  * @access public
00081  */
00082     var $params = null;
00083 /**
00084  * Constructor.
00085  */
00086     function __construct($url = null, $base = false) {
00087         if ($base !== false) {
00088             Configure::write('App.base', $base);
00089         }
00090         if ($url !== null) {
00091             return $this->dispatch($url);
00092         }
00093     }
00094 /**
00095  * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the results (if autoRender is set).
00096  *
00097  * If no controller of given name can be found, invoke() shows error messages in
00098  * the form of Missing Controllers information. It does the same with Actions (methods of Controllers are called
00099  * Actions).
00100  *
00101  * @param string $url URL information to work on
00102  * @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params
00103  * @return boolean Success
00104  * @access public
00105  */
00106     function dispatch($url = null, $additionalParams = array()) {
00107         if ($this->base === false) {
00108             $this->base = $this->baseUrl();
00109         }
00110 
00111         if (is_array($url)) {
00112             $url = $this->__extractParams($url, $additionalParams);
00113         } else {
00114             if ($url) {
00115                 $_GET['url'] = $url;
00116             }
00117             $url = $this->getUrl();
00118             $this->params = array_merge($this->parseParams($url), $additionalParams);
00119         }
00120 
00121         $this->here = $this->base . '/' . $url;
00122 
00123         if ($this->cached($url)) {
00124             $this->_stop();
00125         }
00126 
00127         $controller =& $this->__getController();
00128 
00129         if (!is_object($controller)) {
00130             Router::setRequestInfo(array($this->params, array('base' => $this->base, 'webroot' => $this->webroot)));
00131             return $this->cakeError('missingController', array(array(
00132                 'className' => Inflector::camelize($this->params['controller']) . 'Controller',
00133                 'webroot' => $this->webroot,
00134                 'url' => $url,
00135                 'base' => $this->base
00136             )));
00137         }
00138 
00139         $privateAction = (bool)(strpos($this->params['action'], '_', 0) === 0);
00140         $prefixes = Router::prefixes();
00141 
00142         if (!empty($prefixes)) {
00143             if (isset($this->params['prefix'])) {
00144                 $this->params['action'] = $this->params['prefix'] . '_' . $this->params['action'];
00145             } elseif (strpos($this->params['action'], '_') !== false && !$privateAction) {
00146                 list($prefix, $action) = explode('_', $this->params['action']);
00147                 $privateAction = in_array($prefix, $prefixes);
00148             }
00149         }
00150 
00151         Router::setRequestInfo(array(
00152             $this->params, array('base' => $this->base, 'here' => $this->here, 'webroot' => $this->webroot)
00153         ));
00154 
00155         if ($privateAction) {
00156             return $this->cakeError('privateAction', array(array(
00157                 'className' => Inflector::camelize($this->params['controller'] . "Controller"),
00158                 'action' => $this->params['action'],
00159                 'webroot' => $this->webroot,
00160                 'url' => $url,
00161                 'base' => $this->base
00162             )));
00163         }
00164 
00165         $controller->base = $this->base;
00166         $controller->here = $this->here;
00167         $controller->webroot = $this->webroot;
00168         $controller->plugin = $this->plugin;
00169         $controller->params =& $this->params;
00170         $controller->action =& $this->params['action'];
00171         $controller->passedArgs = array_merge($this->params['pass'], $this->params['named']);
00172 
00173         if (!empty($this->params['data'])) {
00174             $controller->data =& $this->params['data'];
00175         } else {
00176             $controller->data = null;
00177         }
00178         if (array_key_exists('return', $this->params) && $this->params['return'] == 1) {
00179             $controller->autoRender = false;
00180         }
00181         if (!empty($this->params['bare'])) {
00182             $controller->autoLayout = false;
00183         }
00184         if (array_key_exists('layout', $this->params)) {
00185             if (empty($this->params['layout'])) {
00186                 $controller->autoLayout = false;
00187             } else {
00188                 $controller->layout = $this->params['layout'];
00189             }
00190         }
00191         if (isset($this->params['viewPath'])) {
00192             $controller->viewPath = $this->params['viewPath'];
00193         }
00194         return $this->_invoke($controller, $this->params);
00195     }
00196 /**
00197  * Invokes given controller's render action if autoRender option is set. Otherwise the
00198  * contents of the operation are returned as a string.
00199  *
00200  * @param object $controller Controller to invoke
00201  * @param array $params Parameters with at least the 'action' to invoke
00202  * @param boolean $missingAction Set to true if missing action should be rendered, false otherwise
00203  * @return string Output as sent by controller
00204  * @access protected
00205  */
00206     function _invoke(&$controller, $params) {
00207         $controller->constructClasses();
00208         $controller->Component->initialize($controller);
00209         $controller->beforeFilter();
00210         $controller->Component->startup($controller);
00211 
00212         $methods = array_flip($controller->methods);
00213 
00214         if (!isset($methods[strtolower($params['action'])])) {
00215             if ($controller->scaffold !== false) {
00216                 App::import('Core', 'Scaffold');
00217                 return new Scaffold($controller, $params);
00218             }
00219             return $this->cakeError('missingAction', array(array(
00220                 'className' => Inflector::camelize($params['controller']."Controller"),
00221                 'action' => $params['action'],
00222                 'webroot' => $this->webroot,
00223                 'url' => $this->here,
00224                 'base' => $this->base
00225             )));
00226         }
00227         $output = $controller->dispatchMethod($params['action'], $params['pass']);
00228 
00229         if ($controller->autoRender) {
00230             $controller->output = $controller->render();
00231         } elseif (empty($controller->output)) {
00232             $controller->output = $output;
00233         }
00234         $controller->Component->shutdown($controller);
00235         $controller->afterFilter();
00236 
00237         if (isset($params['return'])) {
00238             return $controller->output;
00239         }
00240         echo($controller->output);
00241     }
00242 /**
00243  * Sets the params when $url is passed as an array to Object::requestAction();
00244  *
00245  * @param array $url
00246  * @param array $additionalParams
00247  * @return string $url
00248  * @access private
00249  */
00250     function __extractParams($url, $additionalParams = array()) {
00251         $defaults = array('pass' => array(), 'named' => array(), 'form' => array());
00252         $this->params = array_merge($defaults, $url, $additionalParams);
00253         return Router::url($url);
00254     }
00255 /**
00256  * Returns array of GET and POST parameters. GET parameters are taken from given URL.
00257  *
00258  * @param string $fromUrl URL to mine for parameter information.
00259  * @return array Parameters found in POST and GET.
00260  * @access public
00261  */
00262     function parseParams($fromUrl) {
00263         $params = array();
00264 
00265         if (isset($_POST)) {
00266             $params['form'] = $_POST;
00267             if (ini_get('magic_quotes_gpc') === '1') {
00268                 $params['form'] = stripslashes_deep($params['form']);
00269             }
00270             if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) {
00271                 $params['form']['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE');
00272             }
00273             if (isset($params['form']['_method'])) {
00274                 if (isset($_SERVER) && !empty($_SERVER)) {
00275                     $_SERVER['REQUEST_METHOD'] = $params['form']['_method'];
00276                 } else {
00277                     $_ENV['REQUEST_METHOD'] = $params['form']['_method'];
00278                 }
00279                 unset($params['form']['_method']);
00280             }
00281         }
00282         $namedExpressions = Router::getNamedExpressions();
00283         extract($namedExpressions);
00284         include CONFIGS . 'routes.php';
00285         $params = array_merge(Router::parse($fromUrl), $params);
00286 
00287         if (strlen($params['action']) === 0) {
00288             $params['action'] = 'index';
00289         }
00290         if (isset($params['form']['data'])) {
00291             $params['data'] = Router::stripEscape($params['form']['data']);
00292             unset($params['form']['data']);
00293         }
00294         if (isset($_GET)) {
00295             if (ini_get('magic_quotes_gpc') === '1') {
00296                 $url = stripslashes_deep($_GET);
00297             } else {
00298                 $url = $_GET;
00299             }
00300             if (isset($params['url'])) {
00301                 $params['url'] = array_merge($params['url'], $url);
00302             } else {
00303                 $params['url'] = $url;
00304             }
00305         }
00306         foreach ($_FILES as $name => $data) {
00307             if ($name != 'data') {
00308                 $params['form'][$name] = $data;
00309             }
00310         }
00311         if (isset($_FILES['data'])) {
00312             foreach ($_FILES['data'] as $key => $data) {
00313                 foreach ($data as $model => $fields) {
00314                     foreach ($fields as $field => $value) {
00315                         if (is_array($value)) {
00316                             foreach ($value as $k => $v) {
00317                                 $params['data'][$model][$field][$k][$key] = $v;
00318                             }
00319                         } else {
00320                             $params['data'][$model][$field][$key] = $value;
00321                         }
00322                     }
00323                 }
00324             }
00325         }
00326         return $params;
00327     }
00328 /**
00329  * Returns a base URL and sets the proper webroot
00330  *
00331  * @return string Base URL
00332  * @access public
00333  */
00334     function baseUrl() {
00335         $dir = $webroot = null;
00336         $config = Configure::read('App');
00337         extract($config);
00338 
00339         if (!$base) {
00340             $base = $this->base;
00341         }
00342         if ($base !== false) {
00343             $this->webroot = $base . '/';
00344             return $this->base = $base;
00345         }
00346         if (!$baseUrl) {
00347             $replace = array('<', '>', '*', '\'', '"');
00348             $base = str_replace($replace, '', dirname(env('PHP_SELF')));
00349 
00350             if ($webroot === 'webroot' && $webroot === basename($base)) {
00351                 $base = dirname($base);
00352             }
00353             if ($dir === 'app' && $dir === basename($base)) {
00354                 $base = dirname($base);
00355             }
00356 
00357             if ($base === DS || $base === '.') {
00358                 $base = '';
00359             }
00360 
00361             $this->webroot = $base .'/';
00362             return $base;
00363         }
00364         $file = null;
00365 
00366         if ($baseUrl) {
00367             $file = '/' . basename($baseUrl);
00368             $base = dirname($baseUrl);
00369 
00370             if ($base === DS || $base === '.') {
00371                 $base = '';
00372             }
00373             $this->webroot = $base .'/';
00374 
00375             if (strpos($this->webroot, $dir) === false) {
00376                 $this->webroot .= $dir . '/' ;
00377             }
00378             if (strpos($this->webroot, $webroot) === false) {
00379                 $this->webroot .= $webroot . '/';
00380             }
00381             return $base . $file;
00382         }
00383         return false;
00384     }
00385 /**
00386  * Restructure params in case we're serving a plugin.
00387  *
00388  * @param array $params Array on where to re-set 'controller', 'action', and 'pass' indexes
00389  * @param boolean $reverse
00390  * @return array Restructured array
00391  * @access protected
00392  */
00393     function _restructureParams($params, $reverse = false) {
00394         if ($reverse === true) {
00395             extract(Router::getArgs($params['action']));
00396             $params = array_merge($params, array(
00397                 'controller'=> $params['plugin'],
00398                 'action'=> $params['controller'],
00399                 'pass' => array_merge($pass, $params['pass']),
00400                 'named' => array_merge($named, $params['named'])
00401             ));
00402             $this->plugin = $params['plugin'];
00403         } else {
00404             $params['plugin'] = $params['controller'];
00405             $params['controller'] = $params['action'];
00406             if (isset($params['pass'][0])) {
00407                 $params['action'] = $params['pass'][0];
00408                 array_shift($params['pass']);
00409             } else {
00410                 $params['action'] = null;
00411             }
00412         }
00413         return $params;
00414     }
00415 /**
00416  * Get controller to use, either plugin controller or application controller
00417  *
00418  * @param array $params Array of parameters
00419  * @return mixed name of controller if not loaded, or object if loaded
00420  * @access private
00421  */
00422     function &__getController($params = null) {
00423         if (!is_array($params)) {
00424             $original = $params = $this->params;
00425         }
00426         $controller = false;
00427         $ctrlClass = $this->__loadController($params);
00428         if (!$ctrlClass) {
00429             if (!isset($params['plugin'])) {
00430                 $params = $this->_restructureParams($params);
00431             } else {
00432                 if (empty($original['pass']) && $original['action'] == 'index') {
00433                     $params['action'] = null;
00434                 }
00435                 $params = $this->_restructureParams($params, true);
00436             }
00437             $ctrlClass = $this->__loadController($params);
00438             if (!$ctrlClass) {
00439                 $this->params = $original;
00440                 return $controller;
00441             }
00442         } else {
00443             $params = $this->params;
00444         }
00445         $name = $ctrlClass;
00446         $ctrlClass = $ctrlClass . 'Controller';
00447         if (class_exists($ctrlClass)) {
00448             if (strtolower(get_parent_class($ctrlClass)) === strtolower($name . 'AppController') && empty($params['plugin'])) {
00449                 $params = $this->_restructureParams($params);
00450                 $params = $this->_restructureParams($params, true);
00451             }
00452             $this->params = $params;
00453             $controller =& new $ctrlClass();
00454         }
00455         return $controller;
00456     }
00457 /**
00458  * Load controller and return controller class
00459  *
00460  * @param array $params Array of parameters
00461  * @return string|bool Name of controller class name
00462  * @access private
00463  */
00464     function __loadController($params) {
00465         $pluginName = $pluginPath = $controller = null;
00466         if (!empty($params['plugin'])) {
00467             $this->plugin = $params['plugin'];
00468             $pluginName = Inflector::camelize($params['plugin']);
00469             $pluginPath = $pluginName . '.';
00470             $this->params['controller'] = $this->plugin;
00471             $controller = $pluginName;
00472         }
00473         if (!empty($params['controller'])) {
00474             $this->params['controller'] = $params['controller'];
00475             $controller = Inflector::camelize($params['controller']);
00476         }
00477         if ($pluginPath . $controller) {
00478             if (App::import('Controller', $pluginPath . $controller)) {
00479                 return $controller;
00480             }
00481         }
00482         return false;
00483     }
00484 /**
00485  * Returns the REQUEST_URI from the server environment, or, failing that,
00486  * constructs a new one, using the PHP_SELF constant and other variables.
00487  *
00488  * @return string URI
00489  * @access public
00490  */
00491     function uri() {
00492         foreach (array('HTTP_X_REWRITE_URL', 'REQUEST_URI', 'argv') as $var) {
00493             if ($uri = env($var)) {
00494                 if ($var == 'argv') {
00495                     $uri = $uri[0];
00496                 }
00497                 break;
00498             }
00499         }
00500         $base = preg_replace('/^\//', '', '' . Configure::read('App.baseUrl'));
00501 
00502         if ($base) {
00503             $uri = preg_replace('/^(?:\/)?(?:' . preg_quote($base, '/') . ')?(?:url=)?/', '', $uri);
00504         }
00505         if (PHP_SAPI == 'isapi') {
00506             $uri = preg_replace('/^(?:\/)?(?:\/)?(?:\?)?(?:url=)?/', '', $uri);
00507         }
00508         if (!empty($uri)) {
00509             if (key($_GET) && strpos(key($_GET), '?') !== false) {
00510                 unset($_GET[key($_GET)]);
00511             }
00512             $uri = preg_split('/\?/', $uri, 2);
00513 
00514             if (isset($uri[1])) {
00515                 parse_str($uri[1], $_GET);
00516             }
00517             $uri = $uri[0];
00518         } else {
00519             $uri = env('QUERY_STRING');
00520         }
00521         if (is_string($uri) && strpos($uri, 'index.php') !== false) {
00522             list(, $uri) = explode('index.php', $uri, 2);
00523         }
00524         if (empty($uri) || $uri == '/' || $uri == '//') {
00525             return '';
00526         }
00527         return str_replace('//', '/', '/' . $uri);
00528     }
00529 /**
00530  * Returns and sets the $_GET[url] derived from the REQUEST_URI
00531  *
00532  * @param string $uri Request URI
00533  * @param string $base Base path
00534  * @return string URL
00535  * @access public
00536  */
00537     function getUrl($uri = null, $base = null) {
00538         if (empty($_GET['url'])) {
00539             if ($uri == null) {
00540                 $uri = $this->uri();
00541             }
00542             if ($base == null) {
00543                 $base = $this->base;
00544             }
00545             $url = null;
00546             $tmpUri = preg_replace('/^(?:\?)?(?:\/)?/', '', $uri);
00547             $baseDir = preg_replace('/^\//', '', dirname($base)) . '/';
00548 
00549             if ($tmpUri === '/' || $tmpUri == $baseDir || $tmpUri == $base) {
00550                 $url = $_GET['url'] = '/';
00551             } else {
00552                 if ($base && strpos($uri, $base) !== false) {
00553                     $elements = explode($base, $uri);
00554                 } elseif (preg_match('/^[\/\?\/|\/\?|\?\/]/', $uri)) {
00555                     $elements = array(1 => preg_replace('/^[\/\?\/|\/\?|\?\/]/', '', $uri));
00556                 } else {
00557                     $elements = array();
00558                 }
00559 
00560                 if (!empty($elements[1])) {
00561                     $_GET['url'] = $elements[1];
00562                     $url = $elements[1];
00563                 } else {
00564                     $url = $_GET['url'] = '/';
00565                 }
00566 
00567                 if (strpos($url, '/') === 0 && $url != '/') {
00568                     $url = $_GET['url'] = substr($url, 1);
00569                 }
00570             }
00571         } else {
00572             $url = $_GET['url'];
00573         }
00574         if ($url{0} == '/') {
00575             $url = substr($url, 1);
00576         }
00577         return $url;
00578     }
00579 /**
00580  * Outputs cached dispatch for js, css, img, view cache
00581  *
00582  * @param string $url Requested URL
00583  * @access public
00584  */
00585     function cached($url) {
00586         if (strpos($url, 'css/') !== false || strpos($url, 'js/') !== false || strpos($url, 'img/') !== false) {
00587             if (strpos($url, 'ccss/') === 0) {
00588                 include WWW_ROOT . DS . Configure::read('Asset.filter.css');
00589                 $this->_stop();
00590             } elseif (strpos($url, 'cjs/') === 0) {
00591                 include WWW_ROOT . DS . Configure::read('Asset.filter.js');
00592                 $this->_stop();
00593             }
00594             $isAsset = false;
00595             $assets = array('js' => 'text/javascript', 'css' => 'text/css', 'gif' => 'image/gif', 'jpg' => 'image/jpeg', 'png' => 'image/png');
00596             $ext = array_pop(explode('.', $url));
00597 
00598             foreach ($assets as $type => $contentType) {
00599                 if ($type === $ext) {
00600                     if ($type === 'css' || $type === 'js') {
00601                         $pos = strpos($url, $type . '/');
00602                     } else {
00603                         $pos = strpos($url, 'img/');
00604                     }
00605                     $isAsset = true;
00606                     break;
00607                 }
00608             }
00609 
00610             if ($isAsset === true) {
00611                 $ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
00612 
00613                 if ($ob && Configure::read('Asset.compress')) {
00614                     ob_start();
00615                     ob_start('ob_gzhandler');
00616                 }
00617                 $assetFile = null;
00618                 $paths = array();
00619 
00620                 if ($pos > 0) {
00621                     $plugin = substr($url, 0, $pos - 1);
00622                     $url = str_replace($plugin . '/', '', $url);
00623                     $pluginPaths = Configure::read('pluginPaths');
00624                     $count = count($pluginPaths);
00625                     for ($i = 0; $i < $count; $i++) {
00626                         $paths[] = $pluginPaths[$i] . $plugin . DS . 'vendors' . DS;
00627                     }
00628                 }
00629                 $paths = array_merge($paths, Configure::read('vendorPaths'));
00630 
00631                 foreach ($paths as $path) {
00632                     if (is_file($path . $url) && file_exists($path . $url)) {
00633                         $assetFile = $path . $url;
00634                         break;
00635                     }
00636                 }
00637 
00638                 if ($assetFile !== null) {
00639                     $fileModified = filemtime($assetFile);
00640                     header("Date: " . date("D, j M Y G:i:s ", $fileModified) . 'GMT');
00641                     header('Content-type: ' . $assets[$type]);
00642                     header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
00643                     header("Cache-Control: cache");
00644                     header("Pragma: cache");
00645                     if ($type === 'css' || $type === 'js') {
00646                         include($assetFile);
00647                     } else {
00648                         readfile($assetFile);
00649                     }
00650 
00651                     if (Configure::read('Asset.compress')) {
00652                         ob_end_flush();
00653                     }
00654                     return true;
00655                 }
00656             }
00657         }
00658 
00659         if (Configure::read('Cache.check') === true) {
00660             $path = $this->here;
00661             if ($this->here == '/') {
00662                 $path = 'home';
00663             }
00664             $path = strtolower(Inflector::slug($path));
00665 
00666             $filename = CACHE . 'views' . DS . $path . '.php';
00667 
00668             if (!file_exists($filename)) {
00669                 $filename = CACHE . 'views' . DS . $path . '_index.php';
00670             }
00671 
00672             if (file_exists($filename)) {
00673                 if (!class_exists('View')) {
00674                     App::import('Core', 'View');
00675                 }
00676                 $controller = null;
00677                 $view =& new View($controller, false);
00678                 return $view->renderCache($filename, getMicrotime());
00679             }
00680         }
00681         return false;
00682     }
00683 }
00684 ?>

Generated on Sun Nov 22 00:30:52 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7