Completed
Push — master ( c52b93...4abd7b )
by
unknown
19:44
created

UriBuilder::getLanguage()   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\Mvc\Request;
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 string
113
     */
114
    protected $language;
115
116
    /**
117
     * @var bool
118
     */
119
    protected $noCache = false;
120
121
    /**
122
     * @var string
123
     */
124
    protected $format = '';
125
126
    /**
127
     * @var string|null
128
     */
129
    protected $argumentPrefix;
130
131
    /**
132
     * @var \TYPO3\CMS\Extbase\Service\EnvironmentService
133
     */
134
    protected $environmentService;
135
136
    /**
137
     * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
138
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
139
     */
140
    public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager): void
141
    {
142
        $this->configurationManager = $configurationManager;
143
    }
144
145
    /**
146
     * @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
147
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
148
     */
149
    public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService): void
150
    {
151
        $this->extensionService = $extensionService;
152
    }
153
154
    /**
155
     * @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
156
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
157
     */
158
    public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService): void
159
    {
160
        $this->environmentService = $environmentService;
161
    }
162
163
    /**
164
     * Life-cycle method that is called by the DI container as soon as this object is completely built
165
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
166
     */
167
    public function initializeObject(): void
168
    {
169
        $this->contentObject = $this->configurationManager->getContentObject();
170
    }
171
172
    /**
173
     * Sets the current request
174
     *
175
     * @param Request $request
176
     * @return static the current UriBuilder to allow method chaining
177
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
178
     */
179
    public function setRequest(Request $request): UriBuilder
180
    {
181
        $this->request = $request;
182
        return $this;
183
    }
184
185
    /**
186
     * @return Request|null
187
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
188
     */
189
    public function getRequest(): ?Request
190
    {
191
        return $this->request;
192
    }
193
194
    /**
195
     * Additional query parameters.
196
     * If you want to "prefix" arguments, you can pass in multidimensional arrays:
197
     * array('prefix1' => array('foo' => 'bar')) gets "&prefix1[foo]=bar"
198
     *
199
     * @param array $arguments
200
     * @return static the current UriBuilder to allow method chaining
201
     */
202
    public function setArguments(array $arguments): UriBuilder
203
    {
204
        $this->arguments = $arguments;
205
        return $this;
206
    }
207
208
    /**
209
     * @return array
210
     * @internal
211
     */
212
    public function getArguments(): array
213
    {
214
        return $this->arguments;
215
    }
216
217
    /**
218
     * If specified, adds a given HTML anchor to the URI (#...)
219
     *
220
     * @param string $section
221
     * @return static the current UriBuilder to allow method chaining
222
     */
223
    public function setSection(string $section): UriBuilder
224
    {
225
        $this->section = $section;
226
        return $this;
227
    }
228
229
    /**
230
     * @return string
231
     * @internal
232
     */
233
    public function getSection(): string
234
    {
235
        return $this->section;
236
    }
237
238
    /**
239
     * Specifies the format of the target (e.g. "html" or "xml")
240
     *
241
     * @param string $format
242
     * @return static the current UriBuilder to allow method chaining
243
     */
244
    public function setFormat(string $format): UriBuilder
245
    {
246
        $this->format = $format;
247
        return $this;
248
    }
249
250
    /**
251
     * @return string
252
     * @internal
253
     */
254
    public function getFormat(): string
255
    {
256
        return $this->format;
257
    }
258
259
    /**
260
     * If set, the URI is prepended with the current base URI. Defaults to FALSE.
261
     *
262
     * @param bool $createAbsoluteUri
263
     * @return static the current UriBuilder to allow method chaining
264
     */
265
    public function setCreateAbsoluteUri(bool $createAbsoluteUri): UriBuilder
266
    {
267
        $this->createAbsoluteUri = $createAbsoluteUri;
268
        return $this;
269
    }
270
271
    /**
272
     * @return bool
273
     * @internal
274
     */
275
    public function getCreateAbsoluteUri(): bool
276
    {
277
        return $this->createAbsoluteUri;
278
    }
279
280
    /**
281
     * @return string|null
282
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
283
     */
284
    public function getAbsoluteUriScheme(): ?string
285
    {
286
        return $this->absoluteUriScheme;
287
    }
288
289
    /**
290
     * Sets the scheme that should be used for absolute URIs in FE mode
291
     *
292
     * @param string $absoluteUriScheme the scheme to be used for absolute URIs
293
     * @return static the current UriBuilder to allow method chaining
294
     */
295
    public function setAbsoluteUriScheme(string $absoluteUriScheme): UriBuilder
296
    {
297
        $this->absoluteUriScheme = $absoluteUriScheme;
298
        return $this;
299
    }
300
301
    /**
302
     * Enforces a URI / link to a page to a specific language (or use "current")
303
     * @param string|null $language
304
     * @return UriBuilder
305
     */
