123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- <?php
- /*
- * This file is part of the symfony package.
- * (c) 2004-2006 Fabien Potencier <fabien.potencier@symfony-project.com>
- * (c) 2004-2006 Sean Kerr <sean@code-box.org>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- /**
- * sfController directs application flow.
- *
- * @package symfony
- * @subpackage controller
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @author Sean Kerr <sean@code-box.org>
- * @version SVN: $Id: sfController.class.php 11286 2008-09-02 10:27:36Z fabien $
- */
- abstract class sfController
- {
- protected
- $context = null,
- $dispatcher = null,
- $controllerClasses = array(),
- $maxForwards = 5,
- $renderMode = sfView::RENDER_CLIENT;
- /**
- * Class constructor.
- *
- * @see initialize()
- */
- public function __construct($context)
- {
- $this->initialize($context);
- }
- /**
- * Initializes this controller.
- *
- * @param sfContext $context A sfContext implementation instance
- */
- public function initialize($context)
- {
- $this->context = $context;
- $this->dispatcher = $context->getEventDispatcher();
- // set max forwards
- $this->maxForwards = sfConfig::get('sf_max_forwards');
- }
- /**
- * Indicates whether or not a module has a specific component.
- *
- * @param string $moduleName A module name
- * @param string $componentName An component name
- *
- * @return bool true, if the component exists, otherwise false
- */
- public function componentExists($moduleName, $componentName)
- {
- return $this->controllerExists($moduleName, $componentName, 'component', false);
- }
- /**
- * Indicates whether or not a module has a specific action.
- *
- * @param string $moduleName A module name
- * @param string $actionName An action name
- *
- * @return bool true, if the action exists, otherwise false
- */
- public function actionExists($moduleName, $actionName)
- {
- return $this->controllerExists($moduleName, $actionName, 'action', false);
- }
- /**
- * Looks for a controller and optionally throw exceptions if existence is required (i.e.
- * in the case of {@link getController()}).
- *
- * @param string $moduleName The name of the module
- * @param string $controllerName The name of the controller within the module
- * @param string $extension Either 'action' or 'component' depending on the type of controller to look for
- * @param boolean $throwExceptions Whether to throw exceptions if the controller doesn't exist
- *
- * @throws sfConfigurationException thrown if the module is not enabled
- * @throws sfControllerException thrown if the controller doesn't exist and the $throwExceptions parameter is set to true
- *
- * @return boolean true if the controller exists, false otherwise
- */
- protected function controllerExists($moduleName, $controllerName, $extension, $throwExceptions)
- {
- $dirs = $this->context->getConfiguration()->getControllerDirs($moduleName);
- foreach ($dirs as $dir => $checkEnabled)
- {
- // plugin module enabled?
- if ($checkEnabled && !in_array($moduleName, sfConfig::get('sf_enabled_modules')) && is_readable($dir))
- {
- throw new sfConfigurationException(sprintf('The module "%s" is not enabled.', $moduleName));
- }
- // one action per file or one file for all actions
- $classFile = strtolower($extension);
- $classSuffix = ucfirst(strtolower($extension));
- $file = $dir.'/'.$controllerName.$classSuffix.'.class.php';
- if (is_readable($file))
- {
- // action class exists
- require_once($file);
- $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix] = $controllerName.$classSuffix;
- return true;
- }
- $module_file = $dir.'/'.$classFile.'s.class.php';
- if (is_readable($module_file))
- {
- // module class exists
- require_once($module_file);
- if (!class_exists($moduleName.$classSuffix.'s', false))
- {
- if ($throwExceptions)
- {
- throw new sfControllerException(sprintf('There is no "%s" class in your action file "%s".', $moduleName.$classSuffix.'s', $module_file));
- }
- return false;
- }
- // action is defined in this class?
- if (!in_array('execute'.ucfirst($controllerName), get_class_methods($moduleName.$classSuffix.'s')))
- {
- if ($throwExceptions)
- {
- throw new sfControllerException(sprintf('There is no "%s" method in your action class "%s".', 'execute'.ucfirst($controllerName), $moduleName.$classSuffix.'s'));
- }
- return false;
- }
- $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix] = $moduleName.$classSuffix.'s';
- return true;
- }
- }
- // send an exception if debug
- if ($throwExceptions && sfConfig::get('sf_debug'))
- {
- $dirs = array_keys($dirs);
- // remove sf_root_dir from dirs
- foreach ($dirs as &$dir)
- {
- $dir = str_replace(sfConfig::get('sf_root_dir'), '%SF_ROOT_DIR%', $dir);
- }
- throw new sfControllerException(sprintf('Controller "%s/%s" does not exist in: %s.', $moduleName, $controllerName, implode(', ', $dirs)));
- }
- return false;
- }
- /**
- * Forwards the request to another action.
- *
- * @param string $moduleName A module name
- * @param string $actionName An action name
- *
- * @throws <b>sfConfigurationException</b> If an invalid configuration setting has been found
- * @throws <b>sfForwardException</b> If an error occurs while forwarding the request
- * @throws <b>sfInitializationException</b> If the action could not be initialized
- * @throws <b>sfSecurityException</b> If the action requires security but the user implementation is not of type sfSecurityUser
- */
- public function forward($moduleName, $actionName)
- {
- // replace unwanted characters
- $moduleName = preg_replace('/[^a-z0-9_]+/i', '', $moduleName);
- $actionName = preg_replace('/[^a-z0-9_]+/i', '', $actionName);
- if ($this->getActionStack()->getSize() >= $this->maxForwards)
- {
- // let's kill this party before it turns into cpu cycle hell
- throw new sfForwardException(sprintf('Too many forwards have been detected for this request (> %d).', $this->maxForwards));
- }
- // check for a module generator config file
- $this->context->getConfigCache()->import('modules/'.$moduleName.'/config/generator.yml', false, true);
- if (!$this->actionExists($moduleName, $actionName))
- {
- // the requested action doesn't exist
- if (sfConfig::get('sf_logging_enabled'))
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Action "%s/%s" does not exist', $moduleName, $actionName))));
- }
- throw new sfError404Exception(sprintf('Action "%s/%s" does not exist.', $moduleName, $actionName));
- }
- // create an instance of the action
- $actionInstance = $this->getAction($moduleName, $actionName);
- // add a new action stack entry
- $this->getActionStack()->addEntry($moduleName, $actionName, $actionInstance);
- // include module configuration
- require($this->context->getConfigCache()->checkConfig('modules/'.$moduleName.'/config/module.yml'));
- // check if this module is internal
- if ($this->getActionStack()->getSize() == 1 && sfConfig::get('mod_'.strtolower($moduleName).'_is_internal') && !sfConfig::get('sf_test'))
- {
- throw new sfConfigurationException(sprintf('Action "%s" from module "%s" cannot be called directly.', $actionName, $moduleName));
- }
- // module enabled?
- if (sfConfig::get('mod_'.strtolower($moduleName).'_enabled'))
- {
- // check for a module config.php
- $moduleConfig = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/config/config.php';
- if (is_readable($moduleConfig))
- {
- require_once($moduleConfig);
- }
- // create a new filter chain
- $filterChain = new sfFilterChain();
- $filterChain->loadConfiguration($actionInstance);
- $this->context->getEventDispatcher()->notify(new sfEvent($this, 'controller.change_action', array('module' => $moduleName, 'action' => $actionName)));
- if ($moduleName == sfConfig::get('sf_error_404_module') && $actionName == sfConfig::get('sf_error_404_action'))
- {
- $this->context->getResponse()->setStatusCode(404);
- $this->context->getResponse()->setHttpHeader('Status', '404 Not Found');
- $this->dispatcher->notify(new sfEvent($this, 'controller.page_not_found', array('module' => $moduleName, 'action' => $actionName)));
- }
- // process the filter chain
- $filterChain->execute();
- }
- else
- {
- $moduleName = sfConfig::get('sf_module_disabled_module');
- $actionName = sfConfig::get('sf_module_disabled_action');
- if (!$this->actionExists($moduleName, $actionName))
- {
- // cannot find mod disabled module/action
- throw new sfConfigurationException(sprintf('Invalid configuration settings: [sf_module_disabled_module] "%s", [sf_module_disabled_action] "%s".', $moduleName, $actionName));
- }
- $this->forward($moduleName, $actionName);
- }
- }
- /**
- * Retrieves an sfAction implementation instance.
- *
- * @param string $moduleName A module name
- * @param string $actionname An action name
- *
- * @return sfAction An sfAction implementation instance, if the action exists, otherwise null
- */
- public function getAction($moduleName, $actionName)
- {
- return $this->getController($moduleName, $actionName, 'action');
- }
- /**
- * Retrieves a sfComponent implementation instance.
- *
- * @param string $moduleName A module name
- * @param string $componentName A component name
- *
- * @return sfComponent A sfComponent implementation instance, if the component exists, otherwise null
- */
- public function getComponent($moduleName, $componentName)
- {
- return $this->getController($moduleName, $componentName, 'component');
- }
- /**
- * Retrieves a controller implementation instance.
- *
- * @param string $moduleName A module name
- * @param string $controllerName A component name
- * @param string $extension Either 'action' or 'component' depending on the type of controller to look for
- *
- * @return object A controller implementation instance, if the controller exists, otherwise null
- *
- * @see getComponent(), getAction()
- */
- protected function getController($moduleName, $controllerName, $extension)
- {
- $classSuffix = ucfirst(strtolower($extension));
- if (!isset($this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix]))
- {
- $this->controllerExists($moduleName, $controllerName, $extension, true);
- }
- $class = $this->controllerClasses[$moduleName.'_'.$controllerName.'_'.$classSuffix];
- // fix for same name classes
- $moduleClass = $moduleName.'_'.$class;
- if (class_exists($moduleClass, false))
- {
- $class = $moduleClass;
- }
- return new $class($this->context, $moduleName, $controllerName);
- }
- /**
- * Retrieves the action stack.
- *
- * @return sfActionStack An sfActionStack instance, if the action stack is enabled, otherwise null
- */
- public function getActionStack()
- {
- return $this->context->getActionStack();
- }
- /**
- * Retrieves the presentation rendering mode.
- *
- * @return int One of the following:
- * - sfView::RENDER_CLIENT
- * - sfView::RENDER_VAR
- */
- public function getRenderMode()
- {
- return $this->renderMode;
- }
- /**
- * Retrieves a sfView implementation instance.
- *
- * @param string $moduleName A module name
- * @param string $actionName An action name
- * @param string $viewName A view name
- *
- * @return sfView A sfView implementation instance, if the view exists, otherwise null
- */
- public function getView($moduleName, $actionName, $viewName)
- {
- // user view exists?
- $file = sfConfig::get('sf_app_module_dir').'/'.$moduleName.'/view/'.$actionName.$viewName.'View.class.php';
- if (is_readable($file))
- {
- require_once($file);
- $class = $actionName.$viewName.'View';
- // fix for same name classes
- $moduleClass = $moduleName.'_'.$class;
- if (class_exists($moduleClass, false))
- {
- $class = $moduleClass;
- }
- }
- else
- {
- // view class (as configured in module.yml or defined in action)
- $class = sfConfig::get('mod_'.strtolower($moduleName).'_view_class', 'sfPHP').'View';
- }
- return new $class($this->context, $moduleName, $actionName, $viewName);
- }
- /**
- * [DEPRECATED] Sends and email.
- *
- * This methods calls a module/action with the sfMailView class.
- *
- * @param string $module A module name
- * @param string $action An action name
- *
- * @return string The generated mail content
- *
- * @see sfMailView, getPresentationFor(), sfController
- * @deprecated 1.1
- */
- public function sendEmail($module, $action)
- {
- if (sfConfig::get('sf_logging_enabled'))
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array('sendEmail method is deprecated', 'priority' => sfLogger::ERR)));
- }
- return $this->getPresentationFor($module, $action, 'sfMail');
- }
- /**
- * Returns the rendered view presentation of a given module/action.
- *
- * @param string $module A module name
- * @param string $action An action name
- * @param string $viewName A View class name
- *
- * @return string The generated content
- */
- public function getPresentationFor($module, $action, $viewName = null)
- {
- if (sfConfig::get('sf_logging_enabled'))
- {
- $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('Get presentation for action "%s/%s" (view class: "%s")', $module, $action, $viewName))));
- }
- // get original render mode
- $renderMode = $this->getRenderMode();
- // set render mode to var
- $this->setRenderMode(sfView::RENDER_VAR);
- // grab the action stack
- $actionStack = $this->getActionStack();
- // grab this next forward's action stack index
- $index = $actionStack->getSize();
- // set viewName if needed
- if ($viewName)
- {
- $currentViewName = sfConfig::get('mod_'.strtolower($module).'_view_class');
- sfConfig::set('mod_'.strtolower($module).'_view_class', $viewName);
- }
- try
- {
- // forward to the mail action
- $this->forward($module, $action);
- }
- catch (Exception $e)
- {
- // put render mode back
- $this->setRenderMode($renderMode);
- // remove viewName
- if ($viewName)
- {
- sfConfig::set('mod_'.strtolower($module).'_view_class', $currentViewName);
- }
- throw $e;
- }
- // grab the action entry from this forward
- $actionEntry = $actionStack->getEntry($index);
- // get raw email content
- $presentation =& $actionEntry->getPresentation();
- // put render mode back
- $this->setRenderMode($renderMode);
- // remove the action entry
- $nb = $actionStack->getSize() - $index;
- while ($nb-- > 0)
- {
- $actionEntry = $actionStack->popEntry();
- if ($actionEntry->getModuleName() == sfConfig::get('sf_login_module') && $actionEntry->getActionName() == sfConfig::get('sf_login_action'))
- {
- throw new sfException('Your action is secured, but the user is not authenticated.');
- }
- else if ($actionEntry->getModuleName() == sfConfig::get('sf_secure_module') && $actionEntry->getActionName() == sfConfig::get('sf_secure_action'))
- {
- throw new sfException('Your action is secured, but the user does not have access.');
- }
- }
- // remove viewName
- if ($viewName)
- {
- sfConfig::set('mod_'.strtolower($module).'_view_class', $currentViewName);
- }
- return $presentation;
- }
- /**
- * Sets the presentation rendering mode.
- *
- * @param int $mode A rendering mode
- *
- * @throws sfRenderException If an invalid render mode has been set
- */
- public function setRenderMode($mode)
- {
- if ($mode == sfView::RENDER_CLIENT || $mode == sfView::RENDER_VAR || $mode == sfView::RENDER_NONE)
- {
- $this->renderMode = $mode;
- return;
- }
- // invalid rendering mode type
- throw new sfRenderException(sprintf('Invalid rendering mode: %s.', $mode));
- }
- /**
- * Indicates whether or not we were called using the CLI version of PHP.
- *
- * @return bool true, if using cli, otherwise false.
- */
- public function inCLI()
- {
- return 0 == strncasecmp(PHP_SAPI, 'cli', 3);
- }
- /**
- * Calls methods defined via sfEventDispatcher.
- *
- * @param string $method The method name
- * @param array $arguments The method arguments
- *
- * @return mixed The returned value of the called method
- */
- public function __call($method, $arguments)
- {
- $event = $this->dispatcher->notifyUntil(new sfEvent($this, 'controller.method_not_found', array('method' => $method, 'arguments' => $arguments)));
- if (!$event->isProcessed())
- {
- throw new sfException(sprintf('Call to undefined method %s::%s.', get_class($this), $method));
- }
- return $event->getReturnValue();
- }
- }
|