Dispatcher.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *
00017 *  This script is distributed in the hope that it will be useful,
00018 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 *  GNU General Public License for more details.
00021 *
00022 *  This copyright notice MUST APPEAR in all copies of the script!
00023 ***************************************************************/
00024 
00025 /**
00026  * Creates a request an dispatches it to the controller which was specified
00027  * by TS Setup, Flexform and returns the content to the v4 framework.
00028  *
00029  * This class is the main entry point for extbase extensions.
00030  *
00031  * @package Extbase
00032  * @version $ID:$
00033  */
00034 class Tx_Extbase_Dispatcher {
00035 
00036     /**
00037      * Back reference to the parent content object
00038      * This has to be public as it is set directly from TYPO3
00039      *
00040      * @var tslib_cObj
00041      */
00042     public $cObj;
00043 
00044     /**
00045      * @var Tx_Extbase_Utility_ClassLoader
00046      */
00047     protected $classLoader;
00048 
00049     /**
00050      * @var Tx_Extbase_Configuration_AbstractConfigurationManager
00051      */
00052     protected static $configurationManager;
00053 
00054     /**
00055      * @var t3lib_cache_Manager
00056      */
00057     protected $cacheManager;
00058 
00059     /**
00060      * @var Tx_Extbase_Reflection_Service
00061      */
00062     protected static $reflectionService;
00063 
00064     /**
00065      * @var Tx_Extbase_Persistence_Manager
00066      */
00067     protected static $persistenceManager;
00068 
00069     /**
00070      * The configuration for the Extbase framework
00071      * @var array
00072      */
00073     protected static $extbaseFrameworkConfiguration;
00074 
00075 
00076     /**
00077      * Constructs this Dispatcher and registers the autoloader
00078      */
00079     public function __construct() {
00080         t3lib_cache::initializeCachingFramework();
00081         $this->initializeClassLoader();
00082         $this->initializeCache();
00083         $this->initializeReflection();
00084     }
00085 
00086     /**
00087      * Creates a request an dispatches it to a controller.
00088      *
00089      * @param string $content The content
00090      * @param array $configuration The TS configuration array
00091      * @return string $content The processed content
00092      */
00093     public function dispatch($content, $configuration) {
00094         // FIXME Remove the next lines. These are only there to generate the ext_autoload.php file
00095         //$extutil = new Tx_Extbase_Utility_Extension;
00096         //$extutil->createAutoloadRegistryForExtension('extbase', t3lib_extMgm::extPath('extbase'));
00097         //$extutil->createAutoloadRegistryForExtension('fluid', t3lib_extMgm::extPath('fluid'));
00098 
00099         $this->timeTrackPush('Extbase is called.','');
00100         $this->timeTrackPush('Extbase gets initialized.','');
00101 
00102         if (!is_array($configuration)) {
00103             t3lib_div::sysLog('Extbase was not able to dispatch the request. No configuration.', 'extbase', t3lib_div::SYSLOG_SEVERITY_ERROR);
00104             return $content;
00105         }
00106 
00107         $this->initializeConfigurationManagerAndFrameworkConfiguration($configuration);
00108 
00109         $requestBuilder = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_RequestBuilder');
00110         $request = $requestBuilder->initialize(self::$extbaseFrameworkConfiguration);
00111         $request = $requestBuilder->build();
00112         if (isset($this->cObj->data) && is_array($this->cObj->data)) {
00113             // we need to check the above conditions as cObj is not available in Backend.
00114             $request->setContentObjectData($this->cObj->data);
00115             $request->setIsCached($this->cObj->getUserObjectType() == tslib_cObj::OBJECTTYPE_USER);
00116         }
00117         $response = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_Response');
00118 
00119         // Request hash service
00120         $requestHashService = t3lib_div::makeInstance('Tx_Extbase_Security_Channel_RequestHashService'); // singleton
00121         $requestHashService->verifyRequest($request);
00122 
00123         $persistenceManager = self::getPersistenceManager();
00124 
00125         $this->timeTrackPull();
00126 
00127         $this->timeTrackPush('Extbase dispatches request.','');
00128         $dispatchLoopCount = 0;
00129         while (!$request->isDispatched()) {
00130             if ($dispatchLoopCount++ > 99) throw new Tx_Extbase_MVC_Exception_InfiniteLoop('Could not ultimately dispatch the request after '  . $dispatchLoopCount . ' iterations.', 1217839467);
00131             $controller = $this->getPreparedController($request);
00132             try {
00133                 $controller->processRequest($request, $response);
00134             } catch (Tx_Extbase_MVC_Exception_StopAction $ignoredException) {
00135             }
00136         }
00137         $this->timeTrackPull();
00138 
00139         $this->timeTrackPush('Extbase persists all changes.','');
00140         $flashMessages = t3lib_div::makeInstance('Tx_Extbase_MVC_Controller_FlashMessages'); // singleton
00141         $flashMessages->persist();
00142         $persistenceManager->persistAll();
00143         $this->timeTrackPull();
00144 
00145         self::$reflectionService->shutdown();
00146 
00147         if (count($response->getAdditionalHeaderData()) > 0) {
00148             $GLOBALS['TSFE']->additionalHeaderData[$request->getControllerExtensionName()] = implode("\n", $response->getAdditionalHeaderData());
00149         }
00150         $response->sendHeaders();
00151         $this->timeTrackPull();
00152         return $response->getContent();
00153     }
00154 
00155     /**
00156      * Initializes the autoload mechanism of Extbase. This is supplement to the core autoloader.
00157      *
00158      * @return void
00159      */
00160     protected function initializeClassLoader() {
00161         if (!class_exists('Tx_Extbase_Utility_ClassLoader')) {
00162             require(t3lib_extmgm::extPath('extbase') . 'Classes/Utility/ClassLoader.php');
00163         }
00164 
00165         $classLoader = new Tx_Extbase_Utility_ClassLoader();
00166         spl_autoload_register(array($classLoader, 'loadClass'));
00167     }
00168 
00169     /**
00170      * Initializes the configuration manager and the Extbase settings
00171      *
00172      * @param $configuration The current incoming configuration
00173      * @return void
00174      */
00175     protected function initializeConfigurationManagerAndFrameworkConfiguration($configuration) {
00176         if (TYPO3_MODE === 'FE') {
00177             self::$configurationManager = t3lib_div::makeInstance('Tx_Extbase_Configuration_FrontendConfigurationManager');
00178             self::$configurationManager->setContentObject($this->cObj);
00179         } else {
00180             self::$configurationManager = t3lib_div::makeInstance('Tx_Extbase_Configuration_BackendConfigurationManager');
00181         }
00182         self::$extbaseFrameworkConfiguration = self::$configurationManager->getFrameworkConfiguration($configuration);
00183     }
00184 
00185     /**
00186      * Initializes the cache framework
00187      *
00188      * @return void
00189      */
00190     protected function initializeCache() {
00191         $this->cacheManager = $GLOBALS['typo3CacheManager'];
00192         try {
00193             $this->cacheManager->getCache('cache_extbase_reflection');
00194         } catch (t3lib_cache_exception_NoSuchCache $exception) {
00195             $GLOBALS['typo3CacheFactory']->create(
00196                 'cache_extbase_reflection',
00197                 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_extbase_reflection']['frontend'],
00198                 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_extbase_reflection']['backend'],
00199                 $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['cache_extbase_reflection']['options']
00200             );
00201         }
00202     }
00203 
00204     /**
00205      * Initializes the Reflection Service
00206      *
00207      * @return void
00208      */
00209     protected function initializeReflection() {
00210         self::$reflectionService = t3lib_div::makeInstance('Tx_Extbase_Reflection_Service');
00211         self::$reflectionService->setCache($this->cacheManager->getCache('cache_extbase_reflection'));
00212         if (!self::$reflectionService->isInitialized()) {
00213             self::$reflectionService->initialize();
00214         }
00215     }
00216 
00217     /**
00218      * Builds and returns a controller
00219      *
00220      * @param Tx_Extbase_MVC_Web_Request $request
00221      * @return Tx_Extbase_MVC_Controller_ControllerInterface The prepared controller
00222      */
00223     protected function getPreparedController(Tx_Extbase_MVC_Web_Request $request) {
00224         $controllerObjectName = $request->getControllerObjectName();
00225         $controller = t3lib_div::makeInstance($controllerObjectName);
00226         if (!$controller instanceof Tx_Extbase_MVC_Controller_ControllerInterface) {
00227             throw new Tx_Extbase_MVC_Exception_InvalidController('Invalid controller "' . $request->getControllerObjectName() . '". The controller must implement the Tx_Extbase_MVC_Controller_ControllerInterface.', 1202921619);
00228         }
00229         $propertyMapper = t3lib_div::makeInstance('Tx_Extbase_Property_Mapper');
00230         $propertyMapper->injectReflectionService(self::$reflectionService);
00231         $controller->injectPropertyMapper($propertyMapper);
00232 
00233         $controller->injectSettings(is_array(self::$extbaseFrameworkConfiguration['settings']) ? self::$extbaseFrameworkConfiguration['settings'] : array());
00234 
00235         $flashMessageContainer = t3lib_div::makeInstance('Tx_Extbase_MVC_Controller_FlashMessages'); // singleton
00236         $flashMessageContainer->reset();
00237         $controller->injectFlashMessageContainer($flashMessageContainer);
00238 
00239         $objectManager = t3lib_div::makeInstance('Tx_Extbase_Object_Manager');
00240         $validatorResolver = t3lib_div::makeInstance('Tx_Extbase_Validation_ValidatorResolver');
00241         $validatorResolver->injectObjectManager($objectManager);
00242         $validatorResolver->injectReflectionService(self::$reflectionService);
00243         $controller->injectValidatorResolver($validatorResolver);
00244         $controller->injectReflectionService(self::$reflectionService);
00245         $controller->injectObjectManager($objectManager);
00246         return $controller;
00247     }
00248 
00249     /**
00250      * This function prepares and returns the Persistance Manager
00251      *
00252      * @return Tx_Extbase_Persistence_Manager A (singleton) instance of the Persistence Manager
00253      */
00254     public static function getPersistenceManager() {
00255         if (self::$persistenceManager === NULL) {
00256             $identityMap = t3lib_div::makeInstance('Tx_Extbase_Persistence_IdentityMap');
00257             $persistenceSession = t3lib_div::makeInstance('Tx_Extbase_Persistence_Session'); // singleton
00258 
00259             $dataMapFactory = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_DataMapFactory');
00260             $dataMapFactory->injectReflectionService(self::$reflectionService);
00261 
00262             $dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_DataMapper'); // singleton
00263             $dataMapper->injectIdentityMap($identityMap);
00264             $dataMapper->injectSession($persistenceSession);
00265             $dataMapper->injectReflectionService(self::$reflectionService);
00266             $dataMapper->injectDataMapFactory($dataMapFactory);
00267 
00268             $storageBackend = t3lib_div::makeInstance('Tx_Extbase_Persistence_Storage_Typo3DbBackend', $GLOBALS['TYPO3_DB']); // singleton
00269             $storageBackend->injectDataMapper($dataMapper);
00270 
00271             $qomFactory = t3lib_div::makeInstance('Tx_Extbase_Persistence_QOM_QueryObjectModelFactory', $storageBackend);
00272 
00273             $dataMapper->setQomFactory($qomFactory);
00274 
00275             $persistenceBackend = t3lib_div::makeInstance('Tx_Extbase_Persistence_Backend', $persistenceSession, $storageBackend); // singleton
00276             $persistenceBackend->injectDataMapper($dataMapper);
00277             $persistenceBackend->injectIdentityMap($identityMap);
00278             $persistenceBackend->injectReflectionService(self::$reflectionService);
00279             $persistenceBackend->injectQueryFactory(t3lib_div::makeInstance('Tx_Extbase_Persistence_QueryFactory'));
00280             $persistenceBackend->injectQomFactory($qomFactory);
00281 
00282             $objectManager = t3lib_div::makeInstance('Tx_Extbase_Object_Manager'); // singleton
00283 
00284             $persistenceManager = t3lib_div::makeInstance('Tx_Extbase_Persistence_Manager'); // singleton
00285             $persistenceManager->injectBackend($persistenceBackend);
00286             $persistenceManager->injectSession($persistenceSession);
00287             $persistenceManager->injectObjectManager($objectManager);
00288 
00289             self::$persistenceManager = $persistenceManager;
00290         }
00291 
00292         return self::$persistenceManager;
00293     }
00294 
00295     /**
00296      * This function returns the Configuration Manager. It is instanciated for
00297      * each call to dispatch() only once.
00298      *
00299      * @return Tx_Extbase_Configuration_Manager An instance of the Configuration Manager
00300      */
00301     public static function getConfigurationManager() {
00302         return self::$configurationManager;
00303     }
00304 
00305     /**
00306      * This function returns the settings of Extbase
00307      *
00308      * @return array The settings
00309      */
00310     public static function getExtbaseFrameworkConfiguration() {
00311         return self::$extbaseFrameworkConfiguration;
00312     }
00313 
00314     /**
00315      * Calls an Extbase Backend module.
00316      *
00317      * @param string $module The name of the module
00318      * @return void
00319      */
00320     public function callModule($module) {
00321         if (isset($GLOBALS['TBE_MODULES']['_configuration'][$module])) {
00322             $config = $GLOBALS['TBE_MODULES']['_configuration'][$module];
00323 
00324             // Check permissions and exit if the user has no permission for entry
00325             $GLOBALS['BE_USER']->modAccess($config, TRUE);
00326             if (t3lib_div::_GP('id')) {
00327                 // Check page access
00328                 $id = t3lib_div::_GP('id');
00329                 $permClause = $GLOBALS['BE_USER']->getPagePermsClause(TRUE);
00330                 $access = is_array(t3lib_BEfunc::readPageAccess($id, $permClause));
00331                 if (!$access) {
00332                     t3lib_BEfunc::typo3PrintError('No Access', 'You don\'t have access to this page', 0);
00333                 }
00334             }
00335 
00336             // Resolve the controller/action to use
00337             $controllerAction = $this->resolveControllerAction($module);
00338 
00339             // As for SCbase modules, output of the controller/action pair should be echoed
00340             echo $this->transfer($module, $controllerAction['controllerName'], $controllerAction['actionName']);
00341             return TRUE;
00342         } else {
00343             return FALSE;
00344         }
00345     }
00346 
00347     /**
00348      * Resolves the controller and action to use for current call.
00349      * This takes into account any function menu that has being called.
00350      *
00351      * @param string $module The name of the module
00352      * @return array The controller/action pair to use for current call
00353      */
00354     protected function resolveControllerAction($module) {
00355         $configuration = $GLOBALS['TBE_MODULES']['_configuration'][$module];
00356         $fallbackControllerAction = $this->getFallbackControllerAction($configuration);
00357 
00358             // Extract dispatcher settings from request
00359         $argumentPrefix = strtolower('tx_' . $configuration['extensionName'] . '_' . $configuration['name']);
00360         $dispatcherParameters = t3lib_div::_GPmerged($argumentPrefix);
00361         $dispatcherControllerAction = $this->getDispatcherControllerAction($configuration, $dispatcherParameters);
00362 
00363             // Extract module function settings from request
00364         $moduleFunctionControllerAction = $this->getModuleFunctionControllerAction($module, $fallbackControllerAction['controllerName']);
00365 
00366             // Dispatcher controller/action has precedence over default controller/action
00367         $controllerAction = t3lib_div::array_merge_recursive_overrule($fallbackControllerAction, $dispatcherControllerAction, FALSE, FALSE);
00368             // Module function controller/action has precedence
00369         $controllerAction = t3lib_div::array_merge_recursive_overrule($controllerAction, $moduleFunctionControllerAction, FALSE, FALSE);
00370 
00371         return $controllerAction;
00372     }
00373 
00374     /**
00375      * Returns the fallback controller/action pair to be used when request does not contain
00376      * any controller/action to be used or the provided parameters are not valid.
00377      *
00378      * @param array $configuration The module configuration
00379      * @return array The controller/action pair
00380      */
00381     protected function getFallbackControllerAction($configuration) {
00382             // Extract module settings from its registration in ext_tables.php
00383         $controllers = array_keys($configuration['controllerActions']);
00384         $defaultController = array_shift($controllers);
00385         $actions = t3lib_div::trimExplode(',', $configuration['controllerActions'][$defaultController], TRUE);
00386         $defaultAction = $actions[0];
00387 
00388         return array(
00389             'controllerName' => $defaultController,
00390             'actionName' => $defaultAction,
00391         );
00392     }
00393 
00394     /**
00395      * Returns the controller/action pair that was specified by the request if it is valid,
00396      * otherwise, will just return a blank controller/action pair meaning the default
00397      * controller/action should be used instead.
00398      *
00399      * @param array $configuration The module configuration
00400      * @param array $dispatcherParameters The dispatcher parameters
00401      * @return array The controller/action pair
00402      */
00403     protected function getDispatcherControllerAction($configuration, $dispatcherParameters) {
00404         $controllerAction = array(
00405             'controllerName' => '',
00406             'actionName' => '',
00407         );
00408 
00409         if (!isset($dispatcherParameters['controllerName'])) {
00410                 // Early return: should use fallback controller/action
00411             return $controllerAction;
00412         }
00413 
00414             // Extract configured controllers from module's registration in ext_tables.php
00415         $controllers = array_keys($configuration['controllerActions']);
00416 
00417         $controller = $dispatcherParameters['controllerName'];
00418         if (in_array($controller, $controllers)) {
00419                 // Update return value as selected controller is valid
00420             $controllerAction['controllerName'] = $controller;
00421             $actions = t3lib_div::trimExplode(',', $configuration['controllerActions'][$controller], TRUE);
00422             if (isset($dispatcherParameters['actionName'])) {
00423                     // Extract configured actions for selected controllers
00424                 $action = $dispatcherParameters['actionName'];
00425                 if (in_array($action, $actions)) {
00426                         // Requested action is valid for selected controller
00427                     $controllerAction['actionName'] = $action;
00428                 } else {
00429                         // Use first action of selected controller as fallback action
00430                     $controllerAction['actionName'] = $actions[0];
00431                 }
00432             } else {
00433                     // Use first action of selected controller as fallback action
00434                 $controllerAction['actionName'] = $actions[0];
00435             }
00436         }
00437 
00438         return $controllerAction;
00439     }
00440 
00441     /**
00442      * Returns the controller/action pair to use if a module function parameter is found
00443      * in the request, otherwise, will just return a blank controller/action pair.
00444      *
00445      * @param string $module The name of the module
00446      * @param string $defaultController The module's default controller
00447      * @return array The controller/action pair
00448      */
00449     protected function getModuleFunctionControllerAction($module, $defaultController) {
00450         $controllerAction = array(
00451             'controllerName' => '',
00452             'actionName' => '',
00453         );
00454 
00455         $set = t3lib_div::_GP('SET');
00456         if (!$set) {
00457                 // Early return
00458             return $controllerAction;
00459         }
00460 
00461         $moduleFunction = $set['function'];
00462         $matches = array();
00463         if (preg_match('/^(.*)->(.*)$/', $moduleFunction, $matches)) {
00464             $controllerAction['controllerName'] = $matches[1];
00465             $controllerAction['actionName'] = $matches[2];
00466         } else {
00467                 // Support for external SCbase module function rendering
00468             $functions = $GLOBALS['TBE_MODULES_EXT']['_configuration'][$module]['MOD_MENU']['function'];
00469             if (isset($functions[$moduleFunction])) {
00470                 $controllerAction['controllerName'] = $defaultController;
00471                 $controllerAction['actionName'] = 'extObj';
00472             }
00473         }
00474 
00475         return $controllerAction;
00476     }
00477 
00478     /**
00479      * Transfers the request to an Extbase backend module, calling
00480      * a given controller/action.
00481      *
00482      * @param string $module The name of the module
00483      * @param string $controller The controller to use
00484      * @param string $action The controller's action to execute
00485      * @return string The module rendered view
00486      */
00487     protected function transfer($module, $controller, $action) {
00488         $config = $GLOBALS['TBE_MODULES']['_configuration'][$module];
00489 
00490         $extbaseConfiguration = array(
00491             'userFunc' => 'tx_extbase_dispatcher->dispatch',
00492             'pluginName' => $module,
00493             'extensionName' => $config['extensionName'],
00494             'controller' => $controller,
00495             'action' => $action,
00496             'switchableControllerActions.' => array(),
00497             'settings' => '< module.tx_' . strtolower($config['extensionName']) . '.settings',
00498             'persistence' => '< module.tx_' . strtolower($config['extensionName']) . '.persistence',
00499             'view' => '< module.tx_' . strtolower($config['extensionName']) . '.view',
00500         );
00501 
00502         $i = 1;
00503         foreach ($config['controllerActions'] as $controller => $actions) {
00504                 // Add an "extObj" action for the default controller to handle external
00505                 // SCbase modules which add function menu entries
00506             if ($i == 1) {
00507                 $actions .= ',extObj';
00508             }
00509             $extbaseConfiguration['switchableControllerActions.'][$i++ . '.'] = array(
00510                 'controller' => $controller,
00511                 'actions' => $actions,
00512             );
00513         }
00514 
00515             // BACK_PATH is the path from the typo3/ directory from within the
00516             // directory containing the controller file. We are using mod.php dispatcher
00517             // and thus we are already within typo3/ because we call typo3/mod.php
00518         $GLOBALS['BACK_PATH'] = '';
00519         return $this->dispatch('', $extbaseConfiguration);
00520     }
00521 
00522     /**
00523      * Push some information to time tracking if in Frontend
00524      *
00525      * @param string $name
00526      * @todo correct variable names
00527      * @return void
00528      */
00529     protected function timeTrackPush($name, $param2) {
00530         if (isset($GLOBALS['TT'])) {
00531             $GLOBALS['TT']->push($name, $param2);
00532         }
00533     }
00534 
00535     /**
00536      * Time track pull
00537      * @todo complete documentation of this method.
00538      */
00539     protected function timeTrackPull() {
00540         if (isset($GLOBALS['TT'])) {
00541             $GLOBALS['TT']->pull();
00542         }
00543     }
00544 
00545 }
00546 ?>

Generated on Sat Sep 4 04:17:18 2010 for TYPO3 API by  doxygen 1.4.7