Passed
Push — master ( 06455d...1bf66b )
by
unknown
14:25
created

LinkService::resolve()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
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\Core\LinkHandling;
19
20
use TYPO3\CMS\Core\LinkHandling\Exception\UnknownLinkHandlerException;
21
use TYPO3\CMS\Core\LinkHandling\Exception\UnknownUrnException;
22
use TYPO3\CMS\Core\SingletonInterface;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Core\Utility\StringUtility;
25
26
/**
27
 * Class LinkService, responsible to find what kind of resource (type) is used
28
 * to link to (email, external url, file, page etc)
29
 * with the possibility to get a system-wide understandable "urn" to identify
30
 * what type it actually is, based on the scheme or prefix.
31
 */
32
class LinkService implements SingletonInterface
33
{
34
    const TYPE_PAGE = 'page';
35
    const TYPE_URL = 'url';
36
    const TYPE_EMAIL = 'email';
37
    const TYPE_TELEPHONE = 'telephone';
38
    const TYPE_FILE = 'file';
39
    const TYPE_FOLDER = 'folder';
40
    const TYPE_RECORD = 'record';
41
    const TYPE_UNKNOWN = 'unknown';
42
43
    /**
44
     * All registered LinkHandlers
45
     *
46
     * @var LinkHandlingInterface[]
47
     */
48
    protected $handlers;
49
50
    /**
51
     * LinkService constructor initializes the registered handlers.
52
     */
53
    public function __construct()
54
    {
55
        $registeredLinkHandlers = $GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'] ?? [];
56
        $registeredLinkHandlers = is_array($registeredLinkHandlers) ? $registeredLinkHandlers : [];
57
        /** @var array<string,class-string> $registeredLinkHandlers */
58
        if ($registeredLinkHandlers !== []) {
59
            foreach ($registeredLinkHandlers as $type => $handlerClassName) {
60
                if (!isset($this->handlers[$type]) || !is_object($this->handlers[$type])) {
61
                    $handler = GeneralUtility::makeInstance($handlerClassName);
62
                    if ($handler instanceof LinkHandlingInterface) {
63
                        $this->handlers[$type] = $handler;
64
                    }
65
                }
66
            }
67
        }
68
    }
69
70
    /**
71
     * Part of the typolink construction functionality, called by typoLink()
72
     * Used to resolve "legacy"-based typolinks and URNs.
73
     *
74
     * Tries to get the type of the link from the link parameter
75
     * could be
76
     *  - "mailto" an email address
77
     *  - "url" external URL
78
     *  - "file" a local file (checked AFTER getPublicUrl() is called)
79
     *  - "page" a page (integer)
80
     *
81
     * Does NOT check if the page exists or the file exists.
82
     *
83
     * @param string $linkParameter could be "fileadmin/myfile.jpg", "[email protected]", "13" or "http://www.typo3.org"
84
     * @return array
85
     */
86
    public function resolve(string $linkParameter): array
87
    {
88
        try {
89
            // Check if the new syntax with "t3://" is used
90
            return $this->resolveByStringRepresentation($linkParameter);
91
        } catch (UnknownUrnException $e) {
92
            $legacyLinkNotationConverter = GeneralUtility::makeInstance(LegacyLinkNotationConverter::class);
93
            return $legacyLinkNotationConverter->resolve($linkParameter);
94
        }
95
    }
96
97
    /**
98
     * Returns an array with data interpretation of the link target, something like t3://page?uid=23.
99
     *
100
     * @param string $urn
101
     * @return array
102
     * @throws Exception\UnknownLinkHandlerException
103
     * @throws Exception\UnknownUrnException
104
     */
105
    public function resolveByStringRepresentation(string $urn): array
106
    {
107
        // linking to any t3:// syntax
108
        if (stripos($urn, 't3://') === 0) {
109
            // lets parse the urn
110
            $urnParsed = parse_url($urn);
111
            $type = $urnParsed['host'];
112
            if (isset($urnParsed['query'])) {
113
                parse_str(htmlspecialchars_decode($urnParsed['query']), $data);
114
            } else {
115
                $data = [];
116
            }
117
            $fragment = $urnParsed['fragment'] ?? null;
118
119
            if (is_object($this->handlers[$type])) {
120
                $result = $this->handlers[$type]->resolveHandlerData($data);
121
                $result['type'] = $type;
122
            } else {
123
                throw new UnknownLinkHandlerException('LinkHandler for ' . $type . ' was not registered', 1460581769);
124
            }
125
            // this was historically named "section"
126
            if ($fragment) {
127
                $result['fragment'] = $fragment;
128
            }
129
        } elseif ((strpos($urn, '://') || StringUtility::beginsWith($urn, '//')) && $this->handlers[self::TYPE_URL]) {
130
            $result = $this->handlers[self::TYPE_URL]->resolveHandlerData(['url' => $urn]);
131
            $result['type'] = self::TYPE_URL;
132
        } elseif (stripos($urn, 'mailto:') === 0 && $this->handlers[self::TYPE_EMAIL]) {
133
            $result = $this->handlers[self::TYPE_EMAIL]->resolveHandlerData(['email' => $urn]);
134
            $result['type'] = self::TYPE_EMAIL;
135
        } elseif (stripos($urn, 'tel:') === 0 && $this->handlers[self::TYPE_TELEPHONE]) {
136
            $result = $this->handlers[self::TYPE_TELEPHONE]->resolveHandlerData(['telephone' => $urn]);
137
            $result['type'] = self::TYPE_TELEPHONE;
138
        } else {
139
            $result = [];
140
            if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Link']['resolveByStringRepresentation'] ?? null)) {
141
                $params = ['urn' => $urn, 'result' => &$result];
142
                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Link']['resolveByStringRepresentation'] as $hookMethod) {
143
                    $fakeThis = null;
144
                    GeneralUtility::callUserFunction($hookMethod, $params, $fakeThis);
145
                }
146
            }
147
            if (empty($result) || empty($result['type'])) {
148
                throw new UnknownUrnException('No valid URN to resolve found', 1457177667);
149
            }
150
        }
151
152
        return $result;
153
    }
154
155
    /**
156
     * Returns a string interpretation of the link target, something like
157
     *
158
     *  - t3://page?uid=23&my=value#cool
159
     *  - https://www.typo3.org/
160
     *  - t3://file?uid=13
161
     *  - t3://folder?storage=2&identifier=/my/folder/
162
     *  - mailto:[email protected]
163
     *
164
     * @param array $parameters
165
     * @return string
166
     * @throws Exception\UnknownLinkHandlerException
167
     */
168
    public function asString(array $parameters): string
169
    {
170
        $linkHandler = $this->handlers[$parameters['type']] ?? null;
171
        if ($linkHandler !== null) {
172
            return $this->handlers[$parameters['type']]->asString($parameters);
173
        }
174
        if (isset($parameters['url']) && !empty($parameters['url'])) {
175
            // This usually happens for tel: or other types where a URL is available and the
176
            // legacy link service could resolve at least something
177
            return $parameters['url'];
178
        }
179
        throw new UnknownLinkHandlerException('No valid handlers found for type: ' . $parameters['type'], 1460629247);
180
    }
181
}
182