Repeat::createHook()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 3
nc 3
nop 3
1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2017 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.2                                                             */
7
namespace Transphporm\Property;
8
class Repeat implements \Transphporm\Property {
9
	private $functionSet;
10
	private $elementData;
11
	private $line;
12
    private $filePath;
13
14
	public function __construct(\Transphporm\FunctionSet $functionSet, \Transphporm\Hook\ElementData $elementData, &$line, \Transphporm\FilePath $filePath) {
15
		$this->functionSet = $functionSet;
16
		$this->elementData = $elementData;
17
		$this->line = &$line;
18
        $this->filePath = $filePath;
19
	}
20
21
22
23
	public function run(array $values, \DomElement $element, array $rules, \Transphporm\Hook\PseudoMatcher $pseudoMatcher, array $properties = []) {
24
		$values = $this->fixEmpty($values);
25
		if ($element->getAttribute('transphporm') === 'added') return $element->parentNode->removeChild($element);
26
		$this->handleContentModeAppend($element, $rules);
27
		$max = $this->getMax($values);
28
		$count = 0;
29
		$repeat = $this->getRepeatValue($values, $max);
30
		//Don't run repeat on the cloned element or it will loop forever
31
32
		unset($rules['repeat']);
33
34
35
		$hook = $this->createHook($rules, $pseudoMatcher, $properties);
36
37
38
		foreach ($repeat as $key => $iteration) {
39
			if ($count+1 > $max) break;
40
			$clone = $this->cloneElement($element, $iteration, $key, $count++);
41
			//Re-run the hook on the new element, but use the iterated data
42
			if ($hook) $hook->run($clone);
43
		}
44
45
		//Remove the original element
46
		$element->parentNode->removeChild($element);
47
		return false;
48
	}
49
50
	private function handleContentModeAppend(\DomElement $element, array $rules) {
51
		$contentMode = (isset($rules['content-mode'])) ? $rules['content-mode']->read() : 'replace';
52
53
		if ($contentMode == 'append') {
54
			$clone = $element->cloneNode(true);
55
			$clone->setAttribute('transphporm', 'immutable');
56
			$element->parentNode->insertBefore($clone, $element);
57
		}
58
	}
59
60
	private function getRepeatValue($values, &$max) {
61
		$mode = $this->getMode($values);
62
		$repeat = $values[0];
63
64
		if ($mode !== 'each') { // $mode === 'loop'
65
			$repeat = range($values[0], $max);
66
			$max++;
67
		}
68
		return $repeat;
69
	}
70
71
	private function getMode($args) {
72
		return isset($args[2]) ? $args[2] : 'each';
73
	}
74
75
	private function fixEmpty($value) {
76
		if (empty($value[0])) $value[0] = [];
77
		return $value;
78
	}
79
80
	private function cloneElement($element, $iteration, $key, $count) {
81
		$clone = $element->cloneNode(true);
82
		$this->tagElement($clone, $count);
83
84
		$this->elementData->bind($clone, $iteration, 'iteration');
85
		$this->elementData->bind($clone, $key, 'key');
86
		$element->parentNode->insertBefore($clone, $element);
87
		return $clone;
88
	}
89
90
	private function tagElement($element, $count) {
91
		//Mark all but one of the nodes as having been added by transphporm, when the hook is run again, these are removed
92
		if ($count > 0) $element->setAttribute('transphporm', 'added');
93
	}
94
95
	private function getMax($values) {
96
		return isset($values[1]) ? $values[1] : PHP_INT_MAX;
97
	}
98
99
	private function createHook($newRules, $pseudoMatcher, $properties) {
100
		// Only create a hook if there are remaining properties to process
101
		// for this rule e.g. repeat: data(); content: "foo"
102
		// The content property still needs to be used
103
		// But for rules that are just { repeat: data(); } this can be skipped.
104
		if (empty($newRules)) return false;
105
106
		$hook = new \Transphporm\Hook\PropertyHook($newRules, $this->line, null, $this->line, $pseudoMatcher, new \Transphporm\Parser\Value($this->functionSet), $this->functionSet, $this->filePath);
107
		foreach ($properties as $name => $property) $hook->registerProperty($name, $property);
108
		return $hook;
109
	}
110
}
111