Completed
Push — master ( c1d8f4...533b50 )
by
unknown
29:29
created

UriBuilder::getTargetPageType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Extbase\Mvc\Web\Routing;
19
20
use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException;
21
use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
22
use TYPO3\CMS\Core\Utility\ArrayUtility;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Core\Utility\HttpUtility;
25
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
26
use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
27
use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
28
use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException;
29
use TYPO3\CMS\Extbase\Mvc\Request;
30
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
31
use TYPO3\CMS\Extbase\Service\EnvironmentService;
32
use TYPO3\CMS\Extbase\Service\ExtensionService;
33
34
/**
35
 * An URI Builder
36
 */
37
class UriBuilder
38
{
39
    /**
40
     * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
41
     */
42
    protected $configurationManager;
43
44
    /**
45
     * @var \TYPO3\CMS\Extbase\Service\ExtensionService
46
     */
47
    protected $extensionService;
48
49
    /**
50
     * An instance of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
51
     *
52
     * @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
53
     */
54
    protected $contentObject;
55
56
    /**
57
     * @var Request|null
58
     */
59
    protected $request;
60
61
    /**
62
     * @var array
63
     */
64
    protected $arguments = [];
65
66
    /**
67
     * Arguments which have been used for building the last URI
68
     *
69
     * @var array
70
     */
71
    protected $lastArguments = [];
72
73
    /**
74
     * @var string
75
     */
76
    protected $section = '';
77
78
    /**
79
     * @var bool
80
     */
81
    protected $createAbsoluteUri = false;
82
83
    /**
84
     * @var string
85
     */
86
    protected $absoluteUriScheme;
87
88
    /**
89
     * @var bool
90
     */
91
    protected $addQueryString = false;
92
93
    /**
94
     * @var string
95
     */
96
    protected $addQueryStringMethod = '';
97
98
    /**
99
     * @var array
100
     */
101
    protected $argumentsToBeExcludedFromQueryString = [];
102
103
    /**
104
     * @var bool
105
     */
106
    protected $linkAccessRestrictedPages = false;
107
108
    /**
109
     * @var int|null
110
     */
111
    protected $targetPageUid;
112
113
    /**
114
     * @var int
115
     */
116
    protected $targetPageType = 0;
117
118
    /**
119
     * @var string
120
     */
121
    protected $language;
122
123
    /**
124
     * @var bool
125
     */
126
    protected $noCache = false;
127
128
    /**
129
     * @var string
130
     */
131
    protected $format = '';
132
133
    /**
134
     * @var string|null
135
     */
136
    protected $argumentPrefix;
137
138
    /**
139
     * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
140
     */
141
    protected $environmentService;
142
143
    /**
144
     * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
145
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
146
     */
147
    public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager): void
148
    {
149
        $this->configurationManager = $configurationManager;
150
    }
151
152
    /**
153
     * @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
154
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
155
     */
156
    public function injectExtensionService(ExtensionService $extensionService): void
157
    {
158
        $this->extensionService = $extensionService;
159
    }
160
161
    /**
162
     * @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
163
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
164
     */
165
    public function injectEnvironmentService(EnvironmentService $environmentService): void
166
    {
167
        $this->environmentService = $environmentService;
168
    }
169
170
    /**
171
     * Life-cycle method that is called by the DI container as soon as this object is completely built
172
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
173
     */
174
    public function initializeObject(): void
175
    {
176
        $this->contentObject = $this->configurationManager->getContentObject();
177
    }
178
179
    /**
180
     * Sets the current request
181
     *
182
     * @param Request $request
183
     * @return static the current UriBuilder to allow method chaining
184
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
185
     */
186
    public function setRequest(Request $request): UriBuilder
187
    {
188
        $this->request = $request;
189
        return $this;
190
    }
191
192
    /**
193
     * @return Request|null
194
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
195
     */
196
    public function getRequest(): ?Request
197
    {
198
        return $this->request;
199
    }
