‪TYPO3CMS  ‪main
MarkerBasedTemplateService.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
21 
27 {
28  public function ‪__construct(
29  protected readonly ‪FrontendInterface $hashCache,
30  protected readonly ‪FrontendInterface $runtimeCache,
31  ) {}
32 
42  public function ‪getSubpart($content, $marker)
43  {
44  $start = strpos($content, $marker);
45  if ($start === false) {
46  return '';
47  }
48  $start += strlen($marker);
49  $stop = strpos($content, $marker, $start);
50  // Q: What shall get returned if no stop marker is given
51  // Everything till the end or nothing?
52  if ($stop === false) {
53  return '';
54  }
55  $content = substr($content, $start, $stop - $start);
56  $matches = [];
57  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $content, $matches) === 1) {
58  return $matches[2];
59  }
60  // Resetting $matches
61  $matches = [];
62  if (preg_match('/(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $content, $matches) === 1) {
63  return $matches[1];
64  }
65  // Resetting $matches
66  $matches = [];
67  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)$/s', $content, $matches) === 1) {
68  return $matches[2];
69  }
70 
71  return $content;
72  }
73 
85  public function ‪substituteSubpart($content, $marker, $subpartContent, $recursive = true, $keepMarker = false)
86  {
87  $start = strpos($content, $marker);
88  if ($start === false) {
89  return $content;
90  }
91  $startAM = $start + strlen($marker);
92  $stop = strpos($content, $marker, $startAM);
93  if ($stop === false) {
94  return $content;
95  }
96  $stopAM = $stop + strlen($marker);
97  $before = substr($content, 0, $start);
98  $after = substr($content, $stopAM);
99  $between = substr($content, $startAM, $stop - $startAM);
100  if ($recursive) {
101  $after = $this->‪substituteSubpart($after, $marker, $subpartContent, $recursive, $keepMarker);
102  }
103  if ($keepMarker) {
104  $matches = [];
105  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
106  $before .= $marker . $matches[1];
107  $between = $matches[2];
108  $after = $matches[3] . $marker . $after;
109  } elseif (preg_match('/^(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
110  $before .= $marker;
111  $between = $matches[1];
112  $after = $matches[2] . $marker . $after;
113  } elseif (preg_match('/^([^\\<]*\\-\\-\\>)(.*)$/s', $between, $matches) === 1) {
114  $before .= $marker . $matches[1];
115  $between = $matches[2];
116  $after = $marker . $after;
117  } else {
118  $before .= $marker;
119  $after = $marker . $after;
120  }
121  } else {
122  $matches = [];
123  if (preg_match('/^(.*)\\<\\!\\-\\-[^\\>]*$/s', $before, $matches) === 1) {
124  $before = $matches[1];
125  }
126  if (is_array($subpartContent)) {
127  $matches = [];
128  if (preg_match('/^([^\\<]*\\-\\-\\>)(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
129  $between = $matches[2];
130  } elseif (preg_match('/^(.*)(\\<\\!\\-\\-[^\\>]*)$/s', $between, $matches) === 1) {
131  $between = $matches[1];
132  } elseif (preg_match('/^([^\\<]*\\-\\-\\>)(.*)$/s', $between, $matches) === 1) {
133  $between = $matches[2];
134  }
135  }
136  $matches = [];
137  // resetting $matches
138  if (preg_match('/^[^\\<]*\\-\\-\\>(.*)$/s', $after, $matches) === 1) {
139  $after = $matches[1];
140  }
141  }
142  if (is_array($subpartContent)) {
143  $between = $subpartContent[0] . $between . $subpartContent[1];
144  } else {
145  $between = $subpartContent;
146  }
147 
148  return $before . $between . $after;
149  }
150 
159  public function ‪substituteSubpartArray($content, array $subpartsContent)
160  {
161  foreach ($subpartsContent as $subpartMarker => $subpartContent) {
162  $content = $this->‪substituteSubpart($content, $subpartMarker, $subpartContent);
163  }
164 
165  return $content;
166  }
167 
179  public function ‪substituteMarker($content, $marker, $markContent)
180  {
181  return str_replace($marker, $markContent, $content);
182  }
183 
204  public function ‪substituteMarkerArray($content, $markContentArray, $wrap = '', $uppercase = false, $deleteUnused = false)
205  {
206  if (is_array($markContentArray)) {
207  $wrapArr = ‪GeneralUtility::trimExplode('|', $wrap);
208  $search = [];
209  $replace = [];
210  foreach ($markContentArray as $marker => $markContent) {
211  if ($uppercase) {
212  // use strtr instead of strtoupper to avoid locale problems with Turkish
213  $marker = strtr($marker, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
214  }
215  if (isset($wrapArr[0], $wrapArr[1])) {
216  $marker = $wrapArr[0] . $marker . $wrapArr[1];
217  }
218  $search[] = $marker;
219  $replace[] = $markContent;
220  }
221  $content = str_replace($search, $replace, $content);
222  unset($search, $replace);
223  if ($deleteUnused) {
224  if (empty($wrap)) {
225  $wrapArr = ['###', '###'];
226  }
227  $content = preg_replace('/' . preg_quote($wrapArr[0], '/') . '([A-Z0-9_|\\-]*)' . preg_quote($wrapArr[1], '/') . '/is', '', $content);
228  }
229  }
230 
231  return $content;
232  }
233 
272  public function ‪substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = false, $deleteUnused = false)
273  {
274  $wraps = ‪GeneralUtility::trimExplode('|', $wrap);
275  $singleItems = [];
276  $compoundItems = [];
277  // Split markers and subparts into separate arrays
278  foreach ($markersAndSubparts as $markerName => $markerContent) {
279  if (is_array($markerContent)) {
280  $compoundItems[] = $markerName;
281  } else {
282  $singleItems[$markerName] = $markerContent;
283  }
284  }
285  $subTemplates = [];
286  $subpartSubstitutes = [];
287  // Build a cache for the sub template
288  foreach ($compoundItems as $subpartMarker) {
289  if ($uppercase) {
290  // Use strtr instead of strtoupper to avoid locale problems with Turkish
291  $subpartMarker = strtr($subpartMarker, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
292  }
293  if (isset($wraps[0], $wraps[1])) {
294  $subpartMarker = $wraps[0] . $subpartMarker . $wraps[1];
295  }
296  $subTemplates[$subpartMarker] = $this->‪getSubpart($content, $subpartMarker);
297  }
298  // Replace the subpart contents recursively
299  foreach ($compoundItems as $subpartMarker) {
300  $completeMarker = $subpartMarker;
301  if ($uppercase) {
302  // use strtr instead of strtoupper to avoid locale problems with Turkish
303  $completeMarker = strtr($completeMarker, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
304  }
305  if (isset($wraps[0], $wraps[1])) {
306  $completeMarker = $wraps[0] . $completeMarker . $wraps[1];
307  }
308  if (!empty($markersAndSubparts[$subpartMarker])) {
309  $subpartSubstitutes[$completeMarker] = '';
310  foreach ($markersAndSubparts[$subpartMarker] as $partialMarkersAndSubparts) {
311  $subpartSubstitutes[$completeMarker] .= $this->‪substituteMarkerAndSubpartArrayRecursive(
312  $subTemplates[$completeMarker],
313  $partialMarkersAndSubparts,
314  $wrap,
315  $uppercase,
316  $deleteUnused
317  );
318  }
319  } else {
320  $subpartSubstitutes[$completeMarker] = '';
321  }
322  }
323  // Substitute the single markers and subparts
324  $result = $this->‪substituteSubpartArray($content, $subpartSubstitutes);
325  $result = $this->‪substituteMarkerArray($result, $singleItems, $wrap, $uppercase, $deleteUnused);
326 
327  return $result;
328  }
329 
364  public function ‪substituteMarkerArrayCached($content, array $markContentArray = null, array $subpartContentArray = null, array $wrappedSubpartContentArray = null)
365  {
366  // If not arrays then set them
367  if ($markContentArray === null) {
368  // Plain markers
369  $markContentArray = [];
370  }
371  if ($subpartContentArray === null) {
372  // Subparts being directly substituted
373  $subpartContentArray = [];
374  }
375  if ($wrappedSubpartContentArray === null) {
376  // Subparts being wrapped
377  $wrappedSubpartContentArray = [];
378  }
379  // Finding keys and check hash:
380  $sPkeys = array_keys($subpartContentArray);
381  $wPkeys = array_keys($wrappedSubpartContentArray);
382  $keysToReplace = array_merge(array_keys($markContentArray), $sPkeys, $wPkeys);
383  if (empty($keysToReplace)) {
384  return $content;
385  }
386  asort($keysToReplace);
387  $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize([$content, $keysToReplace]));
388  $fromCache = $this->runtimeCache->get($storeKey);
389  if ($fromCache) {
390  $storeArr = $fromCache;
391  } else {
392  $storeArrDat = $this->hashCache->get($storeKey);
393  if (is_array($storeArrDat)) {
394  $storeArr = $storeArrDat;
395  // Setting the data in the first level cache
396  $this->runtimeCache->set($storeKey, $storeArr);
397  } else {
398  // Finding subparts and substituting them with the subpart as a marker
399  foreach ($sPkeys as $sPK) {
400  $content = $this->‪substituteSubpart($content, $sPK, $sPK);
401  }
402  // Finding subparts and wrapping them with markers
403  foreach ($wPkeys as $wPK) {
404  $content = $this->‪substituteSubpart($content, $wPK, [
405  $wPK,
406  $wPK,
407  ]);
408  }
409 
410  $storeArr = [];
411  // search all markers in the content
412  $result = preg_match_all('/###([^#](?:[^#]*+|#{1,2}[^#])+)###/', $content, $markersInContent);
413  if ($result !== false && !empty($markersInContent[1])) {
414  $keysToReplaceFlipped = array_flip($keysToReplace);
415  $regexKeys = [];
416  $wrappedKeys = [];
417  // Traverse keys and quote them for reg ex.
418  foreach ($markersInContent[1] as $key) {
419  if (isset($keysToReplaceFlipped['###' . $key . '###'])) {
420  $regexKeys[] = preg_quote($key, '/');
421  $wrappedKeys[] = '###' . $key . '###';
422  }
423  }
424  $regex = '/###(?:' . implode('|', $regexKeys) . ')###/';
425  $storeArr['c'] = preg_split($regex, $content); // contains all content parts around markers
426  $storeArr['k'] = $wrappedKeys; // contains all markers incl. ###
427  // Setting the data inside the second-level cache
428  $this->runtimeCache->set($storeKey, $storeArr);
429  // Storing the cached data permanently
430  $this->hashCache->set($storeKey, $storeArr, ['substMarkArrayCached'], 0);
431  }
432  }
433  }
434  if (!empty($storeArr['k']) && is_array($storeArr['k'])) {
435  // Substitution/Merging:
436  // Merging content types together, resetting
437  $valueArr = array_merge($markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
438  $wSCA_reg = [];
439  $content = '';
440  // Traversing the keyList array and merging the static and dynamic content
441  foreach ($storeArr['k'] as $n => $keyN) {
442  // add content before marker
443  $content .= $storeArr['c'][$n];
444  if (!is_array($valueArr[$keyN])) {
445  // fetch marker replacement from $markContentArray or $subpartContentArray
446  $content .= $valueArr[$keyN];
447  } else {
448  if (!isset($wSCA_reg[$keyN])) {
449  $wSCA_reg[$keyN] = 0;
450  }
451  // fetch marker replacement from $wrappedSubpartContentArray
452  $content .= $valueArr[$keyN][$wSCA_reg[$keyN] % 2];
453  $wSCA_reg[$keyN]++;
454  }
455  }
456  // add remaining content
457  $content .= $storeArr['c'][count($storeArr['k'])];
458  }
459  return $content;
460  }
461 
470  public function ‪substituteMarkerInObject(&$tree, array $markContentArray)
471  {
472  if (is_array($tree)) {
473  foreach ($tree as $key => $value) {
474  $this->‪substituteMarkerInObject($tree[$key], $markContentArray);
475  }
476  } else {
477  $tree = $this->‪substituteMarkerArray($tree, $markContentArray);
478  }
479  return $tree;
480  }
481 
495  public function ‪fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $htmlSpecialCharsValue = false, $respectXhtml = false)
496  {
497  if ($fieldList) {
498  $fArr = ‪GeneralUtility::trimExplode(',', $fieldList, true);
499  foreach ($fArr as $field) {
500  $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], $respectXhtml) : $row[$field];
501  }
502  } else {
503  if (is_array($row)) {
504  foreach ($row as $field => $value) {
506  if ($htmlSpecialCharsValue) {
507  $value = htmlspecialchars($value);
508  }
509  $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, $respectXhtml) : $value;
510  }
511  }
512  }
513  }
514  return $markContentArray;
515  }
516 }
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteMarkerInObject
‪mixed substituteMarkerInObject(&$tree, array $markContentArray)
Definition: MarkerBasedTemplateService.php:470
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\__construct
‪__construct(protected readonly FrontendInterface $hashCache, protected readonly FrontendInterface $runtimeCache,)
Definition: MarkerBasedTemplateService.php:28
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteMarkerAndSubpartArrayRecursive
‪string substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap='', $uppercase=false, $deleteUnused=false)
Definition: MarkerBasedTemplateService.php:272
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteMarkerArray
‪string substituteMarkerArray($content, $markContentArray, $wrap='', $uppercase=false, $deleteUnused=false)
Definition: MarkerBasedTemplateService.php:204
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteMarker
‪string substituteMarker($content, $marker, $markContent)
Definition: MarkerBasedTemplateService.php:179
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteMarkerArrayCached
‪string substituteMarkerArrayCached($content, array $markContentArray=null, array $subpartContentArray=null, array $wrappedSubpartContentArray=null)
Definition: MarkerBasedTemplateService.php:364
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteSubpartArray
‪string substituteSubpartArray($content, array $subpartsContent)
Definition: MarkerBasedTemplateService.php:159
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\fillInMarkerArray
‪array fillInMarkerArray(array $markContentArray, array $row, $fieldList='', $nl2br=true, $prefix='FIELD_', $htmlSpecialCharsValue=false, $respectXhtml=false)
Definition: MarkerBasedTemplateService.php:495
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\substituteSubpart
‪string substituteSubpart($content, $marker, $subpartContent, $recursive=true, $keepMarker=false)
Definition: MarkerBasedTemplateService.php:85
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Core\Service
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService\getSubpart
‪string getSubpart($content, $marker)
Definition: MarkerBasedTemplateService.php:42
‪TYPO3\CMS\Core\Service\MarkerBasedTemplateService
Definition: MarkerBasedTemplateService.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822