‪TYPO3CMS  11.5
LinkService.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 
25 
33 {
34  public const ‪TYPE_PAGE = 'page';
35  public const ‪TYPE_URL = 'url';
36  public const ‪TYPE_EMAIL = 'email';
37  public const ‪TYPE_TELEPHONE = 'telephone';
38  public const ‪TYPE_FILE = 'file';
39  public const ‪TYPE_FOLDER = 'folder';
40  public const ‪TYPE_RECORD = 'record';
41  public const ‪TYPE_UNKNOWN = 'unknown';
42 
48  protected ‪$handlers;
49 
53  public function ‪__construct()
54  {
55  $registeredLinkHandlers = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'] ?? [];
56  $registeredLinkHandlers = is_array($registeredLinkHandlers) ? $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 
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 
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] ?? null)) {
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 (($this->handlers[self::TYPE_URL] ?? false) && ‪PathUtility::hasProtocolAndScheme($urn)) {
130  $result = $this->handlers[‪self::TYPE_URL]->resolveHandlerData(['url' => $urn]);
131  $result['type'] = ‪self::TYPE_URL;
132  } elseif (($this->handlers[self::TYPE_EMAIL] ?? false) && str_starts_with(strtolower($urn), 'mailto:')) {
133  $result = $this->handlers[‪self::TYPE_EMAIL]->resolveHandlerData(['email' => $urn]);
134  $result['type'] = ‪self::TYPE_EMAIL;
135  } elseif (($this->handlers[self::TYPE_TELEPHONE] ?? false) && str_starts_with(strtolower($urn), 'tel:')) {
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  // @todo If resolved result (linkDetails) are later used to build an uri using LinkBuilder->build(), it's needed
153  // to have the original $linkParameter in the result array. Otherwise, places may break like e.g. the
154  // DatabaseRecordLinkBuilder. Can we safely set this here directly and avoiding calls before build like
155  // "$linkDetails['typoLinkParameter'] = $redirectTarget;" - e.g. like in the ext:redirects
156  // TYPO3\CMS\Redirects\Service\RedirectService::resolveLinkDetailsFromLinkTarget() and other places.
157 
158  return $result;
159  }
160 
174  public function ‪asString(array $parameters): string
175  {
176  $linkHandler = $this->handlers[$parameters['type']] ?? null;
177  if ($linkHandler !== null) {
178  return $this->handlers[$parameters['type']]->asString($parameters);
179  }
180  if (isset($parameters['url']) && !empty($parameters['url'])) {
181  // This usually happens for tel: or other types where a URL is available and the
182  // legacy link service could resolve at least something
183  return $parameters['url'];
184  }
185  throw new UnknownLinkHandlerException('No valid handlers found for type: ' . $parameters['type'], 1460629247);
186  }
187 }
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:25
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\PathUtility\hasProtocolAndScheme
‪static bool hasProtocolAndScheme(string $path)
Definition: PathUtility.php:463
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50