[BUGFIX] Fix several typos in php comments
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Mvc / Web / Routing / UriBuilder.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Extbase\Mvc\Web\Routing;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException;
20 use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
21 use TYPO3\CMS\Core\Utility\ArrayUtility;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Core\Utility\HttpUtility;
24 use TYPO3\CMS\Extbase\Mvc\Request;
25 use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
26
27 /**
28 * An URI Builder
29 */
30 class UriBuilder
31 {
32 /**
33 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
34 */
35 protected $configurationManager;
36
37 /**
38 * @var \TYPO3\CMS\Extbase\Service\ExtensionService
39 */
40 protected $extensionService;
41
42 /**
43 * An instance of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
44 *
45 * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
46 */
47 protected $contentObject;
48
49 /**
50 * @var Request|null
51 */
52 protected $request;
53
54 /**
55 * @var array
56 */
57 protected $arguments = [];
58
59 /**
60 * Arguments which have been used for building the last URI
61 *
62 * @var array
63 */
64 protected $lastArguments = [];
65
66 /**
67 * @var string
68 */
69 protected $section = '';
70
71 /**
72 * @var bool
73 */
74 protected $createAbsoluteUri = false;
75
76 /**
77 * @var string
78 */
79 protected $absoluteUriScheme;
80
81 /**
82 * @var bool
83 */
84 protected $addQueryString = false;
85
86 /**
87 * @var string
88 */
89 protected $addQueryStringMethod = '';
90
91 /**
92 * @var array
93 */
94 protected $argumentsToBeExcludedFromQueryString = [];
95
96 /**
97 * @var bool
98 */
99 protected $linkAccessRestrictedPages = false;
100
101 /**
102 * @var int|null
103 */
104 protected $targetPageUid;
105
106 /**
107 * @var int
108 */
109 protected $targetPageType = 0;
110
111 /**
112 * @var bool
113 */
114 protected $noCache = false;
115
116 /**
117 * @var string
118 */
119 protected $format = '';
120
121 /**
122 * @var string|null
123 */
124 protected $argumentPrefix;
125
126 /**
127 * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
128 */
129 protected $environmentService;
130
131 /**
132 * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
133 * @internal only to be used within Extbase, not part of TYPO3 Core API.
134 */
135 public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager): void
136 {
137 $this->configurationManager = $configurationManager;
138 }
139
140 /**
141 * @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
142 * @internal only to be used within Extbase, not part of TYPO3 Core API.
143 */
144 public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService): void
145 {
146 $this->extensionService = $extensionService;
147 }
148
149 /**
150 * @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
151 * @internal only to be used within Extbase, not part of TYPO3 Core API.
152 */
153 public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService): void
154 {
155 $this->environmentService = $environmentService;
156 }
157
158 /**
159 * Life-cycle method that is called by the DI container as soon as this object is completely built
160 * @internal only to be used within Extbase, not part of TYPO3 Core API.
161 */
162 public function initializeObject(): void
163 {
164 $this->contentObject = $this->configurationManager->getContentObject();
165 }
166
167 /**
168 * Sets the current request
169 *
170 * @param Request $request
171 * @return static the current UriBuilder to allow method chaining
172 * @internal only to be used within Extbase, not part of TYPO3 Core API.
173 */
174 public function setRequest(Request $request): UriBuilder
175 {
176 $this->request = $request;
177 return $this;
178 }
179
180 /**
181 * @return Request|null
182 * @internal only to be used within Extbase, not part of TYPO3 Core API.
183 */
184 public function getRequest(): ?Request
185 {
186 return $this->request;
187 }
188
189 /**
190 * Additional query parameters.
191 * If you want to "prefix" arguments, you can pass in multidimensional arrays:
192 * array('prefix1' => array('foo' => 'bar')) gets "&prefix1[foo]=bar"
193 *
194 * @param array $arguments
195 * @return static the current UriBuilder to allow method chaining
196 */
197 public function setArguments(array $arguments): UriBuilder
198 {
199 $this->arguments = $arguments;
200 return $this;
201 }
202
203 /**
204 * @return array
205 * @internal
206 */
207 public function getArguments(): array
208 {
209 return $this->arguments;
210 }
211
212 /**
213 * If specified, adds a given HTML anchor to the URI (#...)
214 *
215 * @param string $section
216 * @return static the current UriBuilder to allow method chaining
217 */
218 public function setSection(string $section): UriBuilder
219 {
220 $this->section = $section;
221 return $this;
222 }
223
224 /**
225 * @return string
226 * @internal
227 */
228 public function getSection(): string
229 {
230 return $this->section;
231 }
232
233 /**
234 * Specifies the format of the target (e.g. "html" or "xml")
235 *
236 * @param string $format
237 * @return static the current UriBuilder to allow method chaining
238 */
239 public function setFormat(string $format): UriBuilder
240 {
241 $this->format = $format;
242 return $this;
243 }
244
245 /**
246 * @return string
247 * @internal
248 */
249 public function getFormat(): string
250 {
251 return $this->format;
252 }
253
254 /**
255 * If set, the URI is prepended with the current base URI. Defaults to FALSE.
256 *
257 * @param bool $createAbsoluteUri
258 * @return static the current UriBuilder to allow method chaining
259 */
260 public function setCreateAbsoluteUri(bool $createAbsoluteUri): UriBuilder
261 {
262 $this->createAbsoluteUri = $createAbsoluteUri;
263 return $this;
264 }
265
266 /**
267 * @return bool
268 * @internal
269 */
270 public function getCreateAbsoluteUri(): bool
271 {
272 return $this->createAbsoluteUri;
273 }
274
275 /**
276 * @return string|null
277 * @internal only to be used within Extbase, not part of TYPO3 Core API.
278 */
279 public function getAbsoluteUriScheme(): ?string
280 {
281 return $this->absoluteUriScheme;
282 }
283
284 /**
285 * Sets the scheme that should be used for absolute URIs in FE mode
286 *
287 * @param string $absoluteUriScheme the scheme to be used for absolute URIs
288 * @return static the current UriBuilder to allow method chaining
289 */
290 public function setAbsoluteUriScheme(string $absoluteUriScheme): UriBuilder
291 {
292 $this->absoluteUriScheme = $absoluteUriScheme;
293 return $this;
294 }
295
296 /**
297 * If set, the current query parameters will be merged with $this->arguments. Defaults to FALSE.
298 *
299 * @param bool $addQueryString
300 * @return static the current UriBuilder to allow method chaining
301 * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
302 */
303 public function setAddQueryString(bool $addQueryString): UriBuilder
304 {
305 $this->addQueryString = $addQueryString;
306 return $this;
307 }
308
309 /**
310 * @return bool
311 * @internal
312 */
313 public function getAddQueryString(): bool
314 {
315 return $this->addQueryString;
316 }
317
318 /**
319 * Sets the method to get the addQueryString parameters. Defaults to an empty string
320 * which results in using GeneralUtility::_GET(). Possible values are
321 *
322 * + '' -> uses GeneralUtility::_GET()
323 * + '0' -> uses GeneralUtility::_GET()
324 * + 'GET' -> uses GeneralUtility::_GET()
325 * + '<any>' -> uses parse_str(GeneralUtility::getIndpEnv('QUERY_STRING'))
326 * (<any> refers to literally everything else than previously mentioned values)
327 *
328 * @param string $addQueryStringMethod
329 * @return static the current UriBuilder to allow method chaining
330 * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
331 */
332 public function setAddQueryStringMethod(string $addQueryStringMethod): UriBuilder
333 {
334 if ($addQueryStringMethod === 'POST') {
335 trigger_error('Assigning addQueryStringMethod = POST is not supported anymore since TYPO3 v10.0.', E_USER_WARNING);
336 $addQueryStringMethod = null;
337 } elseif ($addQueryStringMethod === 'GET,POST' || $addQueryStringMethod === 'POST,GET') {
338 trigger_error('Assigning addQueryStringMethod = GET,POST or POST,GET is not supported anymore since TYPO3 v10.0 - falling back to GET.', E_USER_WARNING);
339 $addQueryStringMethod = 'GET';
340 }
341 $this->addQueryStringMethod = $addQueryStringMethod;
342 return $this;
343 }
344
345 /**
346 * @return string
347 * @internal
348 */
349 public function getAddQueryStringMethod(): string
350 {
351 return $this->addQueryStringMethod;
352 }
353
354 /**
355 * A list of arguments to be excluded from the query parameters
356 * Only active if addQueryString is set
357 *
358 * @param array $argumentsToBeExcludedFromQueryString
359 * @return static the current UriBuilder to allow method chaining
360 * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
361 * @see setAddQueryString()
362 */
363 public function setArgumentsToBeExcludedFromQueryString(array $argumentsToBeExcludedFromQueryString): UriBuilder
364 {
365 $this->argumentsToBeExcludedFromQueryString = $argumentsToBeExcludedFromQueryString;
366 return $this;
367 }
368
369 /**
370 * @return array
371 * @internal
372 */
373 public function getArgumentsToBeExcludedFromQueryString(): array
374 {
375 return $this->argumentsToBeExcludedFromQueryString;
376 }
377
378 /**
379 * Specifies the prefix to be used for all arguments.
380 *
381 * @param string $argumentPrefix
382 * @return static the current UriBuilder to allow method chaining
383 */
384 public function setArgumentPrefix(string $argumentPrefix): UriBuilder
385 {
386 $this->argumentPrefix = $argumentPrefix;
387 return $this;
388 }
389
390 /**
391 * @return string|null
392 * @internal only to be used within Extbase, not part of TYPO3 Core API.
393 */
394 public function getArgumentPrefix(): ?string
395 {
396 return $this->argumentPrefix;
397 }
398
399 /**
400 * If set, URIs for pages without access permissions will be created
401 *
402 * @param bool $linkAccessRestrictedPages
403 * @return static the current UriBuilder to allow method chaining
404 */
405 public function setLinkAccessRestrictedPages(bool $linkAccessRestrictedPages): UriBuilder
406 {
407 $this->linkAccessRestrictedPages = $linkAccessRestrictedPages;
408 return $this;
409 }
410
411 /**
412 * @return bool
413 * @internal
414 */
415 public function getLinkAccessRestrictedPages(): bool
416 {
417 return $this->linkAccessRestrictedPages;
418 }
419
420 /**
421 * Uid of the target page
422 *
423 * @param int $targetPageUid
424 * @return static the current UriBuilder to allow method chaining
425 */
426 public function setTargetPageUid(int $targetPageUid): UriBuilder
427 {
428 $this->targetPageUid = $targetPageUid;
429 return $this;
430 }
431
432 /**
433 * returns $this->targetPageUid.
434 *
435 * @return int|null
436 * @internal
437 */
438 public function getTargetPageUid(): ?int
439 {
440 return $this->targetPageUid;
441 }
442
443 /**
444 * Sets the page type of the target URI. Defaults to 0
445 *
446 * @param int $targetPageType
447 * @return static the current UriBuilder to allow method chaining
448 */
449 public function setTargetPageType(int $targetPageType): UriBuilder
450 {
451 $this->targetPageType = $targetPageType;
452 return $this;
453 }
454
455 /**
456 * @return int
457 * @internal only to be used within Extbase, not part of TYPO3 Core API.
458 */
459 public function getTargetPageType(): int
460 {
461 return $this->targetPageType;
462 }
463
464 /**
465 * by default FALSE; if TRUE, &no_cache=1 will be appended to the URI
466 *
467 * @param bool $noCache
468 * @return static the current UriBuilder to allow method chaining
469 */
470 public function setNoCache(bool $noCache): UriBuilder
471 {
472 $this->noCache = $noCache;
473 return $this;
474 }
475
476 /**
477 * @return bool
478 * @internal
479 */
480 public function getNoCache(): bool
481 {
482 return $this->noCache;
483 }
484
485 /**
486 * by default TRUE; if FALSE, no cHash parameter will be appended to the URI
487 * If noCache is set, this setting will be ignored.
488 *
489 * @return static the current UriBuilder to allow method chaining
490 */
491 public function setUseCacheHash(): UriBuilder
492 {
493 trigger_error('Calling UriBuilder->setUseCacheHash() will be removed in TYPO3 v11.0. TYPO3 Core routing is taking care of handling this argument. Simply remove the line to avoid the notice.', E_USER_DEPRECATED);
494 return $this;
495 }
496
497 /**
498 * @return bool
499 * @internal
500 */
501 public function getUseCacheHash(): bool
502 {
503 trigger_error('Calling UriBuilder->getUseCacheHash() will be removed in TYPO3 v11.0. TYPO3 Core routing is taking care of handling this argument. Simply remove the line to avoid the notice.', E_USER_DEPRECATED);
504 return true;
505 }
506
507 /**
508 * Returns the arguments being used for the last URI being built.
509 * This is only set after build() / uriFor() has been called.
510 *
511 * @return array The last arguments
512 * @internal only to be used within Extbase, not part of TYPO3 Core API.
513 */
514 public function getLastArguments(): array
515 {
516 return $this->lastArguments;
517 }
518
519 /**
520 * Resets all UriBuilder options to their default value
521 *
522 * @return static the current UriBuilder to allow method chaining
523 */
524 public function reset(): UriBuilder
525 {
526 $this->arguments = [];
527 $this->section = '';
528 $this->format = '';
529 $this->createAbsoluteUri = false;
530 $this->addQueryString = false;
531 $this->addQueryStringMethod = '';
532 $this->argumentsToBeExcludedFromQueryString = [];
533 $this->linkAccessRestrictedPages = false;
534 $this->targetPageUid = null;
535 $this->targetPageType = 0;
536 $this->noCache = false;
537 $this->argumentPrefix = null;
538 $this->absoluteUriScheme = null;
539 /*
540 * $this->request MUST NOT be reset here because the request is actually a hard dependency and not part
541 * of the internal state of this object.
542 * todo: consider making the request a constructor dependency or get rid of it's usage
543 */
544 return $this;
545 }
546
547 /**
548 * Creates an URI used for linking to an Extbase action.
549 * Works in Frontend and Backend mode of TYPO3.
550 *
551 * @param string|null $actionName Name of the action to be called
552 * @param array|null $controllerArguments Additional query parameters. Will be "namespaced" and merged with $this->arguments.
553 * @param string|null $controllerName Name of the target controller. If not set, current ControllerName is used.
554 * @param string|null $extensionName Name of the target extension, without underscores. If not set, current ExtensionName is used.
555 * @param string|null $pluginName Name of the target plugin. If not set, current PluginName is used.
556 * @return string the rendered URI
557 * @see build()
558 */
559 public function uriFor(
560 ?string $actionName = null,
561 ?array $controllerArguments = null,
562 ?string $controllerName = null,
563 ?string $extensionName = null,
564 ?string $pluginName = null
565 ): string {
566 $controllerArguments = $controllerArguments ?? [];
567
568 if ($actionName !== null) {
569 $controllerArguments['action'] = $actionName;
570 }
571 if ($controllerName !== null) {
572 $controllerArguments['controller'] = $controllerName;
573 } else {
574 $controllerArguments['controller'] = $this->request->getControllerName();
575 }
576 if ($extensionName === null) {
577 $extensionName = $this->request->getControllerExtensionName();
578 }
579 if ($pluginName === null && $this->environmentService->isEnvironmentInFrontendMode()) {
580 $pluginName = $this->extensionService->getPluginNameByAction($extensionName, $controllerArguments['controller'], $controllerArguments['action']);
581 }
582 if ($pluginName === null) {
583 $pluginName = $this->request->getPluginName();
584 }
585 if ($this->environmentService->isEnvironmentInFrontendMode() && $this->configurationManager->isFeatureEnabled('skipDefaultArguments')) {
586 $controllerArguments = $this->removeDefaultControllerAndAction($controllerArguments, $extensionName, $pluginName);
587 }
588 if ($this->targetPageUid === null && $this->environmentService->isEnvironmentInFrontendMode()) {
589 $this->targetPageUid = $this->extensionService->getTargetPidByPlugin($extensionName, $pluginName);
590 }
591 if ($this->format !== '') {
592 $controllerArguments['format'] = $this->format;
593 }
594 if ($this->argumentPrefix !== null) {
595 $prefixedControllerArguments = [$this->argumentPrefix => $controllerArguments];
596 } else {
597 $pluginNamespace = $this->extensionService->getPluginNamespace($extensionName, $pluginName);
598 $prefixedControllerArguments = [$pluginNamespace => $controllerArguments];
599 }
600 ArrayUtility::mergeRecursiveWithOverrule($this->arguments, $prefixedControllerArguments);
601 return $this->build();
602 }
603
604 /**
605 * This removes controller and/or action arguments from given controllerArguments
606 * if they are equal to the default controller/action of the target plugin.
607 * Note: This is only active in FE mode and if feature "skipDefaultArguments" is enabled
608 *
609 * @see \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::isFeatureEnabled()
610 * @param array $controllerArguments the current controller arguments to be modified
611 * @param string $extensionName target extension name
612 * @param string $pluginName target plugin name
613 * @return array
614 */
615 protected function removeDefaultControllerAndAction(array $controllerArguments, string $extensionName, string $pluginName): array
616 {
617 $defaultControllerName = $this->extensionService->getDefaultControllerNameByPlugin($extensionName, $pluginName);
618 if (isset($controllerArguments['action'])) {
619 $defaultActionName = $this->extensionService->getDefaultActionNameByPluginAndController($extensionName, $pluginName, $controllerArguments['controller']);
620 if ($controllerArguments['action'] === $defaultActionName) {
621 unset($controllerArguments['action']);
622 }
623 }
624 if ($controllerArguments['controller'] === $defaultControllerName) {
625 unset($controllerArguments['controller']);
626 }
627 return $controllerArguments;
628 }
629
630 /**
631 * Builds the URI
632 * Depending on the current context this calls buildBackendUri() or buildFrontendUri()
633 *
634 * @return string The URI
635 * @see buildBackendUri()
636 * @see buildFrontendUri()
637 */
638 public function build(): string
639 {
640 if ($this->environmentService->isEnvironmentInBackendMode()) {
641 return $this->buildBackendUri();
642 }
643 return $this->buildFrontendUri();
644 }
645
646 /**
647 * Builds the URI, backend flavour
648 * The resulting URI is relative and starts with "index.php".
649 * The settings pageUid, pageType, noCache & linkAccessRestrictedPages
650 * will be ignored in the backend.
651 *
652 * @return string The URI
653 * @internal only to be used within Extbase, not part of TYPO3 Core API.
654 * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getQueryArguments
655 */
656 public function buildBackendUri(): string
657 {
658 $arguments = [];
659 if ($this->addQueryString === true) {
660 if ($this->addQueryStringMethod === '' || $this->addQueryStringMethod === '0' || $this->addQueryStringMethod === 'GET') {
661 $arguments = GeneralUtility::_GET();
662 } else {
663 // Explode GET vars recursively
664 parse_str(GeneralUtility::getIndpEnv('QUERY_STRING'), $arguments);
665 }
666 foreach ($this->argumentsToBeExcludedFromQueryString as $argumentToBeExcluded) {
667 $argumentArrayToBeExcluded = [];
668 parse_str($argumentToBeExcluded, $argumentArrayToBeExcluded);
669 $arguments = ArrayUtility::arrayDiffAssocRecursive($arguments, $argumentArrayToBeExcluded);
670 }
671 } else {
672 $id = GeneralUtility::_GP('id');
673 $route = GeneralUtility::_GP('route');
674 if ($id !== null) {
675 $arguments['id'] = $id;
676 }
677 if ($route !== null) {
678 $arguments['route'] = $route;
679 }
680 }
681 ArrayUtility::mergeRecursiveWithOverrule($arguments, $this->arguments);
682 $arguments = $this->convertDomainObjectsToIdentityArrays($arguments);
683 $this->lastArguments = $arguments;
684 $routeName = $arguments['route'] ?? null;
685 unset($arguments['route'], $arguments['token']);
686 $backendUriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
687 try {
688 if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
689 $uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
690 } else {
691 $uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
692 }
693 } catch (ResourceNotFoundException $e) {
694 try {
695 if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
696 $uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
697 } else {
698 $uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
699 }
700 } catch (RouteNotFoundException $e) {
701 $uri = '';
702 }
703 }
704 if ($this->section !== '') {
705 $uri .= '#' . $this->section;
706 }
707 return $uri;
708 }
709
710 /**
711 * Builds the URI, frontend flavour
712 *
713 * @return string The URI
714 * @see buildTypolinkConfiguration()
715 * @internal only to be used within Extbase, not part of TYPO3 Core API.
716 */
717 public function buildFrontendUri(): string
718 {
719 $typolinkConfiguration = $this->buildTypolinkConfiguration();
720 if ($this->createAbsoluteUri === true) {
721 $typolinkConfiguration['forceAbsoluteUrl'] = true;
722 if ($this->absoluteUriScheme !== null) {
723 $typolinkConfiguration['forceAbsoluteUrl.']['scheme'] = $this->absoluteUriScheme;
724 }
725 }
726 // Other than stated in the doc block, typoLink_URL does not always return a string
727 // Thus, we explicitly cast to string here.
728 $uri = (string)$this->contentObject->typoLink_URL($typolinkConfiguration);
729 return $uri;
730 }
731
732 /**
733 * Builds a TypoLink configuration array from the current settings
734 *
735 * @return array typolink configuration array
736 * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html
737 */
738 protected function buildTypolinkConfiguration(): array
739 {
740 $typolinkConfiguration = [];
741 $typolinkConfiguration['parameter'] = $this->targetPageUid ?? $GLOBALS['TSFE']->id;
742 if ($this->targetPageType !== 0) {
743 $typolinkConfiguration['parameter'] .= ',' . $this->targetPageType;
744 } elseif ($this->format !== '') {
745 $targetPageType = $this->extensionService->getTargetPageTypeByFormat($this->request->getControllerExtensionName(), $this->format);
746 $typolinkConfiguration['parameter'] .= ',' . $targetPageType;
747 }
748 if (!empty($this->arguments)) {
749 $arguments = $this->convertDomainObjectsToIdentityArrays($this->arguments);
750 $this->lastArguments = $arguments;
751 $typolinkConfiguration['additionalParams'] = HttpUtility::buildQueryString($arguments, '&');
752 }
753 if ($this->addQueryString === true) {
754 $typolinkConfiguration['addQueryString'] = 1;
755 if (!empty($this->argumentsToBeExcludedFromQueryString)) {
756 $typolinkConfiguration['addQueryString.'] = [
757 'exclude' => implode(',', $this->argumentsToBeExcludedFromQueryString)
758 ];
759 }
760 if ($this->addQueryStringMethod !== '') {
761 $typolinkConfiguration['addQueryString.']['method'] = $this->addQueryStringMethod;
762 }
763 }
764 if ($this->noCache === true) {
765 $typolinkConfiguration['no_cache'] = 1;
766 }
767 if ($this->section !== '') {
768 $typolinkConfiguration['section'] = $this->section;
769 }
770 if ($this->linkAccessRestrictedPages === true) {
771 $typolinkConfiguration['linkAccessRestrictedPages'] = 1;
772 }
773 return $typolinkConfiguration;
774 }
775
776 /**
777 * Recursively iterates through the specified arguments and turns instances of type \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
778 * into an arrays containing the uid of the domain object.
779 *
780 * @param array $arguments The arguments to be iterated
781 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException
782 * @return array The modified arguments array
783 */
784 protected function convertDomainObjectsToIdentityArrays(array $arguments): array
785 {
786 foreach ($arguments as $argumentKey => $argumentValue) {
787 // if we have a LazyLoadingProxy here, make sure to get the real instance for further processing
788 if ($argumentValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
789 $argumentValue = $argumentValue->_loadRealInstance();
790 // also update the value in the arguments array, because the lazyLoaded object could be
791 // hidden and thus the $argumentValue would be NULL.
792 $arguments[$argumentKey] = $argumentValue;
793 }
794 if ($argumentValue instanceof \Iterator) {
795 $argumentValue = $this->convertIteratorToArray($argumentValue);
796 }
797 if ($argumentValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
798 if ($argumentValue->getUid() !== null) {
799 $arguments[$argumentKey] = $argumentValue->getUid();
800 } elseif ($argumentValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
801 $arguments[$argumentKey] = $this->convertTransientObjectToArray($argumentValue);
802 } else {
803 throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException('Could not serialize Domain Object ' . get_class($argumentValue) . '. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688);
804 }
805 } elseif (is_array($argumentValue)) {
806 $arguments[$argumentKey] = $this->convertDomainObjectsToIdentityArrays($argumentValue);
807 }
808 }
809 return $arguments;
810 }
811
812 /**
813 * @param \Iterator $iterator
814 * @return array
815 */
816 protected function convertIteratorToArray(\Iterator $iterator): array
817 {
818 if (method_exists($iterator, 'toArray')) {
819 $array = $iterator->toArray();
820 } else {
821 $array = iterator_to_array($iterator);
822 }
823 return $array;
824 }
825
826 /**
827 * Converts a given object recursively into an array.
828 *
829 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject $object
830 * @return array
831 * @todo Refactor this into convertDomainObjectsToIdentityArrays()
832 * @internal only to be used within Extbase, not part of TYPO3 Core API.
833 */
834 public function convertTransientObjectToArray(\TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject $object): array
835 {
836 $result = [];
837 foreach ($object->_getProperties() as $propertyName => $propertyValue) {
838 if ($propertyValue instanceof \Iterator) {
839 $propertyValue = $this->convertIteratorToArray($propertyValue);
840 }
841 if ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
842 if ($propertyValue->getUid() !== null) {
843 $result[$propertyName] = $propertyValue->getUid();
844 } else {
845 $result[$propertyName] = $this->convertTransientObjectToArray($propertyValue);
846 }
847 } elseif (is_array($propertyValue)) {
848 $result[$propertyName] = $this->convertDomainObjectsToIdentityArrays($propertyValue);
849 } else {
850 $result[$propertyName] = $propertyValue;
851 }
852 }
853 return $result;
854 }
855 }