200
201
    /**
202
     * Additional query parameters.
203
     * If you want to "prefix" arguments, you can pass in multidimensional arrays:
204
     * array('prefix1' => array('foo' => 'bar')) gets "&prefix1[foo]=bar"
205
     *
206
     * @param array $arguments
207
     * @return static the current UriBuilder to allow method chaining
208
     */
209
    public function setArguments(array $arguments): UriBuilder
210
    {
211
        $this->arguments = $arguments;
212
        return $this;
213
    }
214
215
    /**
216
     * @return array
217
     * @internal
218
     */
219
    public function getArguments(): array
220
    {
221
        return $this->arguments;
222
    }
223
224
    /**
225
     * If specified, adds a given HTML anchor to the URI (#...)
226
     *
227
     * @param string $section
228
     * @return static the current UriBuilder to allow method chaining
229
     */
230
    public function setSection(string $section): UriBuilder
231
    {
232
        $this->section = $section;
233
        return $this;
234
    }
235
236
    /**
237
     * @return string
238
     * @internal
239
     */
240
    public function getSection(): string
241
    {
242
        return $this->section;
243
    }
244
245
    /**
246
     * Specifies the format of the target (e.g. "html" or "xml")
247
     *
248
     * @param string $format
249
     * @return static the current UriBuilder to allow method chaining
250
     */
251
    public function setFormat(string $format): UriBuilder
252
    {
253
        $this->format = $format;
254
        return $this;
255
    }
256
257
    /**
258
     * @return string
259
     * @internal
260
     */
261
    public function getFormat(): string
262
    {
263
        return $this->format;
264
    }
265
266
    /**
267
     * If set, the URI is prepended with the current base URI. Defaults to FALSE.
268
     *
269
     * @param bool $createAbsoluteUri
270
     * @return static the current UriBuilder to allow method chaining
271
     */
272
    public function setCreateAbsoluteUri(bool $createAbsoluteUri): UriBuilder
273
    {
274
        $this->createAbsoluteUri = $createAbsoluteUri;
275
        return $this;
276
    }
277
278
    /**
279
     * @return bool
280
     * @internal
281
     */
282
    public function getCreateAbsoluteUri(): bool
283
    {
284
        return $this->createAbsoluteUri;
285
    }
286
287
    /**
288
     * @return string|null
289
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
290
     */
291
    public function getAbsoluteUriScheme(): ?string
292
    {
293
        return $this->absoluteUriScheme;
294
    }
295
296
    /**
297
     * Sets the scheme that should be used for absolute URIs in FE mode
298
     *
299
     * @param string $absoluteUriScheme the scheme to be used for absolute URIs
300
     * @return static the current UriBuilder to allow method chaining
301
     */
302
    public function setAbsoluteUriScheme(string $absoluteUriScheme): UriBuilder
303
    {
304
        $this->absoluteUriScheme = $absoluteUriScheme;
305
        return $this;
306
    }
307
308
    /**
309
     * Enforces a URI / link to a page to a specific language (or use "current")
310
     * @param string|null $language
311
     * @return UriBuilder
312
     */
313
    public function setLanguage(?string $language): UriBuilder
314
    {
315
        $this->language = $language;
316
        return $this;
317
    }
318
319
    public function getLanguage(): ?string
320
    {
321
        return $this->language;
322
    }
323
324
    /**
325
     * If set, the current query parameters will be merged with $this->arguments. Defaults to FALSE.
326
     *
327
     * @param bool $addQueryString
328
     * @return static the current UriBuilder to allow method chaining
329
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
330
     */
331
    public function setAddQueryString(bool $addQueryString): UriBuilder
332
    {
333
        $this->addQueryString = $addQueryString;
334
        return $this;
335
    }
336
337
    /**
338
     * @return bool
339
     * @internal
340
     */
341
    public function getAddQueryString(): bool
342
    {
343
        return $this->addQueryString;
344
    }
345
346
    /**
347
     * Sets the method to get the addQueryString parameters. Defaults to an empty string
348
     * which results in using GeneralUtility::_GET(). Possible values are
349
     *
350
     * + ''      -> uses GeneralUtility::_GET()
351
     * + '0'     -> uses GeneralUtility::_GET()
352
     * + 'GET'   -> uses GeneralUtility::_GET()
353
     * + '<any>' -> uses parse_str(GeneralUtility::getIndpEnv('QUERY_STRING'))
354
     *              (<any> refers to literally everything else than previously mentioned values)
355
     *
356
     * @param string $addQueryStringMethod
357
     * @return static the current UriBuilder to allow method chaining
358
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
359
     */
360
    public function setAddQueryStringMethod(string $addQueryStringMethod): UriBuilder
361
    {
362
        if ($addQueryStringMethod === 'POST') {
363
            trigger_error('Assigning addQueryStringMethod = POST is not supported anymore since TYPO3 v10.0.', E_USER_WARNING);
364
            $addQueryStringMethod = null;
365
        } elseif ($addQueryStringMethod === 'GET,POST' || $addQueryStringMethod === 'POST,GET') {
366
            trigger_error('Assigning addQueryStringMethod = GET,POST or POST,GET is not supported anymore since TYPO3 v10.0 - falling back to GET.', E_USER_WARNING);
367
            $addQueryStringMethod = 'GET';
368
        }
369
        $this->addQueryStringMethod = $addQueryStringMethod;
370
        return $this;
371
    }
372
373
    /**
374
     * @return string
375
     * @internal
376
     */
377
    public function getAddQueryStringMethod(): string
378
    {
379
        return $this->addQueryStringMethod;
380
    }
381
382
    /**
383
     * A list of arguments to be excluded from the query parameters
384
     * Only active if addQueryString is set
385
     *
386
     * @param array $argumentsToBeExcludedFromQueryString
387
     * @return static the current UriBuilder to allow method chaining
388
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
389
     * @see setAddQueryString()
390
     */
391
    public function setArgumentsToBeExcludedFromQueryString(array $argumentsToBeExcludedFromQueryString): UriBuilder
392
    {
393
        $this->argumentsToBeExcludedFromQueryString = $argumentsToBeExcludedFromQueryString;
394
        return $this;
395
    }
396
397
    /**
398
     * @return array
399
     * @internal
400
     */
401
    public function getArgumentsToBeExcludedFromQueryString(): array
402
    {
403
        return $this->argumentsToBeExcludedFromQueryString;
404
    }
405
406
    /**
407
     * Specifies the prefix to be used for all arguments.
408
     *
409
     * @param string $argumentPrefix
410
     * @return static the current UriBuilder to allow method chaining
411
     */
412
    public function setArgumentPrefix(string $argumentPrefix): UriBuilder
413
    {
414
        $this->argumentPrefix = $argumentPrefix;
415
        return $this;
416
    }
417
418
    /**
419
     * @return string|null
420
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
421
     */
422
    public function getArgumentPrefix(): ?string
423
    {
424
        return $this->argumentPrefix;
425
    }
426
427
    /**
428
     * If set, URIs for pages without access permissions will be created
429
     *
430
     * @param bool $linkAccessRestrictedPages
431
     * @return static the current UriBuilder to allow method chaining
432
     */
433
    public function setLinkAccessRestrictedPages(bool $linkAccessRestrictedPages): UriBuilder
434
    {
435
        $this->linkAccessRestrictedPages = $linkAccessRestrictedPages;
436
        return $this;
437
    }
438
439
    /**
440
     * @return bool
441
     * @internal
442
     */
443
    public function getLinkAccessRestrictedPages(): bool
444
    {
445
        return $this->linkAccessRestrictedPages;
446
    }
447
448
    /**
449
     * Uid of the target page
450
     *
451
     * @param int $targetPageUid
452
     * @return static the current UriBuilder to allow method chaining
453
     */
454
    public function setTargetPageUid(int $targetPageUid): UriBuilder
455
    {
456
        $this->targetPageUid = $targetPageUid;
457
        return $this;
458
    }
459
460
    /**
461
     * returns $this->targetPageUid.
462
     *
463
     * @return int|null
464
     * @internal
465
     */
466
    public function getTargetPageUid(): ?int