306
    public function setLanguage(?string $language): UriBuilder
307
    {
308
        $this->language = $language;
309
        return $this;
310
    }
311
312
    public function getLanguage(): ?string
313
    {
314
        return $this->language;
315
    }
316
317
    /**
318
     * If set, the current query parameters will be merged with $this->arguments. Defaults to FALSE.
319
     *
320
     * @param bool $addQueryString
321
     * @return static the current UriBuilder to allow method chaining
322
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
323
     */
324
    public function setAddQueryString(bool $addQueryString): UriBuilder
325
    {
326
        $this->addQueryString = $addQueryString;
327
        return $this;
328
    }
329
330
    /**
331
     * @return bool
332
     * @internal
333
     */
334
    public function getAddQueryString(): bool
335
    {
336
        return $this->addQueryString;
337
    }
338
339
    /**
340
     * Sets the method to get the addQueryString parameters. Defaults to an empty string
341
     * which results in using GeneralUtility::_GET(). Possible values are
342
     *
343
     * + ''      -> uses GeneralUtility::_GET()
344
     * + '0'     -> uses GeneralUtility::_GET()
345
     * + 'GET'   -> uses GeneralUtility::_GET()
346
     * + '<any>' -> uses parse_str(GeneralUtility::getIndpEnv('QUERY_STRING'))
347
     *              (<any> refers to literally everything else than previously mentioned values)
348
     *
349
     * @param string $addQueryStringMethod
350
     * @return static the current UriBuilder to allow method chaining
351
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
352
     */
353
    public function setAddQueryStringMethod(string $addQueryStringMethod): UriBuilder
354
    {
355
        if ($addQueryStringMethod === 'POST') {
356
            trigger_error('Assigning addQueryStringMethod = POST is not supported anymore since TYPO3 v10.0.', E_USER_WARNING);
357
            $addQueryStringMethod = null;
358
        } elseif ($addQueryStringMethod === 'GET,POST' || $addQueryStringMethod === 'POST,GET') {
359
            trigger_error('Assigning addQueryStringMethod = GET,POST or POST,GET is not supported anymore since TYPO3 v10.0 - falling back to GET.', E_USER_WARNING);
360
            $addQueryStringMethod = 'GET';
361
        }
362
        $this->addQueryStringMethod = $addQueryStringMethod;
363
        return $this;
364
    }
365
366
    /**
367
     * @return string
368
     * @internal
369
     */
370
    public function getAddQueryStringMethod(): string
371
    {
372
        return $this->addQueryStringMethod;
373
    }
374
375
    /**
376
     * A list of arguments to be excluded from the query parameters
377
     * Only active if addQueryString is set
378
     *
379
     * @param array $argumentsToBeExcludedFromQueryString
380
     * @return static the current UriBuilder to allow method chaining
381
     * @see https://docs.typo3.org/m/typo3/reference-typoscript/master/en-us/Functions/Typolink.html#addquerystring
382
     * @see setAddQueryString()
383
     */
384
    public function setArgumentsToBeExcludedFromQueryString(array $argumentsToBeExcludedFromQueryString): UriBuilder
385
    {
386
        $this->argumentsToBeExcludedFromQueryString = $argumentsToBeExcludedFromQueryString;
387
        return $this;
388
    }
389
390
    /**
391
     * @return array
392
     * @internal
393
     */
394
    public function getArgumentsToBeExcludedFromQueryString(): array
395
    {
396
        return $this->argumentsToBeExcludedFromQueryString;
397
    }
398
399
    /**
400
     * Specifies the prefix to be used for all arguments.
401
     *
402
     * @param string $argumentPrefix
403
     * @return static the current UriBuilder to allow method chaining
404
     */
405
    public function setArgumentPrefix(string $argumentPrefix): UriBuilder
406
    {
407
        $this->argumentPrefix = $argumentPrefix;
408
        return $this;
409
    }
410
411
    /**
412
     * @return string|null
413
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
414
     */
415
    public function getArgumentPrefix(): ?string
416
    {
417
        return $this->argumentPrefix;
418
    }
419
420
    /**
421
     * If set, URIs for pages without access permissions will be created
422
     *
423
     * @param bool $linkAccessRestrictedPages
424
     * @return static the current UriBuilder to allow method chaining
425
     */
426
    public function setLinkAccessRestrictedPages(bool $linkAccessRestrictedPages): UriBuilder
427
    {
428
        $this->linkAccessRestrictedPages = $linkAccessRestrictedPages;
429
        return $this;
430
    }
431
432
    /**
433
     * @return bool
434
     * @internal
435
     */
436
    public function getLinkAccessRestrictedPages(): bool
437
    {
438
        return $this->linkAccessRestrictedPages;
439
    }
