‪TYPO3CMS  ‪main
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 
20 use Psr\EventDispatcher\EventDispatcherInterface;
27 
35 {
36  public const ‪TYPE_PAGE = 'page';
37  public const ‪TYPE_INPAGE = 'inpage';
38  public const ‪TYPE_URL = 'url';
39  public const ‪TYPE_EMAIL = 'email';
40  public const ‪TYPE_TELEPHONE = 'telephone';
41  public const ‪TYPE_FILE = 'file';
42  public const ‪TYPE_FOLDER = 'folder';
43  public const ‪TYPE_RECORD = 'record';
44  public const ‪TYPE_UNKNOWN = 'unknown';
45 
51  protected ‪$handlers;
52 
56  public function ‪__construct()
57  {
58  $registeredLinkHandlers = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'] ?? [];
59  $registeredLinkHandlers = is_array($registeredLinkHandlers) ? $registeredLinkHandlers : [];
61  if ($registeredLinkHandlers !== []) {
62  foreach ($registeredLinkHandlers as $type => $handlerClassName) {
63  if (!isset($this->handlers[$type]) || !is_object($this->handlers[$type])) {
64  $handler = GeneralUtility::makeInstance($handlerClassName);
65  if ($handler instanceof ‪LinkHandlingInterface) {
66  $this->handlers[$type] = $handler;
67  }
68  }
69  }
70  }
71  }
72 
89  public function ‪resolve(string $linkParameter): array
90  {
91  try {
92  // Check if the new syntax with "t3://" is used
93  return $this->‪resolveByStringRepresentation($linkParameter);
94  } catch (UnknownUrnException $e) {
95  $legacyLinkNotationConverter = GeneralUtility::makeInstance(LegacyLinkNotationConverter::class);
96  return $legacyLinkNotationConverter->resolve($linkParameter);
97  }
98  }
99 
106  public function ‪resolveByStringRepresentation(string $urn): array
107  {
108  $result = [];
109  $resolveException = null;
110  try {
111  // linking to any t3:// syntax
112  if (stripos($urn, 't3://') === 0) {
113  // lets parse the urn
114  $urnParsed = parse_url($urn);
115  $type = $urnParsed['host'];
116  if (isset($urnParsed['query'])) {
117  parse_str(htmlspecialchars_decode($urnParsed['query']), $data);
118  } else {
119  $data = [];
120  }
121  $fragment = $urnParsed['fragment'] ?? null;
122 
123  if (isset($this->handlers[$type])) {
124  $result = $this->handlers[$type]->resolveHandlerData($data);
125  $result['type'] = $type;
126  } else {
127  $resolveException = new UnknownLinkHandlerException('LinkHandler for ' . $type . ' was not registered', 1460581769);
128  }
129  // this was historically named "section"
130  if ($fragment) {
131  $result['fragment'] = $fragment;
132  }
133  } elseif (($this->handlers[self::TYPE_URL] ?? false) && ‪PathUtility::hasProtocolAndScheme($urn)) {
134  $result = $this->handlers[‪self::TYPE_URL]->resolveHandlerData(['url' => $urn]);
135  $result['type'] = ‪self::TYPE_URL;
136  } elseif (($this->handlers[self::TYPE_EMAIL] ?? false) && str_starts_with(strtolower($urn), 'mailto:')) {
137  $result = $this->handlers[‪self::TYPE_EMAIL]->resolveHandlerData(['email' => $urn]);
138  $result['type'] = ‪self::TYPE_EMAIL;
139  } elseif (($this->handlers[self::TYPE_TELEPHONE] ?? false) && str_starts_with(strtolower($urn), 'tel:')) {
140  $result = $this->handlers[‪self::TYPE_TELEPHONE]->resolveHandlerData(['telephone' => $urn]);
141  $result['type'] = ‪self::TYPE_TELEPHONE;
142  }
143  } finally {
144  $result = GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
145  new AfterLinkResolvedByStringRepresentationEvent(
146  result: $result,
147  urn: $urn,
148  resolveException: $resolveException
149  )
150  )->getResult();
151 
152  if (empty($result['type'])) {
153  // In case no link type could be resolved and UnknownLinkHandlerException
154  // has been added before, throw the exception now to inform calling components.
155  if ($resolveException === null) {
156  // Use the general UnknownUrnException in case neither a defined
157  // handler nor an event listener could resolve the given URN.
158  $resolveException = new UnknownUrnException('No valid URN to resolve found', 1457177667);
159  }
160 
161  throw $resolveException;
162  }
163  }
164 
165  // @todo If resolved result (linkDetails) are later used to build an uri using LinkBuilder->build(), it's needed
166  // to have the original $linkParameter in the result array. Otherwise, places may break like e.g. the
167  // DatabaseRecordLinkBuilder. Can we safely set this here directly and avoiding calls before build like
168  // "$linkDetails['typoLinkParameter'] = $redirectTarget;" - e.g. like in the ext:redirects
169  // TYPO3\CMS\Redirects\Service\RedirectService::resolveLinkDetailsFromLinkTarget() and other places.
170 
171  return $result;
172  }
173 
186  public function ‪asString(array $parameters): string
187  {
188  $linkHandler = $this->handlers[$parameters['type']] ?? null;
189  if ($linkHandler !== null) {
190  return $this->handlers[$parameters['type']]->asString($parameters);
191  }
192  if (isset($parameters['url']) && !empty($parameters['url'])) {
193  // This usually happens for tel: or other types where a URL is available and the
194  // legacy link service could resolve at least something
195  return $parameters['url'];
196  }
197  throw new UnknownLinkHandlerException('No valid handlers found for type: ' . $parameters['type'], 1460629247);
198  }
199 }
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪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 hasProtocolAndScheme(string $path)
Definition: PathUtility.php:445
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52