dispatch($url); } } /** * Dispatches and invokes given URL, handing over control to the involved controllers, and then renders the * results (if autoRender is set). * * If no controller of given name can be found, invoke() shows error messages in * the form of Missing Controllers information. It does the same with Actions (methods of Controllers are called * Actions). * * @param mixed $url Either a string url or a CakeRequest object information to work on. If $url is a string * It will be used to create the request object. * @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params * @return boolean Success * @throws MissingControllerException, MissingActionException, PrivateActionException if any of those error states * are encountered. */ public function dispatch($url = null, $additionalParams = array()) { if (is_array($url)) { $url = $this->_extractParams($url, $additionalParams); } if ($url instanceof CakeRequest) { $request = $url; } else { $request = new CakeRequest($url); } $this->here = $request->here; if ($this->asset($request->url) || $this->cached($request->url)) { return; } $request = $this->parseParams($request, $additionalParams); $this->request = $request; $controller = $this->_getController(); if (!is_object($controller)) { Router::setRequestInfo($request); throw new MissingControllerException(array( 'controller' => Inflector::camelize($request->params['controller']) . 'Controller' )); } $privateAction = $request->params['action'][0] === '_'; $prefixes = Router::prefixes(); if (!empty($prefixes)) { if (isset($request->params['prefix'])) { $request->params['action'] = $request->params['prefix'] . '_' . $request->params['action']; } elseif (strpos($request->params['action'], '_') > 0) { list($prefix, $action) = explode('_', $request->params['action']); $privateAction = in_array($prefix, $prefixes); } } Router::setRequestInfo($request); if ($privateAction) { throw new PrivateActionException(array( 'controller' => Inflector::camelize($request->params['controller']) . "Controller", 'action' => $request->params['action'] )); } return $this->_invoke($controller, $request); } /** * Initializes the components and models a controller will be using. * Triggers the controller action, and invokes the rendering if Controller::$autoRender is true and echo's the output. * Otherwise the return value of the controller action are returned. * * @param object $controller Controller to invoke * @param array $params Parameters with at least the 'action' to invoke * @param boolean $missingAction Set to true if missing action should be rendered, false otherwise * @return string Output as sent by controller */ protected function _invoke(&$controller, $request) { $controller->constructClasses(); $controller->startupProcess(); $methods = array_flip($controller->methods); if (!isset($methods[$request->params['action']])) { if ($controller->scaffold !== false) { App::import('Controller', 'Scaffold', false); return new Scaffold($controller, $request); } throw new MissingActionException(array( 'controller' => Inflector::camelize($request->params['controller']) . "Controller", 'action' => $request->params['action'] )); } $result =& call_user_func_array(array(&$controller, $request->params['action']), $request->params['pass']); $response = $controller->getResponse(); if ($controller->autoRender) { $controller->render(); } elseif ($response->body() === null) { $response->body($result); } $controller->shutdownProcess(); if (isset($request->params['return'])) { return $response->body(); } $response->send(); } /** * Sets the params when $url is passed as an array to Object::requestAction(); * Merges the $url and $additionalParams and creates a string url. * * @param array $url Array or request parameters * @param array $additionalParams Array of additional parameters. * @return string $url The generated url string. */ protected function _extractParams($url, $additionalParams = array()) { $defaults = array('pass' => array(), 'named' => array(), 'form' => array()); $params = array_merge($defaults, $url, $additionalParams); $this->params = $params; $params += array('base' => false, 'url' => array()); return ltrim(Router::reverse($params), '/'); } /** * Returns array of GET and POST parameters. GET parameters are taken from given URL. * * @param CakeRequest $fromUrl CakeRequest object to mine for parameter information. * @return array Parameters found in POST and GET. */ public function parseParams(CakeRequest $request, $additionalParams = array()) { $namedExpressions = Router::getNamedExpressions(); extract($namedExpressions); include CONFIGS . 'routes.php'; $params = Router::parse($request->url); $request->addParams($params); if (!empty($additionalParams)) { $request->addParams($additionalParams); } return $request; } /** * Get controller to use, either plugin controller or application controller * * @param array $params Array of parameters * @return mixed name of controller if not loaded, or object if loaded */ protected function &_getController() { $controller = false; $ctrlClass = $this->__loadController($this->request); if (!$ctrlClass) { return $controller; } $ctrlClass .= 'Controller'; if (class_exists($ctrlClass)) { $controller = new $ctrlClass($this->request); } return $controller; } /** * Load controller and return controller classname * * @param array $params Array of parameters * @return string|bool Name of controller class name * @access private */ function __loadController($request) { $pluginName = $pluginPath = $controller = null; if (!empty($request->params['plugin'])) { $pluginName = $controller = Inflector::camelize($request->params['plugin']); $pluginPath = $pluginName . '.'; } if (!empty($request->params['controller'])) { $controller = Inflector::camelize($request->params['controller']); } if ($pluginPath . $controller) { if (App::import('Controller', $pluginPath . $controller)) { return $controller; } } return false; } /** * Outputs cached dispatch view cache * * @param string $url Requested URL */ public function cached($url) { if (Configure::read('Cache.check') === true) { $path = $this->here; if ($this->here == '/') { $path = 'home'; } $path = strtolower(Inflector::slug($path)); $filename = CACHE . 'views' . DS . $path . '.php'; if (!file_exists($filename)) { $filename = CACHE . 'views' . DS . $path . '_index.php'; } if (file_exists($filename)) { if (!class_exists('View')) { App::import('View', 'View', false); } $controller = null; $view = new View($controller); return $view->renderCache($filename, microtime(true)); } } return false; } /** * Checks if a requested asset exists and sends it to the browser * * @param $url string $url Requested URL * @return boolean True on success if the asset file was found and sent */ public function asset($url) { if (strpos($url, '..') !== false || strpos($url, '.') === false) { return false; } $filters = Configure::read('Asset.filter'); $isCss = ( strpos($url, 'ccss/') === 0 || preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?response) { $this->response = new CakeResponse(); } if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) { $this->response->statusCode(404); $this->response->send(); return $this->_stop(); } elseif ($isCss) { include WWW_ROOT . DS . $filters['css']; $this->_stop(); } elseif ($isJs) { include WWW_ROOT . DS . $filters['js']; $this->_stop(); } $controller = null; $ext = array_pop(explode('.', $url)); $parts = explode('/', $url); $assetFile = null; if ($parts[0] === 'theme') { $themeName = $parts[1]; unset($parts[0], $parts[1]); $fileFragment = implode(DS, $parts); $path = App::themePath($themeName) . 'webroot' . DS; if (file_exists($path . $fileFragment)) { $assetFile = $path . $fileFragment; } } else { $plugin = $parts[0]; unset($parts[0]); $fileFragment = implode(DS, $parts); $pluginWebroot = App::pluginPath($plugin) . 'webroot' . DS; if (file_exists($pluginWebroot . $fileFragment)) { $assetFile = $pluginWebroot . $fileFragment; } } if ($assetFile !== null) { $this->_deliverAsset($assetFile, $ext); return true; } return false; } /** * Sends an asset file to the client * * @param string $assetFile Path to the asset file in the file system * @param string $ext The extension of the file to determine its mime type * @return void */ protected function _deliverAsset($assetFile, $ext) { ob_start(); $compressionEnabled = Configure::read('Asset.compress') && $this->response->compress(); if ($this->response->type($ext) == $ext) { $contentType = 'application/octet-stream'; $agent = env('HTTP_USER_AGENT'); if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) { $contentType = 'application/octetstream'; } $this->response->type($contentType); } $this->response->cache(filemtime($assetFile)); $this->response->send(); ob_clean(); if ($ext === 'css' || $ext === 'js') { include($assetFile); } else { readfile($assetFile); } if ($compressionEnabled) { ob_end_flush(); } } }