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