TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
extbase/Classes/Mvc/Controller/ActionController.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Extbase\Mvc\Controller;
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 
21 use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
23 
30 {
34  protected $reflectionService;
35 
39  protected $cacheService;
40 
47  protected $view = null;
48 
53  protected $namespacesViewObjectNamePattern = '@vendor\@extension\View\@controller\@action@format';
54 
65 
73  protected $defaultViewObjectName = \TYPO3\CMS\Fluid\View\TemplateView::class;
74 
81  protected $actionMethodName = 'indexAction';
82 
89  protected $errorMethodName = 'errorAction';
90 
96 
103  protected $request;
104 
111  protected $response;
112 
116  public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
117  {
118  $this->reflectionService = $reflectionService;
119  }
120 
124  public function injectCacheService(\TYPO3\CMS\Extbase\Service\CacheService $cacheService)
125  {
126  $this->cacheService = $cacheService;
127  }
128 
133  {
134  $this->mvcPropertyMappingConfigurationService = $mvcPropertyMappingConfigurationService;
135  }
136 
146  public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
147  {
148  if (!$this->canProcessRequest($request)) {
149  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);
150  }
151 
152  if ($response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response && $request instanceof WebRequest) {
153  $response->setRequest($request);
154  }
155  $this->request = $request;
156  $this->request->setDispatched(true);
157  $this->response = $response;
158  $this->uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
159  $this->uriBuilder->setRequest($request);
160  $this->actionMethodName = $this->resolveActionMethodName();
162  $this->initializeActionMethodValidators();
163  $this->mvcPropertyMappingConfigurationService->initializePropertyMappingConfigurationFromRequest($request, $this->arguments);
164  $this->initializeAction();
165  $actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName);
166  if (method_exists($this, $actionInitializationMethodName)) {
167  call_user_func([$this, $actionInitializationMethodName]);
168  }
169  $this->mapRequestArgumentsToControllerArguments();
170  $this->controllerContext = $this->buildControllerContext();
171  $this->view = $this->resolveView();
172  if ($this->view !== null) {
173  $this->initializeView($this->view);
174  }
175  $this->callActionMethod();
176  }
177 
188  protected function initializeActionMethodArguments()
189  {
190  $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->actionMethodName);
191  foreach ($methodParameters as $parameterName => $parameterInfo) {
192  $dataType = null;
193  if (isset($parameterInfo['type'])) {
194  $dataType = $parameterInfo['type'];
195  } elseif ($parameterInfo['array']) {
196  $dataType = 'array';
197  }
198  if ($dataType === null) {
199  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);
200  }
201  $defaultValue = isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : null;
202  $this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === false, $defaultValue);
203  }
204  }
205 
216  protected function initializeActionMethodValidators()
217  {
218 
223  $actionMethodParameters = static::getActionMethodParameters($this->objectManager);
224  if (isset($actionMethodParameters[$this->actionMethodName])) {
225  $methodParameters = $actionMethodParameters[$this->actionMethodName];
226  } else {
227  $methodParameters = [];
228  }
229 
234  $parameterValidators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions(get_class($this), $this->actionMethodName, $methodParameters);
236  foreach ($this->arguments as $argument) {
237  $validator = $parameterValidators[$argument->getName()];
238 
239  $baseValidatorConjunction = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
240  if (!empty($baseValidatorConjunction) && $validator instanceof AbstractCompositeValidator) {
241  $validator->addValidator($baseValidatorConjunction);
242  }
243  $argument->setValidator($validator);
244  }
245  }
246 
253  protected function resolveActionMethodName()
254  {
255  $actionMethodName = $this->request->getControllerActionName() . 'Action';
256  if (!method_exists($this, $actionMethodName)) {
257  throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchActionException('An action "' . $actionMethodName . '" does not exist in controller "' . get_class($this) . '".', 1186669086);
258  }
259  return $actionMethodName;
260  }
261 
272  protected function callActionMethod()
273  {
274  $preparedArguments = [];
276  foreach ($this->arguments as $argument) {
277  $preparedArguments[] = $argument->getValue();
278  }
279  $validationResult = $this->arguments->getValidationResults();
280  if (!$validationResult->hasErrors()) {
281  $this->emitBeforeCallActionMethodSignal($preparedArguments);
282  $actionResult = call_user_func_array([$this, $this->actionMethodName], $preparedArguments);
283  } else {
284  $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName);
285  $ignoreValidationAnnotations = [];
286  if (isset($methodTagsValues['ignorevalidation'])) {
287  $ignoreValidationAnnotations = $methodTagsValues['ignorevalidation'];
288  }
289  // if there exist errors which are not ignored with @ignorevalidation => call error method
290  // else => call action method
291  $shouldCallActionMethod = true;
292  foreach ($validationResult->getSubResults() as $argumentName => $subValidationResult) {
293  if (!$subValidationResult->hasErrors()) {
294  continue;
295  }
296  if (array_search('$' . $argumentName, $ignoreValidationAnnotations) !== false) {
297  continue;
298  }
299  $shouldCallActionMethod = false;
300  break;
301  }
302  if ($shouldCallActionMethod) {
303  $this->emitBeforeCallActionMethodSignal($preparedArguments);
304  $actionResult = call_user_func_array([$this, $this->actionMethodName], $preparedArguments);
305  } else {
306  $actionResult = call_user_func([$this, $this->errorMethodName]);
307  }
308  }
309 
310  if ($actionResult === null && $this->view instanceof ViewInterface) {
311  $this->response->appendContent($this->view->render());
312  } elseif (is_string($actionResult) && $actionResult !== '') {
313  $this->response->appendContent($actionResult);
314  } elseif (is_object($actionResult) && method_exists($actionResult, '__toString')) {
315  $this->response->appendContent((string)$actionResult);
316  }
317  }
318 
324  protected function emitBeforeCallActionMethodSignal(array $preparedArguments)
325  {
326  $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeCallActionMethod', [get_class($this), $this->actionMethodName, $preparedArguments]);
327  }
328 
336  protected function resolveView()
337  {
338  $viewObjectName = $this->resolveViewObjectName();
339  if ($viewObjectName !== false) {
341  $view = $this->objectManager->get($viewObjectName);
342  $this->setViewConfiguration($view);
343  if ($view->canRender($this->controllerContext) === false) {
344  unset($view);
345  }
346  }
347  if (!isset($view) && $this->defaultViewObjectName != '') {
349  $view = $this->objectManager->get($this->defaultViewObjectName);
350  $this->setViewConfiguration($view);
351  if ($view->canRender($this->controllerContext) === false) {
352  unset($view);
353  }
354  }
355  if (!isset($view)) {
356  $view = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\View\NotFoundView::class);
357  $view->assign('errorMessage', 'No template was found. View could not be resolved for action "'
358  . $this->request->getControllerActionName() . '" in class "' . $this->request->getControllerObjectName() . '"');
359  }
360  $view->setControllerContext($this->controllerContext);
361  if (method_exists($view, 'injectSettings')) {
362  $view->injectSettings($this->settings);
363  }
364  $view->initializeView();
365  // In TYPO3.Flow, solved through Object Lifecycle methods, we need to call it explicitly
366  $view->assign('settings', $this->settings);
367  // same with settings injection.
368  return $view;
369  }
370 
376  protected function setViewConfiguration(ViewInterface $view)
377  {
378  // Template Path Override
379  $extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(
381  );
382 
383  // set TemplateRootPaths
384  $viewFunctionName = 'setTemplateRootPaths';
385  if (method_exists($view, $viewFunctionName)) {
386  $setting = 'templateRootPaths';
387  $parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
388  // no need to bother if there is nothing to set
389  if ($parameter) {
390  $view->$viewFunctionName($parameter);
391  }
392  }
393 
394  // set LayoutRootPaths
395  $viewFunctionName = 'setLayoutRootPaths';
396  if (method_exists($view, $viewFunctionName)) {
397  $setting = 'layoutRootPaths';
398  $parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
399  // no need to bother if there is nothing to set
400  if ($parameter) {
401  $view->$viewFunctionName($parameter);
402  }
403  }
404 
405  // set PartialRootPaths
406  $viewFunctionName = 'setPartialRootPaths';
407  if (method_exists($view, $viewFunctionName)) {
408  $setting = 'partialRootPaths';
409  $parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
410  // no need to bother if there is nothing to set
411  if ($parameter) {
412  $view->$viewFunctionName($parameter);
413  }
414  }
415  }
416 
427  protected function getViewProperty($extbaseFrameworkConfiguration, $setting)
428  {
429  $values = [];
430  if (
431  !empty($extbaseFrameworkConfiguration['view'][$setting])
432  && is_array($extbaseFrameworkConfiguration['view'][$setting])
433  ) {
434  $values = $extbaseFrameworkConfiguration['view'][$setting];
435  }
436 
437  return $values;
438  }
439 
446  protected function resolveViewObjectName()
447  {
448  $vendorName = $this->request->getControllerVendorName();
449  if ($vendorName === null) {
450  return false;
451  }
452 
453  $possibleViewName = str_replace(
454  [
455  '@vendor',
456  '@extension',
457  '@controller',
458  '@action'
459  ],
460  [
461  $vendorName,
462  $this->request->getControllerExtensionName(),
463  $this->request->getControllerName(),
464  ucfirst($this->request->getControllerActionName())
465  ],
466  $this->namespacesViewObjectNamePattern
467  );
468  $format = $this->request->getFormat();
469  $viewObjectName = str_replace('@format', ucfirst($format), $possibleViewName);
470  if (class_exists($viewObjectName) === false) {
471  $viewObjectName = str_replace('@format', '', $possibleViewName);
472  }
473  if (isset($this->viewFormatToObjectNameMap[$format]) && class_exists($viewObjectName) === false) {
474  $viewObjectName = $this->viewFormatToObjectNameMap[$format];
475  }
476  return class_exists($viewObjectName) ? $viewObjectName : false;
477  }
478 
490  protected function initializeView(ViewInterface $view)
491  {
492  }
493 
503  protected function initializeAction()
504  {
505  }
506 
520  protected function errorAction()
521  {
522  $this->clearCacheOnError();
523  $this->addErrorFlashMessage();
524  $this->forwardToReferringRequest();
525 
526  return $this->getFlattenedValidationErrorMessage();
527  }
528 
535  protected function clearCacheOnError()
536  {
537  $extbaseSettings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
538  if (isset($extbaseSettings['persistence']['enableAutomaticCacheClearing']) && $extbaseSettings['persistence']['enableAutomaticCacheClearing'] === '1') {
539  if (isset($GLOBALS['TSFE'])) {
540  $pageUid = $GLOBALS['TSFE']->id;
541  $this->cacheService->clearPageCache([$pageUid]);
542  }
543  }
544  }
545 
552  protected function addErrorFlashMessage()
553  {
554  $errorFlashMessage = $this->getErrorFlashMessage();
555  if ($errorFlashMessage !== false) {
556  $this->addFlashMessage($errorFlashMessage, '', FlashMessage::ERROR);
557  }
558  }
559 
568  protected function getErrorFlashMessage()
569  {
570  return 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '()';
571  }
572 
581  protected function forwardToReferringRequest()
582  {
583  $referringRequest = $this->request->getReferringRequest();
584  if ($referringRequest !== null) {
585  $originalRequest = clone $this->request;
586  $this->request->setOriginalRequest($originalRequest);
587  $this->request->setOriginalRequestMappingResults($this->arguments->getValidationResults());
588  $this->forward(
589  $referringRequest->getControllerActionName(),
590  $referringRequest->getControllerName(),
591  $referringRequest->getControllerExtensionName(),
592  $referringRequest->getArguments()
593  );
594  }
595  }
596 
605  {
606  $outputMessage = 'Validation failed while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '().' . PHP_EOL;
607  return $outputMessage;
608  }
609 
618  {
619  $reflectionService = $objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class);
620 
621  $result = [];
622 
623  $className = get_called_class();
624  $methodNames = get_class_methods($className);
625  foreach ($methodNames as $methodName) {
626  if (strlen($methodName) > 6 && strpos($methodName, 'Action', strlen($methodName) - 6) !== false) {
627  $result[$methodName] = $reflectionService->getMethodParameters($className, $methodName);
628  }
629  }
630 
631  return $result;
632  }
633 }
processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request,\TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
forward($actionName, $controllerName=null, $extensionName=null, array $arguments=null)
injectCacheService(\TYPO3\CMS\Extbase\Service\CacheService $cacheService)
injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
injectMvcPropertyMappingConfigurationService(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
addFlashMessage($messageBody, $messageTitle= '', $severity=\TYPO3\CMS\Core\Messaging\AbstractMessage::OK, $storeInSession=true)