• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CPS-IT / handlebars / 19131581028

06 Nov 2025 09:50AM UTC coverage: 86.907% (-4.6%) from 91.508%
19131581028

Pull #486

github

web-flow
Merge a04950bb3 into c6edffed2
Pull Request #486: [FEATURE] Introduce data processor to replace markers in processed data

0 of 53 new or added lines in 3 files covered. (0.0%)

916 of 1054 relevant lines covered (86.91%)

5.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Classes/Renderer/Variables/MarkerBasedValueProcessor.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "handlebars".
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

18
namespace CPSIT\Typo3Handlebars\Renderer\Variables;
19

20
use CPSIT\Typo3Handlebars\Exception;
21

22
/**
23
 * MarkerBasedValueProcessor
24
 *
25
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
26
 * @license GPL-2.0-or-later
27
 * @internal
28
 */
29
final class MarkerBasedValueProcessor
30
{
31
    private const DEFAULT_PATTERN = '/###(\w+)###/';
32

33
    /**
34
     * @var array<string, string|null>
35
     */
36
    private static array $markerTemplateCache = [];
37

38
    /**
39
     * @var array<string, mixed>
40
     */
41
    private array $markerValues = [];
42

43
    /**
44
     * @var array<string, list<mixed>>
45
     */
46
    private array $replacementTargets = [];
47

48
    /**
49
     * @param non-empty-string $markerPattern
50
     */
NEW
51
    private function __construct(
×
52
        private readonly string $markerPattern,
NEW
53
    ) {}
×
54

55
    /**
56
     * @param non-empty-string|null $markerPattern
57
     * @throws Exception\MarkerPatternIsInvalid
58
     */
NEW
59
    public static function create(?string $markerPattern): self
×
60
    {
NEW
61
        if ($markerPattern !== null && !self::isValidPattern($markerPattern)) {
×
NEW
62
            throw new Exception\MarkerPatternIsInvalid($markerPattern);
×
63
        }
64

NEW
65
        return new self($markerPattern ?? self::DEFAULT_PATTERN);
×
66
    }
67

NEW
68
    private static function isValidPattern(string $markerPattern): bool
×
69
    {
NEW
70
        return @preg_match($markerPattern, '') !== false;
×
71
    }
72

73
    /**
74
     * @param array<array-key, mixed> $values
75
     */
NEW
76
    public function replaceMarkers(array &$values, bool $removeNonMatchingMarkers = false): int
×
77
    {
NEW
78
        $this->collectReferencesRecursively($values);
×
79

NEW
80
        $replacedMarkerValues = 0;
×
81

NEW
82
        foreach ($this->replacementTargets as $marker => &$targets) {
×
NEW
83
            $removeTarget = false;
×
84

NEW
85
            if (!isset($this->markerValues[$marker])) {
×
NEW
86
                if ($removeNonMatchingMarkers) {
×
NEW
87
                    $removeTarget = true;
×
88
                } else {
NEW
89
                    continue;
×
90
                }
91
            }
92

NEW
93
            foreach ($targets as &$target) {
×
NEW
94
                if ($removeTarget) {
×
NEW
95
                    unset($target);
×
NEW
96
                    continue;
×
97
                }
98

NEW
99
                [&$parent, $key, $value] = $this->markerValues[$marker];
×
NEW
100
                $target = $value;
×
NEW
101
                $replacedMarkerValues++;
×
NEW
102
                unset($parent[$key]);
×
103
            }
104
        }
105

NEW
106
        return $replacedMarkerValues;
×
107
    }
108

109
    /**
110
     * @param array<array-key, mixed> $values
111
     */
NEW
112
    private function collectReferencesRecursively(array &$values): void
×
113
    {
NEW
114
        foreach ($values as $key => &$value) {
×
NEW
115
            $this->collectReferences($value, $key, $values);
×
116
        }
117
    }
118

119
    /**
120
     * @param array<array-key, mixed> $parent
121
     */
NEW
122
    private function collectReferences(mixed &$value, string|int $key, array &$parent): void
×
123
    {
NEW
124
        if (\is_array($value)) {
×
NEW
125
            $this->collectReferencesRecursively($value);
×
126
        }
NEW
127
        if (\is_string($key) && ($marker = $this->resolveMarker($key)) !== null) {
×
NEW
128
            $this->markerValues[$marker] = [&$parent, $key, $value];
×
129
        }
NEW
130
        if (\is_string($value) && ($marker = $this->resolveMarker($value)) !== null) {
×
NEW
131
            $this->replacementTargets[$marker] ??= [];
×
NEW
132
            $this->replacementTargets[$marker][] = &$value;
×
133
        }
134
    }
135

NEW
136
    private function resolveMarker(string $value): ?string
×
137
    {
NEW
138
        if (\array_key_exists($value, self::$markerTemplateCache)) {
×
NEW
139
            return self::$markerTemplateCache[$value];
×
140
        }
141

NEW
142
        preg_match($this->markerPattern, $value, $matches);
×
143

NEW
144
        return self::$markerTemplateCache[$value] = $matches[1] ?? null;
×
145
    }
146
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc