Passed
Push — master ( d66d7f...a7bade )
by
unknown
18:08
created

UriBuilder::buildTypolinkConfiguration()   C

Complexity

Conditions 10
Paths 288

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 25
c 0
b 0
f 0
dl 0
loc 37
rs 5.7333
cc 10
nc 288
nop 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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