Passed
Push — master ( 01ab6c...0597c0 )
by
unknown
39:34 queued 21:30
created

resolvePageRelatedParameters()   B

Complexity

Conditions 11
Paths 30

Size

Total Lines 32
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 24
nc 30
nop 1
dl 0
loc 32
rs 7.3166
c 0
b 0
f 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
declare(strict_types = 1);
3
namespace TYPO3\CMS\Core\LinkHandling;
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
use TYPO3\CMS\Core\Core\Environment;
19
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
20
use TYPO3\CMS\Core\Resource\File;
21
use TYPO3\CMS\Core\Resource\Folder;
22
use TYPO3\CMS\Core\Resource\ResourceFactory;
23
use TYPO3\CMS\Core\Utility\GeneralUtility;
24
use TYPO3\CMS\Core\Utility\MathUtility;
25
26
/**
27
 * Class to resolve and convert the "old" link information (email, external url, file, page etc)
28
 * to a URL or new format for migration
29
 *
30
 * @internal
31
 */
32
class LegacyLinkNotationConverter
33
{
34
35
    /**
36
     * @var ResourceFactory
37
     */
38
    protected $resourceFactory;
39
40
    /**
41
     * Part of the typolink construction functionality, called by typoLink()
42
     * Used to resolve "legacy"-based typolinks.
43
     *
44
     * Tries to get the type of the link from the link parameter
45
     * could be
46
     *  - "mailto" an email address
47
     *  - "url" external URL
48
     *  - "file" a local file (checked AFTER getPublicUrl() is called)
49
     *  - "page" a page (integer)
50
     *
51
     * Does NOT check if the page exists or the file exists.
52
     *
53
     * @param string $linkParameter could be "fileadmin/myfile.jpg", "[email protected]", "13" or "http://www.typo3.org"
54
     *
55
     * @return array
56
     */
57
    public function resolve(string $linkParameter): array
58
    {
59
        $a = [];
60
        if (stripos(rawurldecode(trim($linkParameter)), 'phar://') === 0) {
61
            throw new \RuntimeException(
62
                'phar scheme not allowed as soft reference target',
63
                1530030673
64
            );
65
        }
66
67
        $result = [];
68
        // Parse URL scheme
69
        $scheme = parse_url($linkParameter, PHP_URL_SCHEME);
70
71
        // Resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier"
72
        if (stripos($linkParameter, 'file:') === 0) {
73
            $result = $this->getFileOrFolderObjectFromMixedIdentifier(substr($linkParameter, 5));
74
        } elseif (GeneralUtility::validEmail(parse_url($linkParameter, PHP_URL_PATH))) {
75
            $result['type'] = LinkService::TYPE_EMAIL;
76
            $result['email'] = $linkParameter;
77
        } elseif (strpos($linkParameter, 'tel:') === 0) {
78
            $result['type'] = LinkService::TYPE_TELEPHONE;
79
            $result['telephone'] = $linkParameter;
80
        } elseif (strpos($linkParameter, ':') !== false) {
81
            // Check for link-handler keyword
82
            [$linkHandlerKeyword, $linkHandlerValue] = explode(':', $linkParameter, 2);
83
            $result['type'] = strtolower(trim($linkHandlerKeyword));
84
            $result['url'] = $linkParameter;
85
            $result['value'] = $linkHandlerValue;
86
            if ($result['type'] === LinkService::TYPE_RECORD) {
87
                [$a['identifier'], $tableAndUid] = explode(':', $linkHandlerValue, 2);
88
                $tableAndUid = explode(':', $tableAndUid);
89
                if (count($tableAndUid) > 1) {
90
                    $a['table'] = $tableAndUid[0];
91
                    $a['uid'] = $tableAndUid[1];
92
                } else {
93
                    // this case can happen if there is the very old linkhandler syntax, which was only record:<table>:<uid>
94
                    $a['table'] = $a['identifier'];
95
                    $a['uid'] = $tableAndUid[0];
96
                }
97
                $result = array_merge($result, $a);
98
            }
99
        } else {
100
            // special handling without a scheme
101
            $isLocalFile = 0;
102
            $fileChar = (int)strpos($linkParameter, '/');
103
            $urlChar = (int)strpos($linkParameter, '.');
104
105
            $isIdOrAlias = MathUtility::canBeInterpretedAsInteger($linkParameter);
106
            $matches = [];
107
            // capture old RTE links relative to TYPO3_mainDir
108
            if (preg_match('#../(?:index\\.php)?\\?id=([^&]+)#', $linkParameter, $matches)) {
109
                $linkParameter = $matches[1];
110
                $isIdOrAlias = true;
111
            }
112
            $containsSlash = false;
113
            if (!$isIdOrAlias) {
114
                // Detects if a file is found in site-root and if so it will be treated like a normal file.
115
                [$rootFileDat] = explode('?', rawurldecode($linkParameter));
116
                $containsSlash = strpos($rootFileDat, '/') !== false;
117
                $pathInfo = pathinfo($rootFileDat);
118
                $fileExtension = strtolower($pathInfo['extension'] ?? '');
119
                if (!$containsSlash
120
                    && trim($rootFileDat)
121
                    && (
122
                        @is_file(Environment::getPublicPath() . '/' . $rootFileDat)
123
                        || $fileExtension === 'php'
124
                        || $fileExtension === 'html'
125
                        || $fileExtension === 'htm'
126
                    )
127
                ) {
128
                    $isLocalFile = 1;
129
                } elseif ($containsSlash) {
130
                    // Adding this so realurl directories are linked right (non-existing).
131
                    $isLocalFile = 2;
132
                }
133
            }
134
135
            // url (external): If doubleSlash or if a '.' comes before a '/'.
136
            if (!$isIdOrAlias && $isLocalFile !== 1 && $urlChar && (!$containsSlash || $urlChar < $fileChar)) {
137
                $result['type'] = LinkService::TYPE_URL;
138
                if (!$scheme) {
139
                    $result['url'] = 'http://' . $linkParameter;
140
                } else {
141
                    $result['url'] = $linkParameter;
142
                }
143
                // file (internal) or folder
144
            } elseif ($containsSlash || $isLocalFile) {
145
                $result = $this->getFileOrFolderObjectFromMixedIdentifier($linkParameter);
146
            } else {
147
                // Integer or alias (alias is without slashes or periods or commas, that is
148
                // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
149
                $result = $this->resolvePageRelatedParameters($linkParameter);
150
            }
151
        }
152
153
        return $result;
154
    }
155
156
    /**
157
     * Internal method to do some magic to get a page parts, additional params, fragment / section hash
158
     *
159
     * @param string $data the input variable, can be "mypage,23" with fragments, keys
160
     *
161
     * @return array the result array with the page type set
162
     */
163
    protected function resolvePageRelatedParameters(string $data): array
164
    {
165
        $result = ['type' => LinkService::TYPE_PAGE];
166
        if (strpos($data, '#') !== false) {
167
            [$data, $result['fragment']] = explode('#', $data, 2);
168
        }
169
        // check for additional parameters
170
        if (strpos($data, '?') !== false) {
171
            [$data, $result['parameters']] = explode('?', $data, 2);
172
        } elseif (strpos($data, '&') !== false) {
173
            [$data, $result['parameters']] = explode('&', $data, 2);
174
        }
175
        if (empty($data)) {
176
            $result['pageuid'] = 'current';
177
        } elseif ($data[0] === '#') {
178
            $result['pageuid'] = 'current';
179
            $result['fragment'] = substr($data, 1);
180
        } elseif (strpos($data, ',') !== false) {
181
            $data = rtrim($data, ',');
182
            [$result['pageuid'], $result['pagetype']] = explode(',', $data, 2);
183
        } elseif (strpos($data, '/') !== false) {
184
            $data = explode('/', trim($data, '/'));
185
            $result['pageuid'] = array_shift($data);
186
            foreach ($data as $k => $item) {
187
                if ($data[$k] % 2 === 0 && !empty($data[$k + 1])) {
188
                    $result['page' . $data[$k]] = $data[$k + 1];
189
                }
190
            }
191
        } else {
192
            $result['pageuid'] = $data;
193
        }
194
        return $result;
195
    }
196
197
    /**
198
     * Internal method that fetches a file or folder object based on the file or folder combined identifier
199
     *
200
     * @param string $mixedIdentifier can be something like "2" (file uid), "fileadmin/i/like.png" or "2:/myidentifier/"
201
     *
202
     * @return array the result with the type (file or folder) set
203
     */
204
    protected function getFileOrFolderObjectFromMixedIdentifier(string $mixedIdentifier): array
205
    {
206
        $result = [];
207
        if (strpos($mixedIdentifier, '#') !== false) {
208
            [$mixedIdentifier, $result['fragment']] = explode('#', $mixedIdentifier, 2);
209
        }
210
        try {
211
            $fileOrFolderObject = $this->getResourceFactory()->retrieveFileOrFolderObject($mixedIdentifier);
212
            // Link to a folder or file
213
            if ($fileOrFolderObject instanceof File) {
214
                $result['type'] = LinkService::TYPE_FILE;
215
                $result['file'] = $fileOrFolderObject;
216
            } elseif ($fileOrFolderObject instanceof Folder) {
0 ignored issues
show
introduced by
$fileOrFolderObject is always a sub-type of TYPO3\CMS\Core\Resource\Folder.
Loading history...
217
                $result['type'] = LinkService::TYPE_FOLDER;
218
                $result['folder'] = $fileOrFolderObject;
219
            } else {
220
                $result['type'] = LinkService::TYPE_UNKNOWN;
221
                $result['file'] = $mixedIdentifier;
222
            }
223
        } catch (\RuntimeException $e) {
224
            // Element wasn't found
225
            $result['type'] = LinkService::TYPE_UNKNOWN;
226
            $result['file'] = $mixedIdentifier;
227
        } catch (ResourceDoesNotExistException $e) {
228
            // Resource was not found
229
            $result['type'] = LinkService::TYPE_UNKNOWN;
230
            $result['file'] = $mixedIdentifier;
231
        }
232
233
        return $result;
234
    }
235
236
    /**
237
     * Initializes the resource factory (only once)
238
     *
239
     * @return ResourceFactory
240
     */
241
    protected function getResourceFactory(): ResourceFactory
242
    {
243
        if (!$this->resourceFactory) {
244
            $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
245
        }
246
        return $this->resourceFactory;
247
    }
248
}
249