Block::__clone()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.2
c 0
b 0
f 0
cc 4
eloc 4
nc 8
nop 0
1
<?php
2
3
/**
4
 * @package Cadmium\Framework\Template
5
 * @author Anton Romanov
6
 * @copyright Copyright (c) 2015-2017, Anton Romanov
7
 * @link http://cadmium-cms.com
8
 */
9
10
namespace Template {
11
12
	use Language, Str, Template;
13
14
	class Block extends Group {
15
16
		private $contents = '', $enabled = true;
17
18
		private $blocks = [], $loops = [], $widgets = [], $variables = [], $phrases = [];
19
20
		/**
21
		 * Set an empty template widget if was not set before
22
		 */
23
24
		private function registerWidget(string $name) {
25
26
			if (false === Template::getWidget($name)) Template::setWidget($name, new Block);
27
		}
28
29
		/**
30
		 * Parse structures
31
		 */
32
33
		private function parseStructures() {
34
35
			preg_match_all(REGEX_TEMPLATE_STRUCTURE, $this->contents, $matches);
36
37
			foreach ($matches[0] as $key => $match) {
38
39
				$toggle = (($matches[1][$key] === '!') ? 'disable' : 'enable');
40
41
				$type = $matches[2][$key]; $name = $matches[3][$key]; $contents = ($matches[4][$key] ?? '');
42
43
				if (!preg_match(REGEX_TEMPLATE_COMPONENT_NAME, $name)) continue;
44
45
				$this->contents = str_replace($match, ('{ ' . $type . ':' . $name . ' / }'), $this->contents);
46
47
				if ($type === 'block') $this->blocks[$name] = (new Block($contents))->$toggle();
48
49
				else if ($type === 'for') $this->loops[$name] = new Loop($contents);
50
51
				else if ($type === 'widget') $this->registerWidget($this->widgets[] = $name);
52
			}
53
		}
54
55
		/**
56
		 * Parse elementaries
57
		 */
58
59
		private function parseElementaries() {
60
61
			$variables = ['pattern' => REGEX_TEMPLATE_VARIABLE, 'stack' => &$this->variables ];
62
63
			$phrases = ['pattern' => REGEX_TEMPLATE_PHRASE, 'stack' => &$this->phrases ];
64
65
			foreach ([$variables, $phrases] as $elementaries) {
66
67
				preg_match_all($elementaries['pattern'], $this->contents, $matches);
68
69
				foreach ($matches[1] as $index => $name) {
70
71
					if (!preg_match(REGEX_TEMPLATE_COMPONENT_NAME, $name)) continue;
72
73
					$elementaries['stack'][$name] = false;
74
				}
75
			}
76
		}
77
78
		/**
79
		 * Build the block contents
80
		 */
81
82
		protected function buildContents() : string {
83
84
			$insertions = [];
85
86
			# Process blocks
87
88
			foreach ($this->blocks as $name => $block) {
89
90
				$insertions['{ block:' . $name . ' / }'] = $block->getContents();
91
			}
92
93
			# Process loops
94
95
			foreach ($this->loops as $name => $loop) {
96
97
				$insertions['{ for:' . $name . ' / }'] = $loop->getContents();
98
			}
99
100
			# Process widgets
101
102
			foreach ($this->widgets as $name) {
103
104
				$widget = Template::getWidget($name);
105
106
				$contents = ((false !== $widget) ? $widget->getContents() : '');
107
108
				$insertions['{ widget:' . $name . ' / }'] = $contents;
109
			}
110
111
			# Process variables
112
113
			foreach ($this->variables as $name => $value) {
114
115
				$value = ((false === $value) ? Template::getGlobal($name) : $value);
116
117
				$insertions['$' . $name . '$'] = Str::formatOutput($value);
118
			}
119
120
			# Process phrases
121
122
			foreach ($this->phrases as $name => $value) {
123
124
				$value = Language::get($name);
125
126
				$insertions['%' . $name . '%'] = Str::formatOutput($value);
127
			}
128
129
			# Process insertions
130
131
			$contents = str_replace(array_keys($insertions), array_values($insertions), $this->contents);
132
133
			# ------------------------
134
135
			return $contents;
136
		}
137
138
		/**
139
		 * Constructor
140
		 */
141
142
		public function __construct(string $contents = '') {
143
144
			$this->contents = $contents;
145
146
			$this->parseStructures(); $this->parseElementaries();
147
		}
148
149
		/**
150
		 * Cloner
151
		 */
152
153
		public function __clone() {
154
155
			foreach ($this->blocks as $name => $block) $this->blocks[$name] = clone $block;
156
157
			foreach ($this->loops as $name => $loop) $this->loops[$name] = clone $loop;
158
159
			foreach ($this->items as $name => $item) $this->items[$name] = clone $item;
160
		}
161
162
		/**
163
		 * Set a block, a loop, or a variable
164
		 *
165
		 * @return Template\Block : the current block object
166
		 */
167
168
		public function set(string $name, $component) : Block {
169
170
			if ($component instanceof Block) $this->setBlock($name, $component);
171
172
			else if (is_array($component)) $this->setLoop($name, $component);
173
174
			else if (is_scalar($component)) $this->setVar($name, $component);
175
176
			# ------------------------
177
178
			return $this;
179
		}
180
181
		/**
182
		 * Set multiple components (blocks, loops, or variables)
183
		 *
184
		 * @return Template\Block : the current block object
185
		 */
186
187
		public function setArray(array $components) : Block {
188
189
			foreach ($components as $name => $component) $this->set($name, $component);
190
191
			return $this;
192
		}
193
194
		/**
195
		 * Set a block
196
		 *
197
		 * @return Template\Block : the current block object
198
		 */
199
200
		public function setBlock(string $name, Block $block) : Block {
201
202
			if (isset($this->blocks[$name])) $this->blocks[$name] = $block;
203
204
			return $this;
205
		}
206
207
		/**
208
		 * Set a loop
209
		 *
210
		 * @return Template\Block : the current block object
211
		 */
212
213
		public function setLoop(string $name, array $items) : Block {
214
215
			if (isset($this->loops[$name])) $this->loops[$name]->setItems($items);
216
217
			return $this;
218
		}
219
220
		/**
221
		 * Set a variable
222
		 *
223
		 * @return Template\Block : the current block object
224
		 */
225
226
		public function setVar(string $name, string $value) : Block {
227
228
			if (isset($this->variables[$name])) $this->variables[$name] = $value;
229
230
			return $this;
231
		}
232
233
		/**
234
		 * Get a block, a loop, or a variable
235
		 *
236
		 * @return Template\Block|Template\Loop|string|false : the component or false if the component does not exist
237
		 */
238
239
		public function get(string $name) {
240
241
			return ($this->blocks[$name] ?? $this->loops[$name] ?? $this->variables[$name] ?? false);
242
		}
243
244
		/**
245
		 * Get a block. It's recommended to use this method instead of magic getter to avoid an error on getting nonexistent block
246
		 *
247
		 * @return Template\Block : the block or an empty block if the block does not exist
248
		 */
249
250
		public function getBlock(string $name) : Block {
251
252
			return ($this->blocks[$name] ?? new Block);
253
		}
254
255
		/**
256
		 * Get the blocks list
257
		 */
258
259
		public function getBlocks() : array {
260
261
			return $this->blocks;
262
		}
263
264
		/**
265
		 * Get a loop. It's recommended to use this method instead of magic getter to avoid an error on getting nonexistent loop
266
		 *
267
		 * @return Template\Loop : the loop or an empty loop if the loop does not exist
268
		 */
269
270
		public function getLoop(string $name) : Loop {
271
272
			return ($this->loops[$name] ?? new Loop);
273
		}
274
275
		/**
276
		 * Get the loops list
277
		 */
278
279
		public function getLoops() : array {
280
281
			return $this->loops;
282
		}
283
284
		/**
285
		 * Get a variable
286
		 *
287
		 * @return string|false : the value or false if the variable does not exist
288
		 */
289
290
		public function getVar(string $name) {
291
292
			return ($this->variables[$name] ?? false);
293
		}
294
295
		/**
296
		 * Get the variable list
297
		 */
298
299
		public function getVars() : array {
300
301
			return $this->variables;
302
		}
303
304
		/**
305
		 * Get the block contents
306
		 */
307
308
		public function getContents() : string {
309
310
			if (!$this->enabled) return '';
311
312
			# Lock the block
313
314
			$this->enabled = false;
315
316
			# Generate contents
317
318
			$contents = ((0 === $this->count) ? $this->buildContents() : parent::getContents());
319
320
			# Unlock the block
321
322
			$this->enabled = true;
323
324
			# ------------------------
325
326
			return $contents;
327
		}
328
329
		/**
330
		 * Disable the block
331
		 *
332
		 * @return Template\Block : the current block object
333
		 */
334
335
		public function disable() : Block {
336
337
			$this->enabled = false;
338
339
			return $this;
340
		}
341
342
		/**
343
		 * Enable the block
344
		 *
345
		 * @return Template\Block : the current block object
346
		 */
347
348
		public function enable() : Block {
349
350
			$this->enabled = true;
351
352
			return $this;
353
		}
354
355
		/**
356
		 * Check if the block is enabled
357
		 */
358
359
		public function isEnabled() : bool {
360
361
			return $this->enabled;
362
		}
363
364
		/**
365
		 * An alias for the set method
366
		 */
367
368
		public function __set(string $name, $component) : Block {
369
370
			return $this->set($name, $component);
371
		}
372
373
		/**
374
		 * An alias for the get method
375
		 */
376
377
		public function __get(string $name) {
378
379
			return $this->get($name);
380
		}
381
382
		/**
383
		 * Check if a component exists
384
		 */
385
386
		public function __isset(string $name) : bool {
387
388
			return (isset($this->blocks[$name]) || isset($this->loops[$name]) || isset($this->variables[$name]));
389
		}
390
	}
391
}
392