- Timestamp:
- 01/01/07 19:42:27 (2 years ago)
- Files:
-
- 1 modified
Legend:
- Unmodified
- Added
- Removed
-
branches/david-execution_flow/src/controller/AgaviController.class.php
r1236 r1448 30 30 { 31 31 /** 32 * @var int The number of forward() calls done so far. 33 */ 34 protected $numForwards = 0; 35 36 /** 32 37 * @var int The maximum number of times this Controller will forward(). 33 38 */ 34 39 protected $maxForwards = 20; 35 40 36 /**37 * @var int The render mode, see AgaviView RENDER_* constants.38 */39 protected $renderMode = AgaviView::RENDER_CLIENT;40 41 /**42 * @var AgaviActionStack An ActionStack instance.43 */44 protected $actionStack = null;45 46 41 /** 47 42 * @var AgaviContext An AgaviContext instance. … … 55 50 'global' => array(), 56 51 'action' => array( 57 '*' => null58 ),59 'rendering' => array(60 52 '*' => null 61 53 ), … … 76 68 77 69 /** 78 * @var AgaviResponse The Response instance for this Controller.79 */80 protected $response = null;81 82 /**83 * @var AgaviResponse The Response to be used after redirects are set.84 */85 protected $redirectResponse = null;86 87 /**88 * Retrieve the ActionStack.89 *90 * @return AgaviActionStack the ActionStack instance91 *92 * @author David Zuelke <dz@bitxtender.com>93 * @since 0.11.094 */95 public function getActionStack()96 {97 return $this->actionStack;98 }99 100 /**101 70 * Indicates whether or not a module has a specific action. 102 71 * … … 104 73 * @param string An action name. 105 74 * 106 * @return mixed The actual name of the action (might be auto-resolved) ,107 * or false if no Action related to that name was found.108 * 109 * @author Sean Kerr <skerr@mojavi.org> 110 * @author David Zuelke <dz@bitxtender.com> 111 * @since 0.9.0 112 */ 113 public function actionExists($moduleName, $actionName = null)75 * @return mixed The actual name of the action (might be auto-resolved). 76 * 77 * @throws AgaviControllerException if the action could not be found. 78 * @author Sean Kerr <skerr@mojavi.org> 79 * @author David Zuelke <dz@bitxtender.com> 80 * @since 0.9.0 81 */ 82 public function resolveAction($moduleName, $actionName = null) 114 83 { 115 84 $actionName = str_replace('.', '/', $actionName); … … 123 92 if(is_readable($file)) { 124 93 return $actionName; 125 } else { 126 return false; 127 } 128 } 94 } 95 } 96 throw new AgaviControllerException(sprintf('Action "%s" in Module "%s" could not be found.', $actionName, $moduleName)); 97 } 98 99 public function incNumForwards() 100 { 101 if(++$this->numForwards > $this->maxForwards) { 102 throw new AgaviForwardException('Too many forwards have been detected for this request.'); 103 } 104 } 105 106 /** 107 * Create and initialize new execution container instance. 108 * 109 * @param string The name of the module. 110 * @param string The name of the action. 111 * @param array Optional additional parameters. 112 * 113 * @return AgaviExecutionContainer A new execution container instance, 114 * fully initialized. 115 * 116 * @author David Zuelke <dz@bitxtender.com> 117 * @since 0.11.0 118 */ 119 public function createExecutionContainer($moduleName, $actionName, array $parameters = array()) 120 { 121 $container = new AgaviExecutionContainer(); 122 $container->initialize($this->context, $moduleName, $actionName, $parameters); 123 return $container; 129 124 } 130 125 … … 146 141 $request->setAttribute('matchedRoutes', $this->context->getRouting()->execute(), 'org.agavi.routing'); 147 142 148 if($parameters != null) { 149 $request->setParametersByRef($parameters); 150 } 143 $request->setParameters($parameters); 151 144 152 145 // determine our module and action … … 171 164 } 172 165 166 $container = $this->createExecutionContainer($moduleName, $actionName); 167 173 168 // create a new filter chain 174 169 $fcfi = $this->context->getFactoryInfo('filter_chain'); 175 170 $filterChain = new $fcfi['class'](); 176 $filterChain->initialize($this-> response, $fcfi['parameters']);171 $filterChain->initialize($this->context, $fcfi['parameters']); 177 172 178 173 $this->loadFilters($filterChain, 'global'); … … 182 177 183 178 // go, go, go! 184 $filterChain->execute(); 185 186 if($this->redirectResponse instanceof AgaviResponse) { 187 $this->redirectResponse->append($this->response->export()); 188 $this->redirectResponse->send(); 179 $filterChain->execute($container); 180 181 $container->getResponse()->send(); 182 183 } catch(Exception $e) { 184 if(isset($container) && $container instanceof AgaviExecutionContainer && $container->getResponse() instanceof AgaviResponse) { 185 AgaviException::printStackTrace($e, $this->context, $response); 189 186 } else { 190 $this->response->send(); 191 } 192 193 } catch(Exception $e) { 194 AgaviException::printStackTrace($e, $this->context, $this->getResponse()); 195 } 196 } 197 198 /** 199 * Forward the request to another action. 200 * 201 * @param string A module name. 202 * @param string An action name. 203 * @param array|AgaviParameterHolder Additional parameters which will be 204 * passed to the action. 205 * 206 * @throws <b>AgaviConfigurationException</b> If an invalid configuration 207 * setting has been found. 208 * @throws <b>AgaviForwardException</b> If an error occurs while 209 * forwarding the request. 210 * @throws <b>AgaviInitializationException</b> If the action could not be 211 * initialized. 212 * @throws <b>AgaviSecurityException</b> If the action requires security 213 * but the user implementation is 214 * not of type SecurityUser. 215 * 216 * @author Sean Kerr <skerr@mojavi.org> 217 * @since 0.9.0 218 */ 219 public function forward($moduleName, $actionName = 'Index', $additionalParams = array()) 220 { 221 $request = $this->context->getRequest(); 222 223 $actionName = str_replace('.', '/', $actionName); 224 $actionName = preg_replace('/[^a-z0-9\-_\/]+/i', '', $actionName); 225 $moduleName = preg_replace('/[^a-z0-9\-_]+/i', '', $moduleName); 226 227 if($this->actionStack->getSize() >= $this->maxForwards) { 228 throw new AgaviForwardException('Too many forwards have been detected for this request'); 229 } 230 231 if(!AgaviConfig::get('core.available', false)) { 232 // application is unavailable 233 $request->setAttributes(array( 234 'requested_module' => $moduleName, 235 'requested_action' => $actionName 236 ), 'org.agavi.controller.forwards.unavailable'); 237 $moduleName = AgaviConfig::get('actions.unavailable_module'); 238 $actionName = AgaviConfig::get('actions.unavailable_action'); 239 240 if(!$this->actionExists($moduleName, $actionName)) { 241 // cannot find unavailable module/action 242 $error = 'Invalid configuration settings: actions.unavailable_module "%s", actions.unavailable_action "%s"'; 243 $error = sprintf($error, $moduleName, $actionName); 244 245 throw new AgaviConfigurationException($error); 246 } 247 248 } elseif(!$this->actionExists($moduleName, $actionName)) { 249 // the requested action doesn't exist 250 251 // track the requested module so we have access to the data 252 // in the error 404 page 253 $request->setAttributes(array( 254 'requested_module' => $moduleName, 255 'requested_action' => $actionName 256 ), 'org.agavi.controller.forwards.error_404'); 257 258 // switch to error 404 action 259 $moduleName = AgaviConfig::get('actions.error_404_module'); 260 $actionName = AgaviConfig::get('actions.error_404_action'); 261 262 if(!$this->actionExists($moduleName, $actionName)) { 263 // cannot find unavailable module/action 264 $error = 'Invalid configuration settings: actions.error_404_module "%s", actions.error_404_action "%s"'; 265 $error = sprintf($error, $moduleName, $actionName); 266 267 throw new AgaviConfigurationException($error); 268 } 269 } 270 271 // get the "real" action name, i.e. allow auto-resolving of sub-action IndexActions 272 $actionName = $this->actionExists($moduleName, $actionName); 273 274 // create an instance of the action 275 $actionInstance = $this->getAction($moduleName, $actionName); 276 277 // add a new action stack entry 278 $actionEntry = $this->actionStack->addEntry($moduleName, $actionName, $actionInstance, new AgaviParameterHolder(array_merge($request->getParameters(), $additionalParams instanceof AgaviParameterHolder ? $additionalParams->getParameters() : (array) $additionalParams))); 279 280 // include the module configuration 281 // laoded only once due to the way import() works 282 if(is_readable(AgaviConfig::get('core.module_dir') . '/' . $moduleName . '/config/module.xml')) { 283 AgaviConfigCache::import(AgaviConfig::get('core.module_dir') . '/' . $moduleName . '/config/module.xml', $this->context->getName()); 284 } else { 285 AgaviConfig::set('modules.' . strtolower($moduleName) . '.enabled', true); 286 } 287 288 // save autoloads so we can restore them later 289 $oldAutoloads = Agavi::$autoloads; 290 291 static $moduleAutoloads = array(); 292 if(!isset($moduleAutoloads[$moduleName])) { 293 $moduleAutoloads[$moduleName] = array(); 294 $moduleAutoload = AgaviConfig::get('core.module_dir') . '/' . $moduleName . '/config/autoload.xml'; 295 if(is_readable($moduleAutoload)) { 296 include(AgaviConfigCache::checkConfig($moduleAutoload)); 297 $moduleAutoloads[$moduleName] = Agavi::$autoloads; 298 } 299 } else { 300 Agavi::$autoloads = array_merge($moduleAutoloads[$moduleName], Agavi::$autoloads); 301 } 302 303 if(AgaviConfig::get('modules.' . strtolower($moduleName) . '.enabled')) { 304 // check for a module config.php 305 $moduleConfig = AgaviConfig::get('core.module_dir') . '/' . $moduleName . '/config.php'; 306 if(is_readable($moduleConfig)) { 307 require_once($moduleConfig); 308 } 309 310 // initialize the action 311 $actionInstance->initialize($this->context); 312 313 // create a new response instance for this action 314 $rfi = $this->context->getFactoryInfo('response'); 315 $response = new $rfi['class']; 316 $response->initialize($this->context, $rfi['parameters']); 317 318 // create a new filter chain 319 $fcfi = $this->context->getFactoryInfo('filter_chain'); 320 $filterChain = new $fcfi['class'](); 321 $filterChain->initialize($response, $fcfi['parameters']); 322 323 if(AgaviConfig::get('core.available', false)) { 324 // the application is available so we'll register 325 // global and module filters, otherwise skip them 326 327 // does this action require security? 328 if(AgaviConfig::get('core.use_security', false) && $actionInstance->isSecure()) { 329 // register security filter 330 $filterChain->register($this->filters['security']); 331 } 332 333 // load filters 334 $this->loadFilters($filterChain, 'action'); 335 $this->loadFilters($filterChain, 'action', $moduleName); 336 } 337 338 // register the execution filter 339 $filterChain->register($this->filters['execution']); 340 341 // process the filter chain 342 $filterChain->execute(); 343 344 // clear the global request attribute namespace containing attributes for the View 345 $request->removeAttributeNamespace($request->getDefaultNamespace()); 346 347 if($this->renderMode == AgaviView::RENDER_CLIENT && !$actionEntry->hasNext()) { 348 // add the output for this action to the global one 349 $this->getResponse()->append($response->export()); 350 } 351 352 // restore autoloads 353 Agavi::$autoloads = $oldAutoloads; 354 355 } else { 356 // module is disabled 357 $request->setAttributes(array( 358 'requested_module' => $moduleName, 359 'requested_action' => $actionName 360 ), 'org.agavi.controller.forwards.disabled'); 361 $moduleName = AgaviConfig::get('actions.module_disabled_module'); 362 $actionName = AgaviConfig::get('actions.module_disabled_action'); 363 364 if(!$this->actionExists($moduleName, $actionName)) { 365 // cannot find mod disabled module/action 366 $error = 'Invalid configuration settings: actions.module_disabled_module "%s", actions.module_disabled_action "%s"'; 367 $error = sprintf($error, $moduleName, $actionName); 368 throw new AgaviConfigurationException($error); 369 } 370 371 $this->forward($moduleName, $actionName); 372 } 373 374 if($actionEntry->hasNext()) { 375 $next = $actionEntry->getNext(); 376 $request->setParameters($next['parameters'] instanceof AgaviParameterHolder ? $next['parameters']->getParameters() : $next['parameters']); 377 $this->forward($next['moduleName'], $next['actionName']); 187 AgaviException::printStackTrace($e, $this->context); 188 } 378 189 } 379 190 } … … 391 202 */ 392 203 abstract public function redirect($to); 393 394 /**395 * Retrieve the currently executing Action's name.396 *397 * @return string The currently executing action name, if one is set,398 * otherwise null.399 *400 * @author Sean Kerr <skerr@mojavi.org>401 * @author David Zuelke <dz@bitxtender.com>402 * @since 0.11.0403 */404 public function getActionName()405 {406 // get the last action stack entry407 $actionEntry = $this->actionStack->getLastEntry();408 409 return $actionEntry->getActionName();410 }411 412 /**413 * Retrieve the currently executing Action's module directory.414 *415 * @return string An absolute filesystem path to the directory of the416 * currently executing module if set, otherwise null.417 *418 * @author Sean Kerr <skerr@mojavi.org>419 * @author David Zuelke <dz@bitxtender.com>420 * @since 0.11.0421 */422 public function getModuleDirectory()423 {424 // get the last action stack entry425 $actionEntry = $this->actionStack->getLastEntry();426 427 return AgaviConfig::get('core.module_dir') . '/' . $actionEntry->getModuleName();428 }429 430 /**431 * Retrieve the currently executing Action's module name.432 *433 * @return string The currently executing module name, if one is set,434 * otherwise null.435 *436 * @author Sean Kerr <skerr@mojavi.org>437 * @author David Zuelke <dz@bitxtender.com>438 * @since 0.11.0439 */440 public function getModuleName()441 {442 // get the last action stack entry443 $actionEntry = $this->actionStack->getLastEntry();444 445 return $actionEntry->getModuleName();446 }447 204 448 205 /** … … 510 267 511 268 /** 512 * Retrieve the Response object.513 *514 * @return AgaviResponse The current Response implementation instance.515 *516 * @author David Zuelke <dz@bitxtender.com>517 * @since 0.11.0518 */519 protected final function getResponse()520 {521 return $this->response;522 }523 524 /**525 * Retrieve the presentation rendering mode.526 *527 * @return int One of the following:528 * - AgaviView::RENDER_CLIENT529 * - AgaviView::RENDER_VAR530 *531 * @author Sean Kerr <skerr@mojavi.org>532 * @since 0.9.0533 */534 public function getRenderMode()535 {536 return $this->renderMode;537 }538 539 /**540 269 * Retrieve a View implementation instance. 541 270 * … … 623 352 $this->filters['execution'] = new $effi['class'](); 624 353 $this->filters['execution']->initialize($this->context, $effi['parameters']); 625 354 } 355 356 public function getFilter($which) 357 { 358 return (isset($this->filters[$which]) ? $this->filters[$which] : null); 626 359 } 627 360 … … 630 363 * 631 364 * @param AgaviFilterChain A FilterChain instance. 632 * @param string "global" , "action" or "rendering".365 * @param string "global" or "action". 633 366 * @param string A module name, or "*" for the generic config. 634 367 * … … 703 436 704 437 /** 705 * Set the presentation rendering mode.706 *707 * @param int A rendering mode.708 *709 * @throws <b>AgaviRenderException</b> - If an invalid render mode has710 * been set.711 *712 * @author Sean Kerr <skerr@mojavi.org>713 * @since 0.9.0714 */715 public function setRenderMode($mode)716 {717 if($mode == AgaviView::RENDER_CLIENT || $mode == AgaviView::RENDER_VAR || $mode == AgaviView::RENDER_NONE) {718 $this->renderMode = $mode;719 return;720 }721 722 // invalid rendering mode type723 $error = 'Invalid rendering mode: %s';724 $error = sprintf($error, $mode);725 726 throw new AgaviRenderException($error);727 }728 729 /**730 438 * Execute the shutdown procedure for this controller. 731 439 * … … 760 468 * @param string The output type name. 761 469 * 762 * @return bool Whether or not the operation was successful.763 *764 470 * @throws <b>AgaviConfigurationException</b> If the given output type 765 471 * doesnt exist. … … 771 477 { 772 478 if(isset($this->outputTypes[$outputType])) { 773 if(!$this->getResponse()->isLocked()) { 774 $this->outputType = $outputType; 775 return true; 776 } 777 return false; 479 $this->outputType = $outputType; 778 480 } else { 779 481 throw new AgaviConfigurationException('Output Type "' . $outputType . '" has not been configured.');