467
    {
468
        return $this->targetPageUid;
469
    }
470
471
    /**
472
     * Sets the page type of the target URI. Defaults to 0
473
     *
474
     * @param int $targetPageType
475
     * @return static the current UriBuilder to allow method chaining
476
     */
477
    public function setTargetPageType(int $targetPageType): UriBuilder
478
    {
479
        $this->targetPageType = $targetPageType;
480
        return $this;
481
    }
482
483
    /**
484
     * @return int
485
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
486
     */
487
    public function getTargetPageType(): int
488
    {
489
        return $this->targetPageType;
490
    }
491
492
    /**
493
     * by default FALSE; if TRUE, &no_cache=1 will be appended to the URI
494
     *
495
     * @param bool $noCache
496
     * @return static the current UriBuilder to allow method chaining
497
     */
498
    public function setNoCache(bool $noCache): UriBuilder
499
    {
500
        $this->noCache = $noCache;
501
        return $this;
502
    }
503
504
    /**
505
     * @return bool
506
     * @internal
507
     */
508
    public function getNoCache(): bool
509
    {
510
        return $this->noCache;
511
    }
512
513
    /**
514
     * Returns the arguments being used for the last URI being built.
515
     * This is only set after build() / uriFor() has been called.
516
     *
517
     * @return array The last arguments
518
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
519
     */
520
    public function getLastArguments(): array
521
    {
522
        return $this->lastArguments;
523
    }
524
525
    /**
526
     * Resets all UriBuilder options to their default value
527
     *
528
     * @return static the current UriBuilder to allow method chaining
529
     */
530
    public function reset(): UriBuilder
531
    {
532
        $this->arguments = [];
533
        $this->section = '';
534
        $this->format = '';
535
        $this->language = null;
536
        $this->createAbsoluteUri = false;
537
        $this->addQueryString = false;
538
        $this->addQueryStringMethod = '';
539
        $this->argumentsToBeExcludedFromQueryString = [];
540
        $this->linkAccessRestrictedPages = false;
541
        $this->targetPageUid = null;
542
        $this->targetPageType = 0;
543
        $this->noCache = false;
544
        $this->argumentPrefix = null;
545
        $this->absoluteUriScheme = null;
546
        /*
547
         * $this->request MUST NOT be reset here because the request is actually a hard dependency and not part
548
         * of the internal state of this object.
549
         * todo: consider making the request a constructor dependency or get rid of it's usage
550
         */
551
        return $this;
552
    }
553
554
    /**
555
     * Creates an URI used for linking to an Extbase action.
556
     * Works in Frontend and Backend mode of TYPO3.
557
     *
558
     * @param string|null $actionName Name of the action to be called
559
     * @param array|null $controllerArguments Additional query parameters. Will be "namespaced" and merged with $this->arguments.
560
     * @param string|null $controllerName Name of the target controller. If not set, current ControllerName is used.
561
     * @param string|null $extensionName Name of the target extension, without underscores. If not set, current ExtensionName is used.
562
     * @param string|null $pluginName Name of the target plugin. If not set, current PluginName is used.
563
     * @return string the rendered URI
564
     * @see build()
565
     */
