Passed
Pull Request — main (#3)
by Peter
05:56 queued 03:02
created

Template::addOccurrence()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
c 0
b 0
f 0
nc 4
nop 5
dl 0
loc 25
rs 9.9666
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Framework\Template;
6
7
use AbterPhp\Framework\Html\Attributes;
8
9
class Template
10
{
11
    public const TYPE_BLOCK = 'block';
12
13
    private const REGEXP_ATTRIBUTES = '/\s*([\w_\-]*)\s*\=\s*\"([^"]*)\"\s*/Ums';
14
    private const REGEXP_TEMPLATES  = '/\{\{\s*(TYPES)\/([\w-]+)(\s+[^}]*)?\s*\}\}/Ums';
15
    private const REGEXP_VARIABLES  = '/{{\s*var\/([\w-]+)\s*}}/';
16
17
    protected string $rawContent = '';
18
19
    /** @var array<string,array<string,ParsedTemplate[]>> */
20
    protected array $parsedTemplates = [];
21
22
    /** @var array<string,string> */
23
    protected array $vars = [];
24
25
    /** @var string[] */
26
    protected array $types = [self::TYPE_BLOCK];
27
28
    /**
29
     * Template constructor.
30
     *
31
     * @param string   $rawContent
32
     * @param string[] $vars
33
     * @param string[] $types
34
     */
35
    public function __construct(string $rawContent = '', array $vars = [], array $types = [self::TYPE_BLOCK])
36
    {
37
        $this->rawContent = $rawContent;
38
        $this->vars       = $vars;
39
        $this->types      = $types;
40
    }
41
42
    /**
43
     * @param string $rawContent
44
     *
45
     * @return Template
46
     */
47
    public function setRawContent(string $rawContent): Template
48
    {
49
        $this->rawContent = $rawContent;
50
51
        return $this;
52
    }
53
54
    /**
55
     * @param string[] $vars
56
     *
57
     * @return Template
58
     */
59
    public function setVars(array $vars): Template
60
    {
61
        $this->vars = $vars;
62
63
        return $this;
64
    }
65
66
    /**
67
     * @param string[] $types
68
     *
69
     * @return Template
70
     */
71
    public function setTypes(array $types): Template
72
    {
73
        $this->types = $types;
74
75
        return $this;
76
    }
77
78
    /**
79
     * @return ParsedTemplate[][][]
80
     */
81
    public function parse(): array
82
    {
83
        if (!$this->rawContent) {
84
            return [];
85
        }
86
87
        $this->replaceVars();
88
89
        $this->parsedTemplates = $this->parseTemplates();
90
91
        return $this->parsedTemplates;
92
    }
93
94
    /**
95
     * Replaces is {{var/xxx}} occurrences in the content
96
     */
97
    private function replaceVars(): void
98
    {
99
        $matches = [];
100
        preg_match_all(self::REGEXP_VARIABLES, $this->rawContent, $matches);
101
102
        foreach ($matches[1] as $idx => $varName) {
103
            $search      = $matches[0][$idx];
104
            $replaceWith = '';
105
            if (array_key_exists($varName, $this->vars)) {
106
                $replaceWith = $this->vars[$varName];
107
            }
108
109
            $this->rawContent = str_replace($search, $replaceWith, $this->rawContent);
110
        }
111
    }
112
113
    /**
114
     * @return array<string,ParsedTemplate[][]>
115
     */
116
    private function parseTemplates(): array
117
    {
118
        $matches = [];
119
        $pattern = str_replace('TYPES', implode('|', $this->types), self::REGEXP_TEMPLATES);
120
        preg_match_all($pattern, $this->rawContent, $matches);
121
122
        $parsedTemplates = [];
123
        foreach ($matches[0] as $idx => $occurrence) {
124
            $type       = $matches[1][$idx];
125
            $identifier = $matches[2][$idx];
126
            $attributes = $this->parseAttributes($matches[3][$idx]);
127
128
            $this->addOccurrence($parsedTemplates, $type, $identifier, $attributes, $occurrence);
129
        }
130
131
        return $parsedTemplates;
132
    }
133
134
    /**
135
     * @param string $rawAttributes
136
     *
137
     * @return Attributes|null
138
     */
139
    private function parseAttributes(string $rawAttributes): ?Attributes
140
    {
141
        if (trim($rawAttributes) === '') {
142
            return null;
143
        }
144
145
        $matches = [];
146
        preg_match_all(self::REGEXP_ATTRIBUTES, $rawAttributes, $matches);
147
148
        $attributes = [];
149
        foreach (array_keys($matches[0]) as $idx) {
150
            $attributes[$matches[1][$idx]] = $matches[2][$idx];
151
        }
152
153
        return new Attributes($attributes);
154
    }
155
156
    /**
157
     * @param ParsedTemplate[][][] &$parsedTemplates
158
     * @param string                $type
159
     * @param string                $identifier
160
     * @param Attributes|null       $attributes
161
     * @param string                $occurrence
162
     */
163
    private function addOccurrence(
164
        array &$parsedTemplates,
165
        string $type,
166
        string $identifier,
167
        ?Attributes $attributes,
168
        string $occurrence
169
    ): void {
170
        if (!isset($parsedTemplates[$type][$identifier])) {
171
            $parsedTemplates[$type][$identifier][] = new ParsedTemplate($type, $identifier, $attributes, [$occurrence]);
172
173
            return;
174
        }
175
176
        foreach ($parsedTemplates[$type][$identifier] as $parsedTemplate) {
177
            // Note: == is used on purpose here!
178
            if (!$parsedTemplate->getAttributes()->isEqual($attributes)) {
179
                continue;
180
            }
181
182
            $parsedTemplate->addOccurrence($occurrence);
183
184
            return;
185
        }
186
187
        $parsedTemplates[$type][$identifier][] = new ParsedTemplate($type, $identifier, $attributes, [$occurrence]);
188
    }
189
190
    /**
191
     * @param string[][] $subTemplateValues
192
     *
193
     * @return string
194
     */
195
    public function render(array $subTemplateValues): string
196
    {
197
        $content = $this->rawContent;
198
199
        foreach ($this->parsedTemplates as $type => $typeTemplates) {
200
            if (!array_key_exists($type, $subTemplateValues)) {
201
                $subTemplateValues[$type] = [];
202
            }
203
            foreach ($typeTemplates as $identifier => $identifierTemplates) {
204
                $replace = '';
205
                if (array_key_exists($identifier, $subTemplateValues[$type])) {
206
                    $replace = $subTemplateValues[$type][$identifier];
207
                }
208
209
                foreach ($identifierTemplates as $parsedTemplate) {
210
                    $content = str_replace($parsedTemplate->getOccurrences(), $replace, $content);
211
                }
212
            }
213
        }
214
215
        return $content;
216
    }
217
}
218