PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/standard/tags/release-1.0.2/library/Zend/Controller/Dispatcher/Standard.php

https://github.com/jorgenils/zend-framework
PHP | 418 lines | 191 code | 48 blank | 179 comment | 28 complexity | 0bd49b879e9fc4878e01a490ba8dc014 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Controller
  17. * @subpackage Dispatcher
  18. * @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /** Zend_Loader */
  22. require_once 'Zend/Loader.php';
  23. /** Zend_Controller_Dispatcher_Abstract */
  24. require_once 'Zend/Controller/Dispatcher/Abstract.php';
  25. /** Zend_Controller_Request_Abstract */
  26. require_once 'Zend/Controller/Request/Abstract.php';
  27. /** Zend_Controller_Response_Abstract */
  28. require_once 'Zend/Controller/Response/Abstract.php';
  29. /** Zend_Controller_Action */
  30. require_once 'Zend/Controller/Action.php';
  31. /**
  32. * @category Zend
  33. * @package Zend_Controller
  34. * @subpackage Dispatcher
  35. * @copyright Copyright (c) 2005-2007 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class Zend_Controller_Dispatcher_Standard extends Zend_Controller_Dispatcher_Abstract
  39. {
  40. /**
  41. * Current dispatchable directory
  42. * @var string
  43. */
  44. protected $_curDirectory;
  45. /**
  46. * Current module (formatted)
  47. * @var string
  48. */
  49. protected $_curModule;
  50. /**
  51. * Constructor: Set current module to default value
  52. *
  53. * @param array $params
  54. * @return void
  55. */
  56. public function __construct(array $params = array())
  57. {
  58. parent::__construct($params);
  59. $this->_curModule = $this->getDefaultModule();
  60. }
  61. /**
  62. * Add a single path to the controller directory stack
  63. *
  64. * @param string $path
  65. * @param string $module
  66. * @return Zend_Controller_Dispatcher_Standard
  67. */
  68. public function addControllerDirectory($path, $module = null)
  69. {
  70. if (null === $module) {
  71. $module = $this->_defaultModule;
  72. }
  73. $this->getFrontController()->addControllerDirectory($path, $module);
  74. return $this;
  75. }
  76. /**
  77. * Set controller directory
  78. *
  79. * @param array|string $directory
  80. * @return Zend_Controller_Dispatcher_Standard
  81. */
  82. public function setControllerDirectory($directory)
  83. {
  84. $this->getFrontController()->setControllerDirectory($directory);
  85. return $this;
  86. }
  87. /**
  88. * Return the currently set directories for Zend_Controller_Action class
  89. * lookup
  90. *
  91. * If a module is specified, returns just that directory.
  92. *
  93. * @param string $module Module name
  94. * @return array|string Returns array of all directories by default, single
  95. * module directory if module argument provided
  96. */
  97. public function getControllerDirectory($module = null)
  98. {
  99. $directories = $this->getFrontController()->getControllerDirectory();
  100. if ((null !== $module) && (isset($directories[$module]))) {
  101. return $directories[$module];
  102. }
  103. return $directories;
  104. }
  105. /**
  106. * Format the module name.
  107. *
  108. * @param string $unformatted
  109. * @return string
  110. */
  111. public function formatModuleName($unformatted)
  112. {
  113. if ($this->_defaultModule == $unformatted) {
  114. return $unformatted;
  115. }
  116. return ucfirst($this->_formatName($unformatted));
  117. }
  118. /**
  119. * Convert a class name to a filename
  120. *
  121. * @param string $class
  122. * @return string
  123. */
  124. public function classToFilename($class)
  125. {
  126. return str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php';
  127. }
  128. /**
  129. * Returns TRUE if the Zend_Controller_Request_Abstract object can be
  130. * dispatched to a controller.
  131. *
  132. * Use this method wisely. By default, the dispatcher will fall back to the
  133. * default controller (either in the module specified or the global default)
  134. * if a given controller does not exist. This method returning false does
  135. * not necessarily indicate the dispatcher will not still dispatch the call.
  136. *
  137. * @param Zend_Controller_Request_Abstract $action
  138. * @return boolean
  139. */
  140. public function isDispatchable(Zend_Controller_Request_Abstract $request)
  141. {
  142. $className = $this->getControllerClass($request);
  143. if (!$className) {
  144. return false;
  145. }
  146. $fileSpec = $this->classToFilename($className);
  147. $dispatchDir = $this->getDispatchDirectory();
  148. $test = $dispatchDir . DIRECTORY_SEPARATOR . $fileSpec;
  149. return Zend_Loader::isReadable($test);
  150. }
  151. /**
  152. * Dispatch to a controller/action
  153. *
  154. * By default, if a controller is not dispatchable, dispatch() will throw
  155. * an exception. If you wish to use the default controller instead, set the
  156. * param 'useDefaultControllerAlways' via {@link setParam()}.
  157. *
  158. * @param Zend_Controller_Request_Abstract $request
  159. * @param Zend_Controller_Response_Abstract $response
  160. * @return boolean
  161. * @throws Zend_Controller_Dispatcher_Exception
  162. */
  163. public function dispatch(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response)
  164. {
  165. $this->setResponse($response);
  166. /**
  167. * Get controller class
  168. */
  169. if (!$this->isDispatchable($request)) {
  170. $controller = $request->getControllerName();
  171. if (!$this->getParam('useDefaultControllerAlways') && !empty($controller)) {
  172. require_once 'Zend/Controller/Dispatcher/Exception.php';
  173. throw new Zend_Controller_Dispatcher_Exception('Invalid controller specified (' . $request->getControllerName() . ')');
  174. }
  175. $className = $this->getDefaultControllerClass($request);
  176. } else {
  177. $className = $this->getControllerClass($request);
  178. if (!$className) {
  179. $className = $this->getDefaultControllerClass($request);
  180. }
  181. }
  182. /**
  183. * Load the controller class file
  184. */
  185. $className = $this->loadClass($className);
  186. /**
  187. * Instantiate controller with request, response, and invocation
  188. * arguments; throw exception if it's not an action controller
  189. */
  190. $controller = new $className($request, $this->getResponse(), $this->getParams());
  191. if (!$controller instanceof Zend_Controller_Action) {
  192. require_once 'Zend/Controller/Dispatcher/Exception.php';
  193. throw new Zend_Controller_Dispatcher_Exception("Controller '$className' is not an instance of Zend_Controller_Action");
  194. }
  195. /**
  196. * Retrieve the action name
  197. */
  198. $action = $this->getActionMethod($request);
  199. /**
  200. * Dispatch the method call
  201. */
  202. $request->setDispatched(true);
  203. // by default, buffer output
  204. $disableOb = $this->getParam('disableOutputBuffering');
  205. $obLevel = ob_get_level();
  206. if (empty($disableOb)) {
  207. ob_start();
  208. }
  209. try {
  210. $controller->dispatch($action);
  211. } catch (Exception $e) {
  212. // Clean output buffer on error
  213. $curObLevel = ob_get_level();
  214. if ($curObLevel > $obLevel) {
  215. do {
  216. ob_get_clean();
  217. $curObLevel = ob_get_level();
  218. } while ($curObLevel > $obLevel);
  219. }
  220. throw $e;
  221. }
  222. if (empty($disableOb)) {
  223. $content = ob_get_clean();
  224. $response->appendBody($content);
  225. }
  226. // Destroy the page controller instance and reflection objects
  227. $controller = null;
  228. }
  229. /**
  230. * Load a controller class
  231. *
  232. * Attempts to load the controller class file from
  233. * {@link getControllerDirectory()}. If the controller belongs to a
  234. * module, looks for the module prefix to the controller class.
  235. *
  236. * @param string $className
  237. * @return string Class name loaded
  238. * @throws Zend_Controller_Dispatcher_Exception if class not loaded
  239. */
  240. public function loadClass($className)
  241. {
  242. $dispatchDir = $this->getDispatchDirectory();
  243. $loadFile = $dispatchDir . DIRECTORY_SEPARATOR . $this->classToFilename($className);
  244. $dir = dirname($loadFile);
  245. $file = basename($loadFile);
  246. try {
  247. Zend_Loader::loadFile($file, $dir, true);
  248. } catch (Zend_Exception $e) {
  249. require_once 'Zend/Controller/Dispatcher/Exception.php';
  250. throw new Zend_Controller_Dispatcher_Exception('Cannot load controller class "' . $className . '" from file "' . $file . '" in directory "' . $dir . '"');
  251. }
  252. if ($this->_defaultModule != $this->_curModule) {
  253. $className = $this->formatModuleName($this->_curModule) . '_' . $className;
  254. }
  255. if (!class_exists($className)) {
  256. require_once 'Zend/Controller/Dispatcher/Exception.php';
  257. throw new Zend_Controller_Dispatcher_Exception('Invalid controller class ("' . $className . '")');
  258. }
  259. return $className;
  260. }
  261. /**
  262. * Get controller class name
  263. *
  264. * Try request first; if not found, try pulling from request parameter;
  265. * if still not found, fallback to default
  266. *
  267. * @param Zend_Controller_Request_Abstract $request
  268. * @return string|false Returns class name on success
  269. */
  270. public function getControllerClass(Zend_Controller_Request_Abstract $request)
  271. {
  272. $controllerName = $request->getControllerName();
  273. if (empty($controllerName)) {
  274. if (!$this->getParam('useDefaultControllerAlways')) {
  275. return false;
  276. }
  277. $controllerName = $this->getDefaultControllerName();
  278. $request->setControllerName($controllerName);
  279. }
  280. $className = $this->formatControllerName($controllerName);
  281. $controllerDirs = $this->getControllerDirectory();
  282. $this->_curModule = $this->_defaultModule;
  283. $this->_curDirectory = $controllerDirs[$this->_defaultModule];
  284. $module = $request->getModuleName();
  285. if ($this->isValidModule($module)) {
  286. $this->_curModule = $module;
  287. $this->_curDirectory = $controllerDirs[$module];
  288. } else {
  289. $request->setModuleName($this->_curModule);
  290. }
  291. return $className;
  292. }
  293. /**
  294. * Determine if a given module is valid
  295. *
  296. * @param string $module
  297. * @return bool
  298. */
  299. public function isValidModule($module)
  300. {
  301. $controllerDir = $this->getControllerDirectory();
  302. return ((null !== $module) && isset($controllerDir[$module]));
  303. }
  304. /**
  305. * Retrieve default controller class
  306. *
  307. * Determines whether the default controller to use lies within the
  308. * requested module, or if the global default should be used.
  309. *
  310. * By default, will only use the module default unless that controller does
  311. * not exist; if this is the case, it falls back to the default controller
  312. * in the default module.
  313. *
  314. * @param Zend_Controller_Request_Abstract $request
  315. * @return string
  316. */
  317. public function getDefaultControllerClass(Zend_Controller_Request_Abstract $request)
  318. {
  319. $controller = $this->getDefaultControllerName();
  320. $default = $this->formatControllerName($controller);
  321. $request->setControllerName($controller)
  322. ->setActionName(null);
  323. $module = $request->getModuleName();
  324. $controllerDirs = $this->getControllerDirectory();
  325. $this->_curModule = $this->_defaultModule;
  326. $this->_curDirectory = $controllerDirs[$this->_defaultModule];
  327. if ($this->isValidModule($module)) {
  328. $moduleDir = $controllerDirs[$module];
  329. $fileSpec = $moduleDir . DIRECTORY_SEPARATOR . $this->classToFilename($default);
  330. if (Zend_Loader::isReadable($fileSpec)) {
  331. $request->setModuleName($module);
  332. $this->_curModule = $this->formatModuleName($module);
  333. $this->_curDirectory = $moduleDir;
  334. }
  335. } else {
  336. $request->setModuleName($this->_defaultModule);
  337. }
  338. return $default;
  339. }
  340. /**
  341. * Return the value of the currently selected dispatch directory (as set by
  342. * {@link getController()})
  343. *
  344. * @return string
  345. */
  346. public function getDispatchDirectory()
  347. {
  348. return $this->_curDirectory;
  349. }
  350. /**
  351. * Determine the action name
  352. *
  353. * First attempt to retrieve from request; then from request params
  354. * using action key; default to default action
  355. *
  356. * Returns formatted action name
  357. *
  358. * @param Zend_Controller_Request_Abstract $request
  359. * @return string
  360. */
  361. public function getActionMethod(Zend_Controller_Request_Abstract $request)
  362. {
  363. $action = $request->getActionName();
  364. if (empty($action)) {
  365. $action = $this->getDefaultAction();
  366. $request->setActionName($action);
  367. }
  368. return $this->formatActionName($action);
  369. }
  370. }