‪TYPO3CMS  ‪main
TypoLinkCodecService.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;
23 
27 final readonly class TypoLinkCodecService
28 {
32  private const DELIMITER = ' ';
33 
37  private const EMPTY_VALUE_SYMBOL = '-';
38 
39  public function __construct(private EventDispatcherInterface $eventDispatcher) {}
40 
47  public function encode(array $typoLinkParts): string
48  {
49  if (empty($typoLinkParts) || !isset($typoLinkParts['url'])) {
50  return '';
51  }
52 
53  // Get empty structure
54  $reverseSortedParameters = array_reverse($this->decode(''), true);
55  $aValueWasSet = false;
56  foreach ($reverseSortedParameters as $key => &$value) {
57  $value = $typoLinkParts[$key] ?? '';
58  // escape special character \ and "
59  $value = str_replace(['\\', '"'], ['\\\\', '\\"'], $value);
60  // enclose with quotes if a string contains the delimiter
61  if (str_contains($value, self::DELIMITER)) {
62  $value = '"' . $value . '"';
63  }
64  // fill with - if another values has already been set
65  if ($value === '' && $aValueWasSet) {
66  $value = self::EMPTY_VALUE_SYMBOL;
67  }
68  if ($value !== '') {
69  $aValueWasSet = true;
70  }
71  }
72 
73  $reverseSortedParameters = $this->eventDispatcher->dispatch(
74  new BeforeTypoLinkEncodedEvent(
75  parameters: $reverseSortedParameters,
76  typoLinkParts: $typoLinkParts,
77  delimiter: self::DELIMITER,
78  emptyValueSymbol: self::EMPTY_VALUE_SYMBOL
79  )
80  )->getParameters();
81 
82  return trim(implode(self::DELIMITER, array_reverse($reverseSortedParameters, true)));
83  }
84 
91  public function decode(string $typoLink): array
92  {
93  $typoLink = trim($typoLink);
94  if ($typoLink !== '') {
95  $parts = str_replace(['\\\\', '\\"'], ['\\', '"'], str_getcsv($typoLink, self::DELIMITER));
96  } else {
97  $parts = [];
98  }
99 
100  // The order of the entries is crucial!!
101  $typoLinkParts = [
102  'url' => isset($parts[0]) ? trim($parts[0]) : '',
103  'target' => isset($parts[1]) && $parts[1] !== self::EMPTY_VALUE_SYMBOL ? trim($parts[1]) : '',
104  'class' => isset($parts[2]) && $parts[2] !== self::EMPTY_VALUE_SYMBOL ? trim($parts[2]) : '',
105  'title' => isset($parts[3]) && $parts[3] !== self::EMPTY_VALUE_SYMBOL ? trim($parts[3]) : '',
106  'additionalParams' => isset($parts[4]) && $parts[4] !== self::EMPTY_VALUE_SYMBOL ? trim($parts[4]) : '',
107  ];
108 
109  return $this->eventDispatcher->dispatch(
110  new AfterTypoLinkDecodedEvent(
111  typoLinkParts: $typoLinkParts,
112  typoLink: $typoLink,
113  delimiter: self::DELIMITER,
114  emptyValueSymbol: self::EMPTY_VALUE_SYMBOL
115  )
116  )->getTypoLinkParts();
117  }
118 }