1. sfController.class.php
  2. /** * sfController directs application flow. * * @package symfony * @subpackage controller * @author Fabien Potencier * @author Sean Kerr * @version SVN: $Id: sfController.class.php 24265 2009-11-23 11:55:33Z Kris.Wallsmith $ */
  3. abstract class sfController
  4. {
  5. protected
  6. $context = null,
  7. $dispatcher = null,
  8. $controllerClasses = array(),
  9. $renderMode = sfView::RENDER_CLIENT;
  10. /**
  11. * Class constructor.
  12. *
  13. * @see initialize()
  14. */
  15. public function __construct($context)
  16. {
  17. $this->initialize($context);
  18. }
  19. /**
  20. * Initializes this controller.
  21. *
  22. * @param sfContext $context A sfContext implementation instance
  23. */
  24. public function initialize($context)
  25. {
  26. $this->context = $context;
  27. $this->dispatcher = $context->getEventDispatcher();
  28. }
  29. /**
  30. * Indicates whether or not a module has a specific component.
  31. *
  32. * @param string $moduleName A module name
  33. * @param string $componentName An component name
  34. *
  35. * @return bool true, if the component exists, otherwise false
  36. */
  37. public function componentExists($moduleName, $componentName)
  38. {
  39. return $this->controllerExists($moduleName, $componentName, 'component', false);
  40. }
  41. /**
  42. * Indicates whether or not a module has a specific action.
  43. *
  44. * @param string $moduleName A module name
  45. * @param string $actionName An action name
  46. *
  47. * @return bool true, if the action exists, otherwise false
  48. */
  49. public function actionExists($moduleName, $actionName)
  50. {
  51. return $this->controllerExists($moduleName, $actionName, 'action', false);
  52. }
  53. /**
  54. * Looks for a controller and optionally throw exceptions if existence is required (i.e.
  55. * in the case of {@link getController()}).
  56. *
  57. * @param string $moduleName The name of the module
  58. * @param string $controllerName The name of the controller within the module
  59. * @param string $extension Either 'action' or 'component' depending on the type of controller to look for
  60. * @param boolean $throwExceptions Whether to throw exceptions if the controller doesn't exist
  61. *
  62. * @throws sfConfigurationException thrown if the module is not enabled
  63. * @throws sfControllerException thrown if the controller doesn't exist and the $throwExceptions parameter is set to true
  64. *
  65. * @return boolean true if the controller exists, false otherwise
  66. */
  67. protected function controllerExists($moduleName, $controllerName, $extension, $throwExceptions)
  68. {
  69. $dirs = $this->context->getConfiguration()->getControllerDirs($moduleName);
  70. foreach ($dirs as $dir => $checkEnabled)
  71. {
  72. // plugin module enabled?
  73. if ($checkEnabled && !in_array($moduleName, sfConfig::get('sf_enabled_modules')) && is_readable($dir))
  74. {
  75. throw new sfConfigurationException(sprintf('The module "%s" is not enabled.', $moduleName));
  76. }
  77. // one action per file or one file for all actions
  78. $classFile = strtolower($extension);
  79. $classSuffix = ucfirst(strtolower($extension));
  80. $file = $dir.'/'.$controllerName.$classSuffix.'.class.php';
  81. if (is_readable($file))
  82. {
  83. // action class exists
  84. require_once($file);
  85. $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix] = $controllerName.$classSuffix;
  86. return true;
  87. }
  88. $module_file = $dir.'/'.$classFile.'s.class.php';
  89. if (is_readable($module_file))
  90. {
  91. // module class exists
  92. require_once($module_file);
  93. if (!class_exists($moduleName.$classSuffix.'s', false))
  94. {
  95. if ($throwExceptions)
  96. {
  97. throw new sfControllerException(sprintf('There is no "%s" class in your action file "%s".', $moduleName.$classSuffix.'s', $module_file));
  98. }
  99. return false;
  100. }
  101. // action is defined in this class?
  102. if (!in_array('execute'.ucfirst($controllerName), get_class_methods($moduleName.$classSuffix.'s')))
  103. {
  104. if ($throwExceptions)
  105. {
  106. throw new sfControllerException(sprintf('There is no "%s" method in your action class "%s".', 'execute'.ucfirst($controllerName), $moduleName.$classSuffix.'s'));
  107. }
  108. return false;
  109. }
  110. $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix] = $moduleName.$classSuffix.'s';
  111. return true;
  112. }
  113. }
  114. // send an exception if debug
  115. if ($throwExceptions && sfConfig::get('sf_debug'))
  116. {
  117. $dirs = array_map(array('sfDebug', 'shortenFilePath'), array_keys($dirs));
  118. throw new sfControllerException(sprintf('Controller "%s/%s" does not exist in: %s.', $moduleName, $controllerName, implode(', ', $dirs)));
  119. }
  120. return false;
  121. }
  122. /**
  123. * Forwards the request to another action.
  124. *
  125. * @param string $moduleName A module name
  126. * @param string $actionName An action name
  127. *
  128. * @throws sfConfigurationException If an invalid configuration setting has been found
  129. * @throws sfForwardException If an error occurs while forwarding the request
  130. * @throws sfError404Exception If the action not exist
  131. * @throws sfInitializationException If the action could not be initialized
  132. */
  133. public function forward($moduleName, $actionName)
  134. {
  135. // replace unwanted characters
  136. $moduleName = preg_replace('/[^a-z0-9_]+/i', '', $moduleName);
  137. $actionName = preg_replace('/[^a-z0-9_]+/i', '', $actionName);
  138. if ($this->getActionStack()->getSize() >= 5)
  139. {
  140. // let's kill this party before it turns into cpu cycle hell
  141. throw new sfForwardException('Too many forwards have been detected for this request.');
  142. }
  143. // check for a module generator config file
  144. $this->context->getConfigCache()->import('modules/'.$moduleName.'/config/generator.yml', false, true);
  145. if (!$this->actionExists($moduleName, $actionName))
  146. {
  147. // the requested action doesn't exist
  148. if (sfConfig::get('sf_logging_enabled'))
  149. {
  150. $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Action "%s/%s" does not exist', $moduleName, $actionName))));
  151. }
  152. throw new sfError404Exception(sprintf('Action "%s/%s" does not exist.', $moduleName, $actionName));
  153. }
  154. // create an instance of the action
  155. $actionInstance = $this->getAction($moduleName, $actionName);
  156. // add a new action stack entry
  157. $this->getActionStack()->addEntry($moduleName, $actionName, $actionInstance);
  158. // include module configuration
  159. require($this->context->getConfigCache()->checkConfig('modules/'.$moduleName.'/config/module.yml'));
  160. // module enabled?
  161. if (sfConfig::get('mod_'.strtolower($moduleName).'_enabled'))
  162. {
  163. // check for a module config.php
  164. $moduleConfig = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/config/config.php';
  165. if (is_readable($moduleConfig))
  166. {
  167. require_once($moduleConfig);
  168. }
  169. // create a new filter chain
  170. $filterChain = new sfFilterChain();
  171. $filterChain->loadConfiguration($actionInstance);
  172. $this->context->getEventDispatcher()->notify(new sfEvent($this, 'controller.change_action', array('module' => $moduleName, 'action' => $actionName)));
  173. if ($moduleName == sfConfig::get('sf_error_404_module') && $actionName == sfConfig::get('sf_error_404_action'))
  174. {
  175. $this->context->getResponse()->setStatusCode(404);
  176. $this->context->getResponse()->setHttpHeader('Status', '404 Not Found');
  177. $this->dispatcher->notify(new sfEvent($this, 'controller.page_not_found', array('module' => $moduleName, 'action' => $actionName)));
  178. }
  179. // process the filter chain
  180. $filterChain->execute();
  181. }
  182. else
  183. {
  184. $moduleName = sfConfig::get('sf_module_disabled_module');
  185. $actionName = sfConfig::get('sf_module_disabled_action');
  186. if (!$this->actionExists($moduleName, $actionName))
  187. {
  188. // cannot find mod disabled module/action
  189. throw new sfConfigurationException(sprintf('Invalid configuration settings: [sf_module_disabled_module] "%s", [sf_module_disabled_action] "%s".', $moduleName, $actionName));
  190. }
  191. $this->forward($moduleName, $actionName);
  192. }
  193. }
  194. /**
  195. * Retrieves an sfAction implementation instance.
  196. *
  197. * @param string $moduleName A module name
  198. * @param string $actionName An action name
  199. *
  200. * @return sfAction An sfAction implementation instance, if the action exists, otherwise null
  201. */
  202. public function getAction($moduleName, $actionName)
  203. {
  204. return $this->getController($moduleName, $actionName, 'action');
  205. }
  206. /**
  207. * Retrieves a sfComponent implementation instance.
  208. *
  209. * @param string $moduleName A module name
  210. * @param string $componentName A component name
  211. *
  212. * @return sfComponent A sfComponent implementation instance, if the component exists, otherwise null
  213. */
  214. public function getComponent($moduleName, $componentName)
  215. {
  216. return $this->getController($moduleName, $componentName, 'component');
  217. }
  218. /**
  219. * Retrieves a controller implementation instance.
  220. *
  221. * @param string $moduleName A module name
  222. * @param string $controllerName A component name
  223. * @param string $extension Either 'action' or 'component' depending on the type of controller to look for
  224. *
  225. * @return object A controller implementation instance, if the controller exists, otherwise null
  226. *
  227. * @see getComponent(), getAction()
  228. */
  229. protected function getController($moduleName, $controllerName, $extension)
  230. {
  231. $classSuffix = ucfirst(strtolower($extension));
  232. if (!isset($this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix]))
  233. {
  234. $this->controllerExists($moduleName, $controllerName, $extension, true);
  235. }
  236. $class = $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix];
  237. // fix for same name classes
  238. $moduleClass = $moduleName.'_'.$class;
  239. if (class_exists($moduleClass, false))
  240. {
  241. $class = $moduleClass;
  242. }
  243. return new $class($this->context, $moduleName, $controllerName);
  244. }
  245. /**
  246. * Retrieves the action stack.
  247. *
  248. * @return sfActionStack An sfActionStack instance, if the action stack is enabled, otherwise null
  249. */
  250. public function getActionStack()
  251. {
  252. return $this->context->getActionStack();
  253. }
  254. /**
  255. * Retrieves the presentation rendering mode.
  256. *
  257. * @return int One of the following:
  258. * - sfView::RENDER_CLIENT
  259. * - sfView::RENDER_VAR
  260. */
  261. public function getRenderMode()
  262. {
  263. return $this->renderMode;
  264. }
  265. /**
  266. * Retrieves a sfView implementation instance.
  267. *
  268. * @param string $moduleName A module name
  269. * @param string $actionName An action name
  270. * @param string $viewName A view name
  271. *
  272. * @return sfView A sfView implementation instance, if the view exists, otherwise null
  273. */
  274. public function getView($moduleName, $actionName, $viewName)
  275. {
  276. // user view exists?
  277. $file = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/view/'.$actionName.$viewName.'View.class.php';
  278. if (is_readable($file))
  279. {
  280. require_once($file);
  281. $class = $actionName.$viewName.'View';
  282. // fix for same name classes
  283. $moduleClass = $moduleName.'_'.$class;
  284. if (class_exists($moduleClass, false))
  285. {
  286. $class = $moduleClass;
  287. }
  288. }
  289. else
  290. {
  291. // view class (as configured in module.yml or defined in action)
  292. $class = sfConfig::get('mod_'.strtolower($moduleName).'_view_class', 'sfPHP').'View';
  293. }
  294. return new $class($this->context, $moduleName, $actionName, $viewName);
  295. }
  296. /**
  297. * Returns the rendered view presentation of a given module/action.
  298. *
  299. * @param string $module A module name
  300. * @param string $action An action name
  301. * @param string $viewName A View class name
  302. *
  303. * @return string The generated content
  304. */
  305. public function getPresentationFor($module, $action, $viewName = null)
  306. {
  307. if (sfConfig::get('sf_logging_enabled'))
  308. {
  309. $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Get presentation for action "%s/%s" (view class: "%s")', $module, $action, $viewName))));
  310. }
  311. // get original render mode
  312. $renderMode = $this->getRenderMode();
  313. // set render mode to var
  314. $this->setRenderMode(sfView::RENDER_VAR);
  315. // grab the action stack
  316. $actionStack = $this->getActionStack();
  317. // grab this next forward's action stack index
  318. $index = $actionStack->getSize();
  319. // set viewName if needed
  320. if ($viewName)
  321. {
  322. $currentViewName = sfConfig::get('mod_'.strtolower($module).'_view_class');
  323. sfConfig::set('mod_'.strtolower($module).'_view_class', $viewName);
  324. }
  325. try
  326. {
  327. // forward to the mail action
  328. $this->forward($module, $action);
  329. }
  330. catch (Exception $e)
  331. {
  332. // put render mode back
  333. $this->setRenderMode($renderMode);
  334. // remove viewName
  335. if ($viewName)
  336. {
  337. sfConfig::set('mod_'.strtolower($module).'_view_class', $currentViewName);
  338. }
  339. throw $e;
  340. }
  341. // grab the action entry from this forward
  342. $actionEntry = $actionStack->getEntry($index);
  343. // get raw email content
  344. $presentation =& $actionEntry->getPresentation();
  345. // put render mode back
  346. $this->setRenderMode($renderMode);
  347. // remove the action entry
  348. $nb = $actionStack->getSize() - $index;
  349. while ($nb-- > 0)
  350. {
  351. $actionEntry = $actionStack->popEntry();
  352. if ($actionEntry->getModuleName() == sfConfig::get('sf_login_module') && $actionEntry->getActionName() == sfConfig::get('sf_login_action'))
  353. {
  354. throw new sfException('Your action is secured, but the user is not authenticated.');
  355. }
  356. else if ($actionEntry->getModuleName() == sfConfig::get('sf_secure_module') && $actionEntry->getActionName() == sfConfig::get('sf_secure_action'))
  357. {
  358. throw new sfException('Your action is secured, but the user does not have access.');
  359. }
  360. }
  361. // remove viewName
  362. if ($viewName)
  363. {
  364. sfConfig::set('mod_'.strtolower($module).'_view_class', $currentViewName);
  365. }
  366. return $presentation;
  367. }
  368. /**
  369. * Sets the presentation rendering mode.
  370. *
  371. * @param int $mode A rendering mode one of the following:
  372. * - sfView::RENDER_CLIENT
  373. * - sfView::RENDER_VAR
  374. * - sfView::RENDER_NONE
  375. *
  376. * @return true
  377. *
  378. * @throws sfRenderException If an invalid render mode has been set
  379. */
  380. public function setRenderMode($mode)
  381. {
  382. if ($mode == sfView::RENDER_CLIENT || $mode == sfView::RENDER_VAR || $mode == sfView::RENDER_NONE)
  383. {
  384. $this->renderMode = $mode;
  385. return;
  386. }
  387. // invalid rendering mode type
  388. throw new sfRenderException(sprintf('Invalid rendering mode: %s.', $mode));
  389. }
  390. /**
  391. * Indicates whether or not we were called using the CLI version of PHP.
  392. *
  393. * @return bool true, if using cli, otherwise false.
  394. */
  395. public function inCLI()
  396. {
  397. return 0 == strncasecmp(PHP_SAPI, 'cli', 3);
  398. }
  399. /**
  400. * Calls methods defined via sfEventDispatcher.
  401. *
  402. * @param string $method The method name
  403. * @param array $arguments The method arguments
  404. *
  405. * @return mixed The returned value of the called method
  406. */
  407. public function __call($method, $arguments)
  408. {
  409. $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'controller.method_not_found', array('method' => $method, 'arguments' => $arguments)));
  410. if (!$event->isProcessed())
  411. {
  412. throw new sfException(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
  413. }
  414. return $event->getReturnValue();
  415. }
  416. }

Debug toolbar