TYPO3 CMS  TYPO3_7-6
ActionController.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
24 
31 {
35  protected $reflectionService;
36 
40  protected $cacheService;
41 
48  protected $view = null;
49 
54  protected $namespacesViewObjectNamePattern = '@vendor\@extension\View\@controller\@action@format';
55 
66 
74  protected $defaultViewObjectName = \TYPO3\CMS\Fluid\View\TemplateView::class;
75 
82  protected $actionMethodName = 'indexAction';
83 
90  protected $errorMethodName = 'errorAction';
91 
97 
104  protected $request;
105 
112  protected $response;
113 
117  public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
118  {
119  $this->reflectionService = $reflectionService;
120  }
121 
125  public function injectCacheService(\TYPO3\CMS\Extbase\Service\CacheService $cacheService)
126  {
127  $this->cacheService = $cacheService;
128  }
129 
134  {
135  $this->mvcPropertyMappingConfigurationService = $mvcPropertyMappingConfigurationService;
136  }
137 
147  public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
148  {
149  if (!$this->canProcessRequest($request)) {
150  throw new \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException(get_class($this) . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes), 1187701131);
151  }
152 
153  if ($response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response && $request instanceof WebRequest) {
154  $response->setRequest($request);
155  }
156  $this->request = $request;
157  $this->request->setDispatched(true);
158  $this->response = $response;
159  $this->uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
160  $this->uriBuilder->setRequest($request);
161  $this->actionMethodName = $this->resolveActionMethodName();
163  $this->initializeActionMethodValidators();
164  $this->mvcPropertyMappingConfigurationService->initializePropertyMappingConfigurationFromRequest($request, $this->arguments);
165  $this->initializeAction();
166  $actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName);
167  if (method_exists($this, $actionInitializationMethodName)) {
168  call_user_func([$this, $actionInitializationMethodName]);
169  }
170  $this->mapRequestArgumentsToControllerArguments();
171  $this->controllerContext = $this->buildControllerContext();
172  $this->view = $this->resolveView();
173  if ($this->view !== null) {
174  $this->initializeView($this->view);
175  }
176  $this->callActionMethod();
177  }
178 
189  protected function initializeActionMethodArguments()
190  {
191  $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->actionMethodName);
192  foreach ($methodParameters as $parameterName => $parameterInfo) {
193  $dataType = null;
194  if (isset($parameterInfo['type'])) {
195  $dataType = $parameterInfo['type'];
196  } elseif ($parameterInfo['array']) {
197  $dataType = 'array';
198  }
199  if ($dataType === null) {
200  throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException('The argument type for parameter $' . $parameterName . ' of method ' . get_class($this) . '->' . $this->actionMethodName . '() could not be detected.', 1253175643);
201  }
202  $defaultValue = isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : null;
203  $this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === false, $defaultValue);
204  }
205  }
206 
217  protected function initializeActionMethodValidators()
218  {
219 
224  $actionMethodParameters = static::getActionMethodParameters($this->objectManager);
225  if (isset($actionMethodParameters[$this->actionMethodName])) {
226  $methodParameters = $actionMethodParameters[$this->actionMethodName];
227  } else {
228  $methodParameters = [];
229  }
230 
235  $parameterValidators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions(get_class($this), $this->actionMethodName, $methodParameters);
237  foreach ($this->arguments as $argument) {
238  $validator = $parameterValidators[$argument->getName()];
239 
240  $baseValidatorConjunction = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
241  if (!empty($baseValidatorConjunction) && $validator instanceof AbstractCompositeValidator) {
242  $validator->addValidator($baseValidatorConjunction);
243  }
244  $argument->setValidator($validator);
245  }
246  }
247 
254  protected function resolveActionMethodName()
255  {
256  $actionMethodName = $this->request->getControllerActionName() . 'Action';
257  if (!method_exists($this, $actionMethodName)) {
258  throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchActionException('An action "' . $actionMethodName . '" does not exist in controller "' . get_class($this) . '".', 1186669086);
259  }
260  return $actionMethodName;
261  }
262 
273  protected function callActionMethod()
274  {
275  $preparedArguments = [];
277  foreach ($this->arguments as $argument) {
278  $preparedArguments[] = $argument->getValue();
279  }
280  $validationResult = $this->arguments->getValidationResults();
281  if (!$validationResult->hasErrors()) {
282  $this->emitBeforeCallActionMethodSignal($preparedArguments);
283  $actionResult = call_user_func_array([$this, $this->actionMethodName], $preparedArguments);
284  } else {
285  $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName);
286  $ignoreValidationAnnotations = [];
287  if (isset($methodTagsValues['ignorevalidation'])) {
288  $ignoreValidationAnnotations = $methodTagsValues['ignorevalidation'];
289  }
290  // if there exist errors which are not ignored with @ignorevalidation => call error method
291  // else => call action method
292  $shouldCallActionMethod = true;
293  foreach ($validationResult->getSubResults() as $argumentName => $subValidationResult) {
294  if (!$subValidationResult->hasErrors()) {
295  continue;
296  }
297  if (array_search('$' . $argumentName, $ignoreValidationAnnotations) !== false) {
298  continue;
299  }
300  $shouldCallActionMethod = false;
301  break;
302  }
303  if ($shouldCallActionMethod) {
304  $this->emitBeforeCallActionMethodSignal($preparedArguments);
305  $actionResult = call_user_func_array([$this, $this->actionMethodName], $preparedArguments);
306  } else {
307  $actionResult = call_user_func([$this, $this->errorMethodName]);
308  }
309  }
310 
311  if ($actionResult === null && $this->view instanceof ViewInterface) {
312  $this->response->appendContent($this->view->render());
313  } elseif (is_string($actionResult) && $actionResult !== '') {
314  $this->response->appendContent($actionResult);
315  } elseif (is_object($actionResult) && method_exists($actionResult, '__toString')) {
316  $this->response->appendContent((string)$actionResult);
317  }
318  }
319 
325  protected function emitBeforeCallActionMethodSignal(array $preparedArguments)
326  {
327  $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeCallActionMethod', [get_class($this), $this->actionMethodName, $preparedArguments]);
328  }
329 
337  protected function resolveView()
338  {
339  $viewObjectName = $this->resolveViewObjectName();
340  if ($viewObjectName !== false) {
342  $view = $this->objectManager->get($viewObjectName);
343  $this->setViewConfiguration($view);
344  if ($view->canRender($this->controllerContext) === false) {
345  unset($view);
346  }
347  }
348  if (!isset($view) && $this->defaultViewObjectName != '') {
350  $view = $this->objectManager->get($this->defaultViewObjectName);
351  $this->setViewConfiguration($view);
352  if ($view->canRender($this->controllerContext) === false) {
353  unset($view);
354  }
355  }
356  if (!isset($view)) {
357  $view = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\View\NotFoundView::class);
358  $view->assign('errorMessage', 'No template was found. View could not be resolved for action "'
359  . $this->request->getControllerActionName() . '" in class "' . $this->request->getControllerObjectName() . '"');
360  }
361  $view->setControllerContext($this->controllerContext);
362  if (method_exists($view, 'injectSettings')) {
363  $view->injectSettings($this->settings);
364  }
365  $view->initializeView();
366  // In TYPO3.Flow, solved through Object Lifecycle methods, we need to call it explicitly
367  $view->assign('settings', $this->settings);
368  // same with settings injection.
369  return $view;
370  }
371 
378  {
379  // Template Path Override
380  $extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(
382  );
383 
384  // set TemplateRootPaths
385  $viewFunctionName = 'setTemplateRootPaths';
386  if (method_exists($view, $viewFunctionName)) {
387  $setting = 'templateRootPaths';
388  $parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
389  // no need to bother if there is nothing to set
390  if ($parameter) {
391  $view->$viewFunctionName($parameter);
392  }
393  }
394 
395  // set LayoutRootPaths
396  $viewFunctionName = 'setLayoutRootPaths';
397  if (method_exists($view, $viewFunctionName)) {
398  $setting = 'layoutRootPaths';
399  $parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
400  // no need to bother if there is nothing to set
401  if ($parameter) {
402  $view->$viewFunctionName($parameter);
403  }
404  }
405 
406  // set PartialRootPaths
407  $viewFunctionName = 'setPartialRootPaths';
408  if (method_exists($view, $viewFunctionName)) {
409  $setting = 'partialRootPaths';
410  $parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
411  // no need to bother if there is nothing to set
412  if ($parameter) {
413  $view->$viewFunctionName($parameter);
414  }
415  }
416  }
417 
428  protected function getViewProperty($extbaseFrameworkConfiguration, $setting)
429  {
430  $values = [];
431  if (
432  !empty($extbaseFrameworkConfiguration['view'][$setting])
433  && is_array($extbaseFrameworkConfiguration['view'][$setting])
434  ) {
435  $values = ArrayUtility::sortArrayWithIntegerKeys($extbaseFrameworkConfiguration['view'][$setting]);
436  $values = array_reverse($values, true);
437  }
438 
439  return $values;
440  }
441 
448  protected function resolveViewObjectName()
449  {
450  $vendorName = $this->request->getControllerVendorName();
451  if ($vendorName === null) {
452  return false;
453  }
454 
455  $possibleViewName = str_replace(
456  [
457  '@vendor',
458  '@extension',
459  '@controller',
460  '@action'
461  ],
462  [
463  $vendorName,
464  $this->request->getControllerExtensionName(),
465  $this->request->getControllerName(),
466  ucfirst($this->request->getControllerActionName())
467  ],
468  $this->namespacesViewObjectNamePattern
469  );
470  $format = $this->request->getFormat();
471  $viewObjectName = str_replace('@format', ucfirst($format), $possibleViewName);
472  if (class_exists($viewObjectName) === false) {
473  $viewObjectName = str_replace('@format', '', $possibleViewName);
474  }
475  if (isset($this->viewFormatToObjectNameMap[$format]) && class_exists($viewObjectName) === false) {
476  $viewObjectName = $this->viewFormatToObjectNameMap[$format];
477  }
478  return class_exists($viewObjectName) ? $viewObjectName : false;
479  }
480 
492  protected function initializeView(ViewInterface $view)
493  {
494  }
495 
505  protected function initializeAction()
506  {
507  }
508 
522  protected function errorAction()
523  {
524  $this->clearCacheOnError();
525  $this->addErrorFlashMessage();
526  $this->forwardToReferringRequest();
527 
528  return $this->getFlattenedValidationErrorMessage();
529  }
530 
537  protected function clearCacheOnError()
538  {
539  $extbaseSettings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
540  if (isset($extbaseSettings['persistence']['enableAutomaticCacheClearing']) && $extbaseSettings['persistence']['enableAutomaticCacheClearing'] === '1') {
541  if (isset($GLOBALS['TSFE'])) {
542  $pageUid = $GLOBALS['TSFE']->id;
543  $this->cacheService->clearPageCache([$pageUid]);
544  }
545  }
546  }
547 
554  protected function addErrorFlashMessage()
555  {
556  $errorFlashMessage = $this->getErrorFlashMessage();
557  if ($errorFlashMessage !== false) {
558  $this->addFlashMessage($errorFlashMessage, '', FlashMessage::ERROR);
559  }
560  }
561 
570  protected function getErrorFlashMessage()
571  {
572  return 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '()';
573  }
574 
583  protected function forwardToReferringRequest()
584  {
585  $referringRequest = $this->request->getReferringRequest();
586  if ($referringRequest !== null) {
587  $originalRequest = clone $this->request;
588  $this->request->setOriginalRequest($originalRequest);
589  $this->request->setOriginalRequestMappingResults($this->arguments->getValidationResults());
590  $this->forward(
591  $referringRequest->getControllerActionName(),
592  $referringRequest->getControllerName(),
593  $referringRequest->getControllerExtensionName(),
594  $referringRequest->getArguments()
595  );
596  }
597  }
598 
607  {
608  $outputMessage = 'Validation failed while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '().' . PHP_EOL;
609  return $outputMessage;
610  }
611 
620  {
621  $reflectionService = $objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class);
622 
623  $result = [];
624 
625  $className = get_called_class();
626  $methodNames = get_class_methods($className);
627  foreach ($methodNames as $methodName) {
628  if (strlen($methodName) > 6 && strpos($methodName, 'Action', strlen($methodName) - 6) !== false) {
629  $result[$methodName] = $reflectionService->getMethodParameters($className, $methodName);
630  }
631  }
632 
633  return $result;
634  }
635 }
injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
injectMvcPropertyMappingConfigurationService(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService)
injectCacheService(\TYPO3\CMS\Extbase\Service\CacheService $cacheService)
processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
getViewProperty($extbaseFrameworkConfiguration, $setting)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
forward($actionName, $controllerName=null, $extensionName=null, array $arguments=null)
addFlashMessage($messageBody, $messageTitle='', $severity=\TYPO3\CMS\Core\Messaging\AbstractMessage::OK, $storeInSession=true)
canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)