PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/controller/sfController.class.php

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