‪TYPO3CMS  ‪main
LegacyLinkNotationConverter.php
Go to the documentation of this file.
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 
19 
28 
36 {
40  protected ‪$resourceFactory;
41 
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  // @todo If resolved result (linkDetails) are later used to build an uri using LinkBuilder->build(), it's needed
69  // to have the original $linkParameter in the result array. Otherwise, places may break like e.g. the
70  // DatabaseRecordLinkBuilder. Can we safely set this here directly and avoiding calls before build like
71  // "$linkDetails['typoLinkParameter'] = $redirectTarget;" - e.g. like in the ext:redirects
72  // TYPO3\CMS\Redirects\Service\RedirectService::resolveLinkDetailsFromLinkTarget() and other places.
73 
74  // Resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier"
75  if (stripos($linkParameter, 'file:') === 0) {
76  $result = $this->‪getFileOrFolderObjectFromMixedIdentifier(substr($linkParameter, 5));
77  } elseif (GeneralUtility::validEmail((string)parse_url($linkParameter, PHP_URL_PATH))) {
78  $result['type'] = ‪LinkService::TYPE_EMAIL;
79  $result['email'] = $linkParameter;
80  } elseif (str_starts_with($linkParameter, 'tel:')) {
81  $result['type'] = ‪LinkService::TYPE_TELEPHONE;
82  $result['telephone'] = $linkParameter;
83  } elseif (str_contains($linkParameter, ':')) {
84  // Check for link-handler keyword
85  [$linkHandlerKeyword, $linkHandlerValue] = explode(':', $linkParameter, 2);
86  $result['type'] = strtolower(trim($linkHandlerKeyword));
87  if ($linkHandlerValue === '') {
88  return [
90  'url' => $linkParameter,
91  ];
92  }
93  $result['url'] = $linkParameter;
94  $result['value'] = $linkHandlerValue;
95  if ($result['type'] === ‪LinkService::TYPE_RECORD) {
96  [$a['identifier'], $tableAndUid] = explode(':', $linkHandlerValue, 2);
97  $tableAndUid = explode(':', $tableAndUid);
98  if (count($tableAndUid) > 1) {
99  $a['table'] = $tableAndUid[0];
100  $a['uid'] = (int)$tableAndUid[1];
101  } else {
102  // this case can happen if there is the very old linkhandler syntax, which was only record:<table>:<uid>
103  $a['table'] = $a['identifier'];
104  $a['uid'] = (int)$tableAndUid[0];
105  }
106  $result = array_merge($result, $a);
107  }
108  } else {
109  // special handling without a scheme
110  $isLocalFile = 0;
111  $fileChar = (int)strpos($linkParameter, '/');
112  $urlChar = (int)strpos($linkParameter, '.');
113 
114  $isIdOrAlias = ‪MathUtility::canBeInterpretedAsInteger($linkParameter);
115  $matches = [];
116  // capture old RTE links relative to TYPO3 Backend /typo3/
117  if (preg_match('#../(?:index\\.php)?\\?id=([^&]+)#', $linkParameter, $matches)) {
118  $linkParameter = $matches[1];
119  $isIdOrAlias = true;
120  }
121  $containsSlash = false;
122  if (!$isIdOrAlias) {
123  // Detects if a file is found in site-root and if so it will be treated like a normal file.
124  [$rootFileDat] = explode('?', rawurldecode($linkParameter));
125  $containsSlash = str_contains($rootFileDat, '/');
126  $pathInfo = pathinfo($rootFileDat);
127  $fileExtension = strtolower($pathInfo['extension'] ?? '');
128  if (!$containsSlash
129  && trim($rootFileDat)
130  && (
131  @is_file(‪Environment::getPublicPath() . '/' . $rootFileDat)
132  || $fileExtension === 'php'
133  || $fileExtension === 'html'
134  || $fileExtension === 'htm'
135  )
136  ) {
137  $isLocalFile = 1;
138  } elseif ($containsSlash) {
139  // Adding this so realurl directories are linked right (non-existing).
140  $isLocalFile = 2;
141  }
142  }
143 
144  // url (external): If doubleSlash or if a '.' comes before a '/'.
145  if (!$isIdOrAlias && $isLocalFile !== 1 && $urlChar && (!$containsSlash || $urlChar < $fileChar)) {
146  $result['type'] = ‪LinkService::TYPE_URL;
147  $result['url'] = ‪UrlLinkHandler::getDefaultScheme() . '://' . $linkParameter;
148  // file (internal) or folder
149  } elseif ($containsSlash || $isLocalFile) {
150  $result = $this->‪getFileOrFolderObjectFromMixedIdentifier($linkParameter);
151  } else {
152  // Integer or alias (alias is without slashes or periods or commas, that is
153  // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
154  $result = $this->‪resolvePageRelatedParameters($linkParameter);
155  }
156  }
157 
158  return $result;
159  }
160 
168  protected function ‪resolvePageRelatedParameters(string $data): array
169  {
170  $result = ['type' => ‪LinkService::TYPE_PAGE];
171  if (str_contains($data, '#')) {
172  [$data, $result['fragment']] = explode('#', $data, 2);
173  }
174  // check for additional parameters
175  if (str_contains($data, '?')) {
176  [$data, $result['parameters']] = explode('?', $data, 2);
177  } elseif (str_contains($data, '&')) {
178  [$data, $result['parameters']] = explode('&', $data, 2);
179  }
180  $data = rtrim($data, ',');
181  if (empty($data)) {
182  $result['pageuid'] = 'current';
183  } elseif ($data[0] === '#') {
184  $result['pageuid'] = 'current';
185  $result['fragment'] = substr($data, 1);
186  } elseif (str_contains($data, ',')) {
187  [$result['pageuid'], $result['pagetype']] = explode(',', $data, 2);
188  } elseif (str_contains($data, '/')) {
189  $data = explode('/', trim($data, '/'));
190  $result['pageuid'] = array_shift($data);
191  foreach ($data as $k => $item) {
192  if ($data[$k] % 2 === 0 && !empty($data[$k + 1])) {
193  $result['page' . $data[$k]] = $data[$k + 1];
194  }
195  }
196  } else {
197  $result['pageuid'] = $data;
198  }
199  if (‪MathUtility::canBeInterpretedAsInteger($result['pageuid'])) {
200  $result['pageuid'] = (int)$result['pageuid'];
201  }
202  return $result;
203  }
204 
212  protected function ‪getFileOrFolderObjectFromMixedIdentifier(string $mixedIdentifier): array
213  {
214  $result = [];
215  try {
216  $fileIdentifier = $mixedIdentifier;
217  $fragment = null;
218  if (str_contains($fileIdentifier, '#')) {
219  [$fileIdentifier, $fragment] = explode('#', $fileIdentifier, 2);
220  }
221  $fileOrFolderObject = $this->‪getResourceFactory()->retrieveFileOrFolderObject($fileIdentifier);
222  // Links to a file/folder in the main TYPO3 directory should not be considered as file links, but an external link
223  if ($fileOrFolderObject instanceof ‪ResourceInterface && $fileOrFolderObject->‪getStorage()->getUid() === 0) {
224  return [
225  'type' => ‪LinkService::TYPE_URL,
226  'url' => $mixedIdentifier,
227  ];
228  }
229  // Link to a folder or file
230  if ($fileOrFolderObject instanceof ‪File) {
231  $result['type'] = ‪LinkService::TYPE_FILE;
232  $result['file'] = $fileOrFolderObject;
233  if ($fragment) {
234  $result['fragment'] = $fragment;
235  }
236  } elseif ($fileOrFolderObject instanceof ‪Folder) {
237  $result['type'] = ‪LinkService::TYPE_FOLDER;
238  $result['folder'] = $fileOrFolderObject;
239  if ($fragment) {
240  $result['fragment'] = $fragment;
241  }
242  } elseif (str_starts_with($mixedIdentifier, '/')) {
243  $result['type'] = ‪LinkService::TYPE_URL;
244  $result['url'] = $mixedIdentifier;
245  } else {
246  $result['type'] = ‪LinkService::TYPE_UNKNOWN;
247  $result['file'] = $mixedIdentifier;
248  }
249  } catch (\RuntimeException $e) {
250  // Element wasn't found
251  $result['type'] = ‪LinkService::TYPE_UNKNOWN;
252  $result['file'] = $mixedIdentifier;
253  } catch (‪ResourceDoesNotExistException $e) {
254  // Resource was not found
255  $result['type'] = ‪LinkService::TYPE_UNKNOWN;
256  $result['file'] = $mixedIdentifier;
257  }
258 
259  return $result;
260  }
261 
265  protected function ‪getResourceFactory(): ‪ResourceFactory
266  {
267  if (!$this->resourceFactory) {
268  $this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
269  }
271  }
272 }
‪TYPO3\CMS\Core\Core\Environment\getPublicPath
‪static getPublicPath()
Definition: Environment.php:187
‪TYPO3\CMS\Core\Resource\ResourceInterface\getStorage
‪getStorage()
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:37
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
Definition: ResourceDoesNotExistException.php:24
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Resource\ResourceInterface
Definition: ResourceInterface.php:21
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:51