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