566
    public function uriFor(
567
        ?string $actionName = null,
568
        ?array $controllerArguments = null,
569
        ?string $controllerName = null,
570
        ?string $extensionName = null,
571
        ?string $pluginName = null
572
    ): string {
573
        $controllerArguments = $controllerArguments ?? [];
574
575
        if ($actionName !== null) {
576
            $controllerArguments['action'] = $actionName;
577
        }
578
        if ($controllerName !== null) {
579
            $controllerArguments['controller'] = $controllerName;
580
        } else {
581
            $controllerArguments['controller'] = $this->request->getControllerName();
0 ignored issues
show
Bug introduced by
The method getControllerName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

581
            /** @scrutinizer ignore-call */ 
582
            $controllerArguments['controller'] = $this->request->getControllerName();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
582
        }
583
        if ($extensionName === null) {
584
            $extensionName = $this->request->getControllerExtensionName();
585
        }
586
        if ($pluginName === null && $this->environmentService->isEnvironmentInFrontendMode()) {
587
            $pluginName = $this->extensionService->getPluginNameByAction($extensionName, $controllerArguments['controller'], $controllerArguments['action'] ?? null);
588
        }
589
        if ($pluginName === null) {
590
            $pluginName = $this->request->getPluginName();
591
        }
592
        if ($this->environmentService->isEnvironmentInFrontendMode() && $this->configurationManager->isFeatureEnabled('skipDefaultArguments')) {
593
            $controllerArguments = $this->removeDefaultControllerAndAction($controllerArguments, $extensionName, $pluginName);
594
        }
595
        if ($this->targetPageUid === null && $this->environmentService->isEnvironmentInFrontendMode()) {
596
            $this->targetPageUid = $this->extensionService->getTargetPidByPlugin($extensionName, $pluginName);
597
        }
598
        if ($this->format !== '') {
599
            $controllerArguments['format'] = $this->format;
600
        }
601
        if ($this->argumentPrefix !== null) {
602
            $prefixedControllerArguments = [$this->argumentPrefix => $controllerArguments];
603
        } else {
604
            $pluginNamespace = $this->extensionService->getPluginNamespace($extensionName, $pluginName);
605
            $prefixedControllerArguments = [$pluginNamespace => $controllerArguments];
606
        }
607
        ArrayUtility::mergeRecursiveWithOverrule($this->arguments, $prefixedControllerArguments);
608
        return $this->build();
609
    }
610
611
    /**
612
     * This removes controller and/or action arguments from given controllerArguments
613
     * if they are equal to the default controller/action of the target plugin.
614
     * Note: This is only active in FE mode and if feature "skipDefaultArguments" is enabled
615
     *
616
     * @see \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::isFeatureEnabled()
617
     * @param array $controllerArguments the current controller arguments to be modified
618
     * @param string $extensionName target extension name
619
     * @param string $pluginName target plugin name
620
     * @return array
621
     */
622
    protected function removeDefaultControllerAndAction(array $controllerArguments, string $extensionName, string $pluginName): array
623
    {
624
        $defaultControllerName = $this->extensionService->getDefaultControllerNameByPlugin($extensionName, $pluginName);
625
        if (isset($controllerArguments['action'])) {
626
            $defaultActionName = $this->extensionService->getDefaultActionNameByPluginAndController($extensionName, $pluginName, $controllerArguments['controller']);
627
            if ($controllerArguments['action'] === $defaultActionName) {
628
                unset($controllerArguments['action']);
629
            }
630
        }
631
        if ($controllerArguments['controller'] === $defaultControllerName) {
632
            unset($controllerArguments['controller']);
633
        }
634
        return $controllerArguments;
635
    }
636
637
    /**
638
     * Builds the URI
639
     * Depending on the current context this calls buildBackendUri() or buildFrontendUri()
640
     *
641
     * @return string The URI
642
     * @see buildBackendUri()
643
     * @see buildFrontendUri()
644
     */
645
    public function build(): string
646
    {
647
        if ($this->environmentService->isEnvironmentInBackendMode()) {
648
            return $this->buildBackendUri();
649
        }
650
        return $this->buildFrontendUri();
651
    }
652
653
    /**
654
     * Builds the URI, backend flavour
655
     * The resulting URI is relative and starts with "index.php".
656
     * The settings pageUid, pageType, noCache & linkAccessRestrictedPages
657
     * will be ignored in the backend.
658
     *
659
     * @return string The URI
660
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
661
     * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::getQueryArguments
662
     */
663
    public function buildBackendUri(): string
