‪TYPO3CMS  ‪main
AbstractAstBuilder.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;
34 
40 abstract class ‪AbstractAstBuilder
41 {
45  protected array ‪$flatConstants = [];
46  protected EventDispatcherInterface ‪$eventDispatcher;
47 
48  protected function ‪handleIdentifierUnsetLine(‪IdentifierUnsetLine $line, ‪CurrentObjectPath $currentObjectPath): void
49  {
50  $node = $currentObjectPath->‪getFirst();
51  $identifierStream = $line->‪getIdentifierTokenStream()->reset();
52  while ($identifierToken = $identifierStream->getNext()) {
53  if (!$foundNode = $node->getChildByName($identifierToken->getValue())) {
54  break;
55  }
56  $nextIdentifierToken = $identifierStream->peekNext();
57  if ($nextIdentifierToken) {
58  $node = $foundNode;
59  continue;
60  }
61  $node->removeChildByName($identifierToken->getValue());
62  break;
63  }
64  }
65 
66  protected function ‪handleIdentifierCopyLine(‪IdentifierCopyLine $line, ‪RootNode $rootNode, ‪CurrentObjectPath $currentObjectPath): ?‪NodeInterface
67  {
68  $sourceIdentifierStream = $line->‪getValueTokenStream()->reset();
69  $sourceNode = $rootNode;
70  if ($sourceIdentifierStream->isRelative()) {
71  // Entry node is current node from current object path if relative, otherwise RootNode.
72  $sourceNode = $currentObjectPath->‪getLast();
73  }
74  while ($identifierToken = $sourceIdentifierStream->getNext()) {
75  // Go through source token stream and locate the sourceNode to copy from.
76  if (!$sourceNode = $sourceNode->getChildByName($identifierToken->getValue())) {
77  // Source node not found - nothing to do for this line
78  return null;
79  }
80  }
81  $isSourceNodeValueNull = true;
82  if ($sourceNode->getValue() !== null) {
83  // When the source node value is not null, it will override the target node value if that exists.
84  $isSourceNodeValueNull = false;
85  }
86 
87  // Locate/create the targets parent node the copied source should be added as child to,
88  // and get the name of the node we're dealing with.
89  $targetIdentifierTokenStream = $line->‪getIdentifierTokenStream()->reset();
90  $targetParentNode = $currentObjectPath->‪getFirst();
91  $targetTokenName = null;
92  while ($targetToken = $targetIdentifierTokenStream->getNext()) {
93  $targetTokenName = $targetToken->getValue();
94  if (!($targetIdentifierTokenStream->peekNext() ?? false)) {
95  break;
96  }
97  if (!$foundNode = $targetParentNode->getChildByName($targetTokenName)) {
98  // Add new node as new child of current last element in path
99  $foundNode = new ‪ChildNode($targetTokenName);
100  $targetParentNode->addChild($foundNode);
101  }
102  $targetParentNode = $foundNode;
103  }
104 
105  $existingTarget = null;
106  if ($isSourceNodeValueNull) {
107  // When the node to copy has no value, but the existing target has,
108  // the value from the existing target is kept. Also, if the existing
109  // node is a ReferenceChildNode and the source does not override this,
110  // source children are added to the existing reference instead of
111  // dropping the existing target.
112  $existingTarget = $targetParentNode->getChildByName($targetTokenName);
113  $existingTargetNodeValue = $existingTarget?->getValue();
114  } else {
115  // Blindly remove existing target node if exists and the value is not overridden by source.
116  $targetParentNode->removeChildByName($targetTokenName);
117  }
118  if ($existingTarget instanceof ‪ReferenceChildNode) {
119  // When existing target is a ReferenceChildNode, keep it and
120  // copy children from source into existing target.
121  $targetNode = $existingTarget;
122  foreach ($sourceNode->getNextChild() as $sourceChild) {
123  $targetNode->addChild(clone $sourceChild);
124  }
125  } else {
126  // Clone full source node tree, update name and add as child to parent node.
128  $targetNode = clone $sourceNode;
129  $targetNode->updateName($targetTokenName);
130  $targetParentNode->addChild($targetNode);
131  }
132  if ($isSourceNodeValueNull && $existingTargetNodeValue) {
133  // If value of old existing target should be kept, set in now.
134  $targetNode->setValue($existingTargetNodeValue);
135  }
136 
137  return $targetNode;
138  }
139 
151  {
152  $tokenStream = $line->‪getIdentifierTokenStream();
153  $node = $currentObjectPath->‪getFirst();
154  $identifierStream = $tokenStream->reset();
155  while ($identifierToken = $identifierStream->getNext()) {
156  $nextIdentifier = $identifierStream->peekNext();
157  $identifierTokenValue = $identifierToken->getValue();
158  if (!($node->getChildByName($identifierTokenValue)) && $nextIdentifier) {
159  // Add new node as new child of current last element in path
160  $foundNode = new ‪ChildNode($identifierTokenValue);
161  $node->addChild($foundNode);
162  } elseif (!$node->getChildByName($identifierTokenValue) && $nextIdentifier === null) {
163  // Parent of target node exists, but target node does not. Add new reference child.
164  $foundNode = new ‪ReferenceChildNode($identifierTokenValue);
165  $foundNode->setReferenceSourceStream($line->‪getValueTokenStream());
166  $node->addChild($foundNode);
167  } elseif (($foundNode = $node->getChildByName($identifierTokenValue)) && $nextIdentifier === null) {
168  // Target node exists already. We create a new one, remove old, but transfer existing children from old to new.
169  $newNode = new ‪ReferenceChildNode($identifierTokenValue);
170  $newNode->setReferenceSourceStream($line->‪getValueTokenStream());
171  foreach ($foundNode->getNextChild() as $existingNodeChild) {
172  $newNode->addChild($existingNodeChild);
173  }
174  $node->removeChildByName($identifierTokenValue);
175  $node->addChild($newNode);
176  $foundNode = $newNode;
177  }
178  $node = $foundNode;
179  }
180  return $node;
181  }
182 
184  {
185  $node = $currentObjectPath->‪getFirst();
186  $identifierStream = $tokenStream->‪reset();
187  while ($identifierToken = $identifierStream->getNext()) {
188  $identifierTokenValue = $identifierToken->getValue();
189  if (!$foundNode = $node->getChildByName($identifierTokenValue)) {
190  // Add new node as new child of current last element in path
191  $foundNode = new ‪ChildNode($identifierTokenValue);
192  $node->addChild($foundNode);
193  }
194  $node = $foundNode;
195  }
196  return $node;
197  }
198 
203  protected function ‪evaluateValueModifier(‪Token $functionNameToken, ?‪Token $functionArgumentToken, ?string $originalValue): ?string
204  {
205  $functionName = $functionNameToken->‪getValue();
206  $functionArgument = null;
207  if ($functionArgumentToken) {
208  $functionArgument = $functionArgumentToken->‪getValue();
209  }
210  switch ($functionName) {
211  case 'prependString':
212  return $functionArgument . $originalValue;
213  case 'appendString':
214  return $originalValue . $functionArgument;
215  case 'removeString':
216  return str_replace((string)$functionArgument, '', $originalValue);
217  case 'replaceString':
218  $functionValueArray = explode('|', (string)$functionArgument, 2);
219  $fromStr = $functionValueArray[0] ?? '';
220  $toStr = $functionValueArray[1] ?? '';
221  return str_replace($fromStr, $toStr, $originalValue);
222  case 'addToList':
223  return ($originalValue !== null ? $originalValue . ',' : '') . $functionArgument;
224  case 'removeFromList':
225  $existingElements = ‪GeneralUtility::trimExplode(',', $originalValue);
226  $removeElements = ‪GeneralUtility::trimExplode(',', (string)$functionArgument);
227  if (!empty($removeElements)) {
228  return implode(',', array_diff($existingElements, $removeElements));
229  }
230  return $originalValue;
231  case 'uniqueList':
232  $elements = ‪GeneralUtility::trimExplode(',', $originalValue);
233  return implode(',', array_unique($elements));
234  case 'reverseList':
235  $elements = ‪GeneralUtility::trimExplode(',', $originalValue);
236  return implode(',', array_reverse($elements));
237  case 'sortList':
238  $elements = ‪GeneralUtility::trimExplode(',', $originalValue);
239  $arguments = ‪GeneralUtility::trimExplode(',', (string)$functionArgument);
240  $arguments = array_map('strtolower', $arguments);
241  $sortFlags = SORT_REGULAR;
242  if (in_array('numeric', $arguments)) {
243  $sortFlags = SORT_NUMERIC;
244  // If the sorting modifier "numeric" is given, all values
245  // are checked and an exception is thrown if a non-numeric value is given
246  // otherwise there is a different behaviour between PHP 7 and PHP 5.x
247  // See also the warning on http://us.php.net/manual/en/function.sort.php
248  foreach ($elements as $element) {
249  if (!is_numeric($element)) {
250  throw new \InvalidArgumentException(
251  'The list "' . $originalValue . '" should be sorted numerically but contains a non-numeric value',
252  1650893781
253  );
254  }
255  }
256  }
257  sort($elements, $sortFlags);
258  if (in_array('descending', $arguments)) {
259  $elements = array_reverse($elements);
260  }
261  return implode(',', $elements);
262  case 'getEnv':
263  $environmentValue = getenv(trim((string)$functionArgument));
264  if ($environmentValue !== false) {
265  return $environmentValue;
266  }
267  return $originalValue;
268  default:
269  return $this->eventDispatcher->dispatch(new ‪EvaluateModifierFunctionEvent($functionName, $functionArgument, $originalValue))->getValue() ?? $originalValue;
270  }
271  }
272 }
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\handleIdentifierReferenceLine
‪handleIdentifierReferenceLine(IdentifierReferenceLine $line, CurrentObjectPath $currentObjectPath)
Definition: AbstractAstBuilder.php:150
‪TYPO3\CMS\Core\TypoScript\AST\CurrentObjectPath\CurrentObjectPath\getFirst
‪getFirst()
Definition: CurrentObjectPath.php:80
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierUnsetLine
Definition: IdentifierUnsetLine.php:31
‪TYPO3\CMS\Core\TypoScript\AST\Node\ReferenceChildNode
Definition: ReferenceChildNode.php:39
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Token\AbstractTokenStream\reset
‪reset()
Definition: AbstractTokenStream.php:83
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\getOrAddNodeFromIdentifierStream
‪getOrAddNodeFromIdentifierStream(CurrentObjectPath $currentObjectPath, IdentifierTokenStream $tokenStream)
Definition: AbstractAstBuilder.php:183
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Token\Token
Definition: Token.php:29
‪TYPO3\CMS\Core\TypoScript\AST\Event\EvaluateModifierFunctionEvent
Definition: EvaluateModifierFunctionEvent.php:30
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierCopyLine
Definition: IdentifierCopyLine.php:37
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierUnsetLine\getIdentifierTokenStream
‪getIdentifierTokenStream()
Definition: IdentifierUnsetLine.php:43
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierReferenceLine\getIdentifierTokenStream
‪getIdentifierTokenStream()
Definition: IdentifierReferenceLine.php:48
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\$flatConstants
‪array $flatConstants
Definition: AbstractAstBuilder.php:45
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Token\IdentifierTokenStream
Definition: IdentifierTokenStream.php:44
‪TYPO3\CMS\Core\TypoScript\AST\Node\NodeInterface
Definition: NodeInterface.php:35
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\evaluateValueModifier
‪evaluateValueModifier(Token $functionNameToken, ?Token $functionArgumentToken, ?string $originalValue)
Definition: AbstractAstBuilder.php:203
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierReferenceLine\getValueTokenStream
‪getValueTokenStream()
Definition: IdentifierReferenceLine.php:62
‪TYPO3\CMS\Core\TypoScript\AST\Node\ChildNodeInterface
Definition: ChildNodeInterface.php:26
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierCopyLine\getValueTokenStream
‪getValueTokenStream()
Definition: IdentifierCopyLine.php:64
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder
Definition: AbstractAstBuilder.php:41
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierReferenceLine
Definition: IdentifierReferenceLine.php:35
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\$eventDispatcher
‪EventDispatcherInterface $eventDispatcher
Definition: AbstractAstBuilder.php:46
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\handleIdentifierUnsetLine
‪handleIdentifierUnsetLine(IdentifierUnsetLine $line, CurrentObjectPath $currentObjectPath)
Definition: AbstractAstBuilder.php:48
‪TYPO3\CMS\Core\TypoScript\AST\CurrentObjectPath\CurrentObjectPath
Definition: CurrentObjectPath.php:32
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Line\IdentifierCopyLine\getIdentifierTokenStream
‪getIdentifierTokenStream()
Definition: IdentifierCopyLine.php:50
‪TYPO3\CMS\Core\TypoScript\AST\AbstractAstBuilder\handleIdentifierCopyLine
‪handleIdentifierCopyLine(IdentifierCopyLine $line, RootNode $rootNode, CurrentObjectPath $currentObjectPath)
Definition: AbstractAstBuilder.php:66
‪TYPO3\CMS\Core\TypoScript\AST\Node\ChildNode
Definition: ChildNode.php:23
‪TYPO3\CMS\Core\TypoScript\AST\Node\RootNode
Definition: RootNode.php:26
‪TYPO3\CMS\Core\TypoScript\Tokenizer\Token\AbstractToken\getValue
‪getValue()
Definition: AbstractToken.php:65
‪TYPO3\CMS\Core\TypoScript\AST
Definition: AbstractAstBuilder.php:18
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\TypoScript\AST\CurrentObjectPath\CurrentObjectPath\getLast
‪getLast()
Definition: CurrentObjectPath.php:85
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822