440
441
    /**
442
     * Uid of the target page
443
     *
444
     * @param int $targetPageUid
445
     * @return static the current UriBuilder to allow method chaining
446
     */
447
    public function setTargetPageUid(int $targetPageUid): UriBuilder
448
    {
449
        $this->targetPageUid = $targetPageUid;
450
        return $this;
451
    }
452
453
    /**
454
     * returns $this->targetPageUid.
455
     *
456
     * @return int|null
457
     * @internal
458
     */
459
    public function getTargetPageUid(): ?int
460
    {
461
        return $this->targetPageUid;
462
    }
463
464
    /**
465
     * Sets the page type of the target URI. Defaults to 0
466
     *
467
     * @param int $targetPageType
468
     * @return static the current UriBuilder to allow method chaining
469
     */
470
    public function setTargetPageType(int $targetPageType): UriBuilder
471
    {
472
        $this->targetPageType = $targetPageType;
473
        return $this;
474
    }
475
476
    /**
477
     * @return int
478
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
479
     */
480
    public function getTargetPageType(): int
481
    {
482
        return $this->targetPageType;
483
    }
484
485
    /**
486
     * by default FALSE; if TRUE, &no_cache=1 will be appended to the URI
487
     *
488
     * @param bool $noCache
489
     * @return static the current UriBuilder to allow method chaining
490
     */
491
    public function setNoCache(bool $noCache): UriBuilder
492
    {
493
        $this->noCache = $noCache;
494
        return $this;
495
    }
496
497
    /**
498
     * @return bool
499
     * @internal
500
     */
501
    public function getNoCache(): bool
502
    {
503
        return $this->noCache;
504
    }
505
506
    /**
507
     * by default TRUE; if FALSE, no cHash parameter will be appended to the URI
508
     * If noCache is set, this setting will be ignored.
509
     *
510
     * @return static the current UriBuilder to allow method chaining
511
     */
512
    public function setUseCacheHash(): UriBuilder
513
    {
514
        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);
515
        return $this;
516
    }
517
518
    /**
519
     * @return bool
520
     * @internal
521
     */
522
    public function getUseCacheHash(): bool
523
    {
524
        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);
525
        return true;
526
    }
527
528
    /**
529
     * Returns the arguments being used for the last URI being built.
530
     * This is only set after build() / uriFor() has been called.
531
     *
532
     * @return array The last arguments
533
     * @internal only to be used within Extbase, not part of TYPO3 Core API.
534
     */
535
    public function getLastArguments(): array
536
    {
537
        return $this->lastArguments;
538
    }
539
540
    /**
541
     * Resets all UriBuilder options to their default value
542
     *
543
     * @return static the current UriBuilder to allow method chaining
544
     */
545
    public function reset(): UriBuilder
546
    {
547
        $this->arguments = [];
548
        $this->section = '';
549
        $this->format = '';
550
        $this->language = null;
551
        $this->createAbsoluteUri = false;
552
        $this->addQueryString = false;
553
        $this->addQueryStringMethod = '';
554
        $this->argumentsToBeExcludedFromQueryString = [];
555
        $this->linkAccessRestrictedPages = false;
556
        $this->targetPageUid = null;
557
        $this->targetPageType = 0;
558
        $this->noCache = false;
559
        $this->argumentPrefix = null;
560
        $this->absoluteUriScheme = null;
561
        /*
562
         * $this->request MUST NOT be reset here because the request is actually a hard dependency and not part
563
         * of the internal state of this object.
564
         * todo: consider making the request a constructor dependency or get rid of it's usage
565
         */
566
        return $this;
567
    }
568
569
    /**
570
     * Creates an URI used for linking to an Extbase action.
571
     * Works in Frontend and Backend mode of TYPO3.
572
     *
573
     * @param string|null $actionName Name of the action to be called
574
     * @param array|null $controllerArguments Additional query parameters. Will be "namespaced" and merged with $this->arguments.
575
     * @param string|null $controllerName Name of the target controller. If not set, current ControllerName is used.
576
     * @param string|null $extensionName Name of the target extension, without underscores. If not set, current ExtensionName is used.
577
     * @param string|null $pluginName Name of the target plugin. If not set, current PluginName is used.
578
     * @return string the rendered URI
579
     * @see build()
580
     */
581
    public function uriFor(
582
        ?string $actionName = null,
583
        ?array $controllerArguments = null,
584
        ?string $controllerName = null,
585
        ?string $extensionName = null,
586
        ?string $pluginName = null
587
    ): string {
588
        $controllerArguments = $controllerArguments ?? [];
589
590
        if ($actionName !== null) {
591
            $controllerArguments['action'] = $actionName;
592
        }
593
        if ($controllerName !== null) {
594
            $controllerArguments['controller'] = $controllerName;
595
        } else {
596
            $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

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