664
    {
665
        $arguments = [];
666
        if ($this->addQueryString === true) {
667
            if ($this->addQueryStringMethod === '' || $this->addQueryStringMethod === '0' || $this->addQueryStringMethod === 'GET') {
668
                $arguments = GeneralUtility::_GET();
669
            } else {
670
                // Explode GET vars recursively
671
                parse_str(GeneralUtility::getIndpEnv('QUERY_STRING'), $arguments);
672
            }
673
            foreach ($this->argumentsToBeExcludedFromQueryString as $argumentToBeExcluded) {
674
                $argumentArrayToBeExcluded = [];
675
                parse_str($argumentToBeExcluded, $argumentArrayToBeExcluded);
676
                $arguments = ArrayUtility::arrayDiffAssocRecursive($arguments, $argumentArrayToBeExcluded);
677
            }
678
        } else {
679
            $id = GeneralUtility::_GP('id');
680
            $route = GeneralUtility::_GP('route');
681
            if ($id !== null) {
682
                $arguments['id'] = $id;
683
            }
684
            if ($route !== null) {
685
                $arguments['route'] = $route;
686
            }
687
        }
688
        ArrayUtility::mergeRecursiveWithOverrule($arguments, $this->arguments);
689
        $arguments = $this->convertDomainObjectsToIdentityArrays($arguments);
690
        $this->lastArguments = $arguments;
691
        $routeName = $arguments['route'] ?? null;
692
        unset($arguments['route'], $arguments['token']);
693
        $backendUriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
694
        try {
695
            if ($this->createAbsoluteUri) {
696
                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
697
            } else {
698
                $uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
699
            }
700
        } catch (ResourceNotFoundException $e) {
701
            try {
702
                if ($this->createAbsoluteUri) {
703
                    $uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
704
                } else {
705
                    $uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
706
                }
707
            } catch (RouteNotFoundException $e) {
708
                $uri = '';
709
            }
710
        }
711
        if ($this->section !== '') {
712
            $uri .= '#' . $this->section;
713
        }
714
        return $uri;
715
    }
716
717
    /**
718
     * Builds the URI, frontend flavour
719
     *
720
     * @return string The URI
721
     * @see buildTypolinkConfiguration()
722
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
723
     */
724
    public function buildFrontendUri(): string
725
    {
726
        $typolinkConfiguration = $this->buildTypolinkConfiguration();
727
        if ($this->createAbsoluteUri === true) {
728
            $typolinkConfiguration['forceAbsoluteUrl'] = true;
729
            if ($this->absoluteUriScheme !== null) {
730
                $typolinkConfiguration['forceAbsoluteUrl.']['scheme'] = $this->absoluteUriScheme;
731
            }
732
        }
733
        // Other than stated in the doc block, typoLink_URL does not always return a string
734
        // Thus, we explicitly cast to string here.
735
        $uri = (string)$this->contentObject->typoLink_URL($typolinkConfiguration);
736
        return $uri;
737
    }
738
739
    /**
740
     * Builds a TypoLink configuration array from the current settings
741
     *
742
     * @return array typolink configuration array
743
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html
744
     */
745
    protected function buildTypolinkConfiguration(): array
746
    {
747
        $typolinkConfiguration = [];
748
        $typolinkConfiguration['parameter'] = $this->targetPageUid ?? $GLOBALS['TSFE']->id;
749
        if ($this->targetPageType !== 0) {
750
            $typolinkConfiguration['parameter'] .= ',' . $this->targetPageType;
751
        } elseif ($this->format !== '') {
752
            $targetPageType = $this->extensionService->getTargetPageTypeByFormat($this->request->getControllerExtensionName(), $this->format);
753
            $typolinkConfiguration['parameter'] .= ',' . $targetPageType;
754
        }
755
        if (!empty($this->arguments)) {
756
            $arguments = $this->convertDomainObjectsToIdentityArrays($this->arguments);
757
            $this->lastArguments = $arguments;
758
            $typolinkConfiguration['additionalParams'] = HttpUtility::buildQueryString($arguments, '&');
759
        }
760
        if ($this->addQueryString === true) {
761
            $typolinkConfiguration['addQueryString'] = 1;
762
            if (!empty($this->argumentsToBeExcludedFromQueryString)) {
763
                $typolinkConfiguration['addQueryString.'] = [
764
                    'exclude' => implode(',', $this->argumentsToBeExcludedFromQueryString)
765
                ];
766
            }
767
            if ($this->addQueryStringMethod !== '') {
768
                $typolinkConfiguration['addQueryString.']['method'] = $this->addQueryStringMethod;
769
            }
770
        }
771
        if ($this->language !== null) {
772
            $typolinkConfiguration['language'] = $this->language;
773
        }
774
775
        if ($this->noCache === true) {
776
            $typolinkConfiguration['no_cache'] = 1;
777
        }
778
        if ($this->section !== '') {
779
            $typolinkConfiguration['section'] = $this->section;
780
        }
781
        if ($this->linkAccessRestrictedPages === true) {
782
            $typolinkConfiguration['linkAccessRestrictedPages'] = 1;
783
        }
784
        return $typolinkConfiguration;
785
    }
