‪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 
25 
33 {
34  public const ‪TYPE_PAGE = 'page';
35  public const ‪TYPE_INPAGE = 'inpage';
36  public const ‪TYPE_URL = 'url';
37  public const ‪TYPE_EMAIL = 'email';
38  public const ‪TYPE_TELEPHONE = 'telephone';
39  public const ‪TYPE_FILE = 'file';
40  public const ‪TYPE_FOLDER = 'folder';
41  public const ‪TYPE_RECORD = 'record';
42  public const ‪TYPE_UNKNOWN = 'unknown';
43 
49  protected ‪$handlers;
50 
54  public function ‪__construct()
55  {
56  $registeredLinkHandlers = ‪$GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'] ?? [];
57  $registeredLinkHandlers = is_array($registeredLinkHandlers) ? $registeredLinkHandlers : [];
59  if ($registeredLinkHandlers !== []) {
60  foreach ($registeredLinkHandlers as $type => $handlerClassName) {
61  if (!isset($this->handlers[$type]) || !is_object($this->handlers[$type])) {
62  $handler = GeneralUtility::makeInstance($handlerClassName);
63  if ($handler instanceof ‪LinkHandlingInterface) {
64  $this->handlers[$type] = $handler;
65  }
66  }
67  }
68  }
69  }
70 
87  public function ‪resolve(string $linkParameter): array
88  {
89  try {
90  // Check if the new syntax with "t3://" is used
91  return $this->‪resolveByStringRepresentation($linkParameter);
92  } catch (UnknownUrnException $e) {
93  $legacyLinkNotationConverter = GeneralUtility::makeInstance(LegacyLinkNotationConverter::class);
94  return $legacyLinkNotationConverter->resolve($linkParameter);
95  }
96  }
97 
104  public function ‪resolveByStringRepresentation(string $urn): array
105  {
106  // linking to any t3:// syntax
107  if (stripos($urn, 't3://') === 0) {
108  // lets parse the urn
109  $urnParsed = parse_url($urn);
110  $type = $urnParsed['host'];
111  if (isset($urnParsed['query'])) {
112  parse_str(htmlspecialchars_decode($urnParsed['query']), $data);
113  } else {
114  $data = [];
115  }
116  $fragment = $urnParsed['fragment'] ?? null;
117 
118  if (isset($this->handlers[$type])) {
119  $result = $this->handlers[$type]->resolveHandlerData($data);
120  $result['type'] = $type;
121  } else {
122  throw new UnknownLinkHandlerException('LinkHandler for ' . $type . ' was not registered', 1460581769);
123  }
124  // this was historically named "section"
125  if ($fragment) {
126  $result['fragment'] = $fragment;
127  }
128  } elseif (($this->handlers[self::TYPE_URL] ?? false) && ‪PathUtility::hasProtocolAndScheme($urn)) {
129  $result = $this->handlers[‪self::TYPE_URL]->resolveHandlerData(['url' => $urn]);
130  $result['type'] = ‪self::TYPE_URL;
131  } elseif (($this->handlers[self::TYPE_EMAIL] ?? false) && str_starts_with(strtolower($urn), 'mailto:')) {
132  $result = $this->handlers[‪self::TYPE_EMAIL]->resolveHandlerData(['email' => $urn]);
133  $result['type'] = ‪self::TYPE_EMAIL;
134  } elseif (($this->handlers[self::TYPE_TELEPHONE] ?? false) && str_starts_with(strtolower($urn), 'tel:')) {
135  $result = $this->handlers[‪self::TYPE_TELEPHONE]->resolveHandlerData(['telephone' => $urn]);
136  $result['type'] = ‪self::TYPE_TELEPHONE;
137  } else {
138  $result = [];
139  if (is_array(‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Link']['resolveByStringRepresentation'] ?? null)) {
140  $params = ['urn' => $urn, 'result' => &$result];
141  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Link']['resolveByStringRepresentation'] as $hookMethod) {
142  $fakeThis = null;
143  GeneralUtility::callUserFunction($hookMethod, $params, $fakeThis);
144  }
145  }
146  if (empty($result) || empty($result['type'])) {
147  throw new UnknownUrnException('No valid URN to resolve found', 1457177667);
148  }
149  }
150 
151  // @todo If resolved result (linkDetails) are later used to build an uri using LinkBuilder->build(), it's needed
152  // to have the original $linkParameter in the result array. Otherwise, places may break like e.g. the
153  // DatabaseRecordLinkBuilder. Can we safely set this here directly and avoiding calls before build like
154  // "$linkDetails['typoLinkParameter'] = $redirectTarget;" - e.g. like in the ext:redirects
155  // TYPO3\CMS\Redirects\Service\RedirectService::resolveLinkDetailsFromLinkTarget() and other places.
156 
157  return $result;
158  }
159 
172  public function ‪asString(array $parameters): string
173  {
174  $linkHandler = $this->handlers[$parameters['type']] ?? null;
175  if ($linkHandler !== null) {
176  return $this->handlers[$parameters['type']]->asString($parameters);
177  }
178  if (isset($parameters['url']) && !empty($parameters['url'])) {
179  // This usually happens for tel: or other types where a URL is available and the
180  // legacy link service could resolve at least something
181  return $parameters['url'];
182  }
183  throw new UnknownLinkHandlerException('No valid handlers found for type: ' . $parameters['type'], 1460629247);
184  }
185 }
‪TYPO3\CMS\Core\Utility\PathUtility
Definition: PathUtility.php:27
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:23
‪$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:51