1<?php 2 3/* 4 * This file is part of the TYPO3 CMS project. 5 * 6 * It is free software; you can redistribute it and/or modify it under 7 * the terms of the GNU General Public License, either version 2 8 * of the License, or any later version. 9 * 10 * For the full copyright and license information, please read the 11 * LICENSE.txt file that was distributed with this source code. 12 * 13 * The TYPO3 project - inspiring people to share! 14 */ 15 16namespace TYPO3\CMS\Extbase\Mvc; 17 18use TYPO3\CMS\Core\Utility\ClassNamingUtility; 19use TYPO3\CMS\Core\Utility\GeneralUtility; 20use TYPO3\CMS\Extbase\Error\Result; 21use TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException; 22use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentNameException; 23use TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerNameException; 24use TYPO3\CMS\Extbase\Mvc\Exception\InvalidRequestMethodException; 25use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException; 26 27/** 28 * Represents a generic request. 29 */ 30class Request implements RequestInterface 31{ 32 const PATTERN_MATCH_FORMAT = '/^[a-z0-9]{1,5}$/'; 33 34 /** 35 * @var string Key of the plugin which identifies the plugin. It must be a string containing [a-z0-9] 36 */ 37 protected $pluginName = ''; 38 39 /** 40 * @var string Name of the extension which is supposed to handle this request. This is the extension name converted to UpperCamelCase 41 */ 42 protected $controllerExtensionName; 43 44 /** 45 * Subpackage key of the controller which is supposed to handle this request. 46 * 47 * @var string 48 */ 49 protected $controllerSubpackageKey; 50 51 /** 52 * @var string 53 */ 54 protected $controllerObjectName; 55 56 /** 57 * @var string Object name of the controller which is supposed to handle this request. 58 */ 59 protected $controllerName = 'Standard'; 60 61 /** 62 * @var string Name of the action the controller is supposed to take. 63 */ 64 protected $controllerActionName = 'index'; 65 66 /** 67 * @var array The arguments for this request 68 */ 69 protected $arguments = []; 70 71 /** 72 * Framework-internal arguments for this request, such as __referrer. 73 * All framework-internal arguments start with double underscore (__), 74 * and are only used from within the framework. Not for user consumption. 75 * Internal Arguments can be objects, in contrast to public arguments 76 * 77 * @var array 78 */ 79 protected $internalArguments = []; 80 81 /** 82 * @var string The requested representation format 83 */ 84 protected $format = 'txt'; 85 86 /** 87 * @var bool If this request has been changed and needs to be dispatched again 88 */ 89 protected $dispatched = false; 90 91 /** 92 * If this request is a forward because of an error, the original request gets filled. 93 * 94 * @var \TYPO3\CMS\Extbase\Mvc\Request 95 */ 96 protected $originalRequest; 97 98 /** 99 * If the request is a forward because of an error, these mapping results get filled here. 100 * 101 * @var \TYPO3\CMS\Extbase\Error\Result 102 */ 103 protected $originalRequestMappingResults; 104 105 /** 106 * @var string Contains the request method 107 */ 108 protected $method = 'GET'; 109 110 /** 111 * @var string 112 */ 113 protected $requestUri; 114 115 /** 116 * @var string The base URI for this request - ie. the host and path leading to the index.php 117 */ 118 protected $baseUri; 119 120 /** 121 * @var bool TRUE if the current request is cached, false otherwise. 122 */ 123 protected $isCached = false; 124 125 /** 126 * Sets the dispatched flag 127 * 128 * @param bool $flag If this request has been dispatched 129 */ 130 public function setDispatched($flag) 131 { 132 $this->dispatched = (bool)$flag; 133 } 134 135 /** 136 * If this request has been dispatched and addressed by the responsible 137 * controller and the response is ready to be sent. 138 * 139 * The dispatcher will try to dispatch the request again if it has not been 140 * addressed yet. 141 * 142 * @return bool TRUE if this request has been dispatched successfully 143 */ 144 public function isDispatched() 145 { 146 return $this->dispatched; 147 } 148 149 /** 150 * @param string $controllerClassName 151 */ 152 public function __construct(string $controllerClassName = '') 153 { 154 $this->controllerObjectName = $controllerClassName; 155 } 156 157 /** 158 * @return string 159 */ 160 public function getControllerObjectName(): string 161 { 162 return $this->controllerObjectName; 163 } 164 165 /** 166 * Explicitly sets the object name of the controller 167 * 168 * @param string $controllerObjectName The fully qualified controller object name 169 * @internal only to be used within Extbase, not part of TYPO3 Core API. 170 */ 171 public function setControllerObjectName($controllerObjectName) 172 { 173 $nameParts = ClassNamingUtility::explodeObjectControllerName($controllerObjectName); 174 $this->controllerExtensionName = $nameParts['extensionName']; 175 $this->controllerSubpackageKey = $nameParts['subpackageKey'] ?? null; 176 $this->controllerName = $nameParts['controllerName']; 177 } 178 179 /** 180 * Sets the plugin name. 181 * 182 * @param string|null $pluginName 183 * @internal only to be used within Extbase, not part of TYPO3 Core API. 184 */ 185 public function setPluginName($pluginName = null) 186 { 187 if ($pluginName !== null) { 188 $this->pluginName = $pluginName; 189 } 190 } 191 192 /** 193 * Returns the plugin key. 194 * 195 * @return string The plugin key 196 */ 197 public function getPluginName() 198 { 199 return $this->pluginName; 200 } 201 202 /** 203 * Sets the extension name of the controller. 204 * 205 * @param string $controllerExtensionName The extension name. 206 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidExtensionNameException if the extension name is not valid 207 * @internal only to be used within Extbase, not part of TYPO3 Core API. 208 */ 209 public function setControllerExtensionName($controllerExtensionName) 210 { 211 if ($controllerExtensionName !== null) { 212 $this->controllerExtensionName = $controllerExtensionName; 213 } 214 } 215 216 /** 217 * Returns the extension name of the specified controller. 218 * 219 * @return string The extension name 220 */ 221 public function getControllerExtensionName() 222 { 223 return $this->controllerExtensionName; 224 } 225 226 /** 227 * Returns the extension name of the specified controller. 228 * 229 * @return string The extension key 230 */ 231 public function getControllerExtensionKey() 232 { 233 return GeneralUtility::camelCaseToLowerCaseUnderscored($this->controllerExtensionName); 234 } 235 236 /** 237 * Sets the subpackage key of the controller. 238 * 239 * @param string $subpackageKey The subpackage key. 240 * @internal only to be used within Extbase, not part of TYPO3 Core API. 241 */ 242 public function setControllerSubpackageKey($subpackageKey) 243 { 244 $this->controllerSubpackageKey = $subpackageKey; 245 } 246 247 /** 248 * Returns the subpackage key of the specified controller. 249 * If there is no subpackage key set, the method returns NULL 250 * 251 * @return string The subpackage key 252 * @internal only to be used within Extbase, not part of TYPO3 Core API. 253 */ 254 public function getControllerSubpackageKey() 255 { 256 return $this->controllerSubpackageKey; 257 } 258 259 /** 260 * @var array 261 */ 262 protected $controllerAliasToClassNameMapping = []; 263 264 /** 265 * @param array $controllerAliasToClassNameMapping 266 */ 267 public function setControllerAliasToClassNameMapping(array $controllerAliasToClassNameMapping) 268 { 269 // this is only needed as long as forwarded requests are altered and unless there 270 // is no new request object created by the request builder. 271 $this->controllerAliasToClassNameMapping = $controllerAliasToClassNameMapping; 272 } 273 274 /** 275 * Sets the name of the controller which is supposed to handle the request. 276 * Note: This is not the object name of the controller! 277 * 278 * @param string $controllerName Name of the controller 279 * @throws Exception\InvalidControllerNameException 280 * @internal only to be used within Extbase, not part of TYPO3 Core API. 281 */ 282 public function setControllerName($controllerName) 283 { 284 if (!is_string($controllerName) && $controllerName !== null) { 285 throw new InvalidControllerNameException('The controller name must be a valid string, ' . gettype($controllerName) . ' given.', 1187176358); 286 } 287 if ($controllerName !== null) { 288 $this->controllerName = $controllerName; 289 $this->controllerObjectName = $this->controllerAliasToClassNameMapping[$controllerName] ?? ''; 290 // There might be no Controller Class, for example for Fluid Templates. 291 } 292 } 293 294 /** 295 * Returns the object name of the controller supposed to handle this request, if one 296 * was set already (if not, the name of the default controller is returned) 297 * 298 * @return string Object name of the controller 299 */ 300 public function getControllerName() 301 { 302 return $this->controllerName; 303 } 304 305 /** 306 * Sets the name of the action contained in this request. 307 * 308 * Note that the action name must start with a lower case letter and is case sensitive. 309 * 310 * @param string $actionName Name of the action to execute by the controller 311 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException if the action name is not valid 312 * @internal only to be used within Extbase, not part of TYPO3 Core API. 313 */ 314 public function setControllerActionName($actionName) 315 { 316 if (!is_string($actionName) && $actionName !== null) { 317 throw new InvalidActionNameException('The action name must be a valid string, ' . gettype($actionName) . ' given (' . $actionName . ').', 1187176359); 318 } 319 if ($actionName[0] !== strtolower($actionName[0]) && $actionName !== null) { 320 throw new InvalidActionNameException('The action name must start with a lower case letter, "' . $actionName . '" does not match this criteria.', 1218473352); 321 } 322 if ($actionName !== null) { 323 $this->controllerActionName = $actionName; 324 } 325 } 326 327 /** 328 * Returns the name of the action the controller is supposed to execute. 329 * 330 * @return string Action name 331 */ 332 public function getControllerActionName() 333 { 334 $controllerObjectName = $this->getControllerObjectName(); 335 if ($controllerObjectName !== '' && $this->controllerActionName === strtolower($this->controllerActionName)) { 336 // todo: this is nonsense! We can detect a non existing method in 337 // todo: \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin, if necessary. 338 // todo: At this point, we want to have a getter for a fixed value. 339 $actionMethodName = $this->controllerActionName . 'Action'; 340 $classMethods = get_class_methods($controllerObjectName); 341 if (is_array($classMethods)) { 342 foreach ($classMethods as $existingMethodName) { 343 if (strtolower($existingMethodName) === strtolower($actionMethodName)) { 344 $this->controllerActionName = substr($existingMethodName, 0, -6); 345 break; 346 } 347 } 348 } 349 } 350 return $this->controllerActionName; 351 } 352 353 /** 354 * Sets the value of the specified argument 355 * 356 * @param string $argumentName Name of the argument to set 357 * @param mixed $value The new value 358 * @throws Exception\InvalidArgumentNameException 359 * @internal only to be used within Extbase, not part of TYPO3 Core API. 360 */ 361 public function setArgument($argumentName, $value) 362 { 363 if (!is_string($argumentName) || $argumentName === '') { 364 throw new InvalidArgumentNameException('Invalid argument name.', 1210858767); 365 } 366 if ($argumentName[0] === '_' && $argumentName[1] === '_') { 367 $this->internalArguments[$argumentName] = $value; 368 return; 369 } 370 if (!in_array($argumentName, ['@extension', '@subpackage', '@controller', '@action', '@format'], true)) { 371 $this->arguments[$argumentName] = $value; 372 } 373 } 374 375 /** 376 * Sets the whole arguments array and therefore replaces any arguments 377 * which existed before. 378 * 379 * @param array $arguments An array of argument names and their values 380 * @internal only to be used within Extbase, not part of TYPO3 Core API. 381 */ 382 public function setArguments(array $arguments) 383 { 384 $this->arguments = []; 385 foreach ($arguments as $argumentName => $argumentValue) { 386 $this->setArgument($argumentName, $argumentValue); 387 } 388 } 389 390 /** 391 * Returns an array of arguments and their values 392 * 393 * @return array Associative array of arguments and their values (which may be arguments and values as well) 394 */ 395 public function getArguments() 396 { 397 return $this->arguments; 398 } 399 400 /** 401 * Returns the value of the specified argument 402 * 403 * @param string $argumentName Name of the argument 404 * 405 * @return string|array Value of the argument 406 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException if such an argument does not exist 407 */ 408 public function getArgument($argumentName) 409 { 410 if (!isset($this->arguments[$argumentName])) { 411 throw new NoSuchArgumentException('An argument "' . $argumentName . '" does not exist for this request.', 1176558158); 412 } 413 return $this->arguments[$argumentName]; 414 } 415 416 /** 417 * Checks if an argument of the given name exists (is set) 418 * 419 * @param string $argumentName Name of the argument to check 420 * 421 * @return bool TRUE if the argument is set, otherwise FALSE 422 */ 423 public function hasArgument($argumentName) 424 { 425 return isset($this->arguments[$argumentName]); 426 } 427 428 /** 429 * Sets the requested representation format 430 * 431 * @param string $format The desired format, something like "html", "xml", "png", "json" or the like. Can even be something like "rss.xml". 432 * @internal only to be used within Extbase, not part of TYPO3 Core API. 433 */ 434 public function setFormat($format) 435 { 436 $this->format = $format; 437 } 438 439 /** 440 * Returns the requested representation format 441 * 442 * @return string The desired format, something like "html", "xml", "png", "json" or the like. 443 */ 444 public function getFormat() 445 { 446 return $this->format; 447 } 448 449 /** 450 * Returns the original request. Filled only if a property mapping error occurred. 451 * 452 * @return \TYPO3\CMS\Extbase\Mvc\Request the original request. 453 * @internal only to be used within Extbase, not part of TYPO3 Core API. 454 */ 455 public function getOriginalRequest() 456 { 457 return $this->originalRequest; 458 } 459 460 /** 461 * @param \TYPO3\CMS\Extbase\Mvc\Request $originalRequest 462 * @internal only to be used within Extbase, not part of TYPO3 Core API. 463 */ 464 public function setOriginalRequest(\TYPO3\CMS\Extbase\Mvc\Request $originalRequest) 465 { 466 $this->originalRequest = $originalRequest; 467 } 468 469 /** 470 * Get the request mapping results for the original request. 471 * 472 * @return \TYPO3\CMS\Extbase\Error\Result 473 * @internal only to be used within Extbase, not part of TYPO3 Core API. 474 */ 475 public function getOriginalRequestMappingResults() 476 { 477 if ($this->originalRequestMappingResults === null) { 478 return new Result(); 479 } 480 return $this->originalRequestMappingResults; 481 } 482 483 /** 484 * @param \TYPO3\CMS\Extbase\Error\Result $originalRequestMappingResults 485 * @internal only to be used within Extbase, not part of TYPO3 Core API. 486 */ 487 public function setOriginalRequestMappingResults(Result $originalRequestMappingResults) 488 { 489 $this->originalRequestMappingResults = $originalRequestMappingResults; 490 } 491 492 /** 493 * Get the internal arguments of the request, i.e. every argument starting 494 * with two underscores. 495 * 496 * @return array 497 * @internal only to be used within Extbase, not part of TYPO3 Core API. 498 */ 499 public function getInternalArguments() 500 { 501 return $this->internalArguments; 502 } 503 504 /** 505 * Returns the value of the specified argument 506 * 507 * @param string $argumentName Name of the argument 508 * @return string Value of the argument, or NULL if not set. 509 * @internal only to be used within Extbase, not part of TYPO3 Core API. 510 */ 511 public function getInternalArgument($argumentName) 512 { 513 if (!isset($this->internalArguments[$argumentName])) { 514 return null; 515 } 516 return $this->internalArguments[$argumentName]; 517 } 518 519 /** 520 * Sets the request method 521 * 522 * @param string $method Name of the request method 523 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidRequestMethodException if the request method is not supported 524 * @internal only to be used within Extbase, not part of TYPO3 Core API. 525 */ 526 public function setMethod($method) 527 { 528 if ($method === '' || strtoupper($method) !== $method) { 529 throw new InvalidRequestMethodException('The request method "' . $method . '" is not supported.', 1217778382); 530 } 531 $this->method = $method; 532 } 533 534 /** 535 * Returns the name of the request method 536 * 537 * @return string Name of the request method 538 */ 539 public function getMethod() 540 { 541 return $this->method; 542 } 543 544 /** 545 * Sets the request URI 546 * 547 * @param string $requestUri URI of this web request 548 * @internal only to be used within Extbase, not part of TYPO3 Core API. 549 */ 550 public function setRequestUri($requestUri) 551 { 552 $this->requestUri = $requestUri; 553 } 554 555 /** 556 * Returns the request URI 557 * 558 * @return string URI of this web request 559 */ 560 public function getRequestUri() 561 { 562 return $this->requestUri; 563 } 564 565 /** 566 * Sets the base URI for this request. 567 * 568 * @param string $baseUri New base URI 569 * @internal only to be used within Extbase, not part of TYPO3 Core API. 570 */ 571 public function setBaseUri($baseUri) 572 { 573 $this->baseUri = $baseUri; 574 } 575 576 /** 577 * Returns the base URI 578 * 579 * @return string Base URI of this web request 580 */ 581 public function getBaseUri() 582 { 583 return $this->baseUri; 584 } 585 586 /** 587 * Set if the current request is cached. 588 * 589 * @param bool $isCached 590 * @internal only to be used within Extbase, not part of TYPO3 Core API. 591 */ 592 public function setIsCached($isCached) 593 { 594 $this->isCached = (bool)$isCached; 595 } 596 597 /** 598 * Return whether the current request is a cached request or not. 599 * 600 * @return bool the caching status. 601 * @internal only to be used within Extbase, not part of TYPO3 Core API. 602 */ 603 public function isCached() 604 { 605 return $this->isCached; 606 } 607} 608