786
787
    /**
788
     * Recursively iterates through the specified arguments and turns instances of type \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
789
     * into an arrays containing the uid of the domain object.
790
     *
791
     * @param array $arguments The arguments to be iterated
792
     * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException
793
     * @return array The modified arguments array
794
     */
795
    protected function convertDomainObjectsToIdentityArrays(array $arguments): array
796
    {
797
        foreach ($arguments as $argumentKey => $argumentValue) {
798
            // if we have a LazyLoadingProxy here, make sure to get the real instance for further processing
799
            if ($argumentValue instanceof LazyLoadingProxy) {
800
                $argumentValue = $argumentValue->_loadRealInstance();
801
                // also update the value in the arguments array, because the lazyLoaded object could be
802
                // hidden and thus the $argumentValue would be NULL.
803
                $arguments[$argumentKey] = $argumentValue;
804
            }
805
            if ($argumentValue instanceof \Iterator) {
806
                $argumentValue = $this->convertIteratorToArray($argumentValue);
807
            }
808
            if ($argumentValue instanceof AbstractDomainObject) {
809
                if ($argumentValue->getUid() !== null) {
810
                    $arguments[$argumentKey] = $argumentValue->getUid();
811
                } elseif ($argumentValue instanceof AbstractValueObject) {
812
                    $arguments[$argumentKey] = $this->convertTransientObjectToArray($argumentValue);
813
                } else {
814
                    throw new InvalidArgumentValueException('Could not serialize Domain Object ' . get_class($argumentValue) . '. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688);
815
                }
816
            } elseif (is_array($argumentValue)) {
817
                $arguments[$argumentKey] = $this->convertDomainObjectsToIdentityArrays($argumentValue);
818
            }
819
        }
820
        return $arguments;
821
    }
822
823
    /**
824
     * @param \Iterator $iterator
825
     * @return array
826
     */
827
    protected function convertIteratorToArray(\Iterator $iterator): array
828
    {
829
        if (method_exists($iterator, 'toArray')) {
830
            $array = $iterator->toArray();
831
        } else {
832
            $array = iterator_to_array($iterator);
833
        }
834
        return $array;
835
    }
836
837
    /**
838
     * Converts a given object recursively into an array.
839
     *
840
     * @param \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject $object
841
     * @return array
842
     * @todo Refactor this into convertDomainObjectsToIdentityArrays()
843
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
844
     */
845
    public function convertTransientObjectToArray(AbstractDomainObject $object): array
846
    {
847
        $result = [];
848
        foreach ($object->_getProperties() as $propertyName => $propertyValue) {
849
            if ($propertyValue instanceof \Iterator) {
850
                $propertyValue = $this->convertIteratorToArray($propertyValue);
851
            }
852
            if ($propertyValue instanceof AbstractDomainObject) {
853
                if ($propertyValue->getUid() !== null) {
854
                    $result[$propertyName] = $propertyValue->getUid();
855
                } else {
856
                    $result[$propertyName] = $this->convertTransientObjectToArray($propertyValue);
857
                }
858
            } elseif (is_array($propertyValue)) {
859
                $result[$propertyName] = $this->convertDomainObjectsToIdentityArrays($propertyValue);
860
            } else {
861
                $result[$propertyName] = $propertyValue;
862
            }
863
        }
864
        return $result;
865
    }
866
}
867