Completed
Push — master ( 04f450...a0fa0b )
by Tom
02:33
created

PseudoMatcher::matches()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.2
cc 4
eloc 5
nc 4
nop 1
1
<?php
2
/* @description     Transformation Style Sheets - Revolutionising PHP templating    *
3
 * @author          Tom Butler [email protected]                                             *
4
 * @copyright       2015 Tom Butler <[email protected]> | https://r.je/                      *
5
 * @license         http://www.opensource.org/licenses/bsd-license.php  BSD License *
6
 * @version         1.0                                                             */
7
namespace Transphporm\Hook;
8
/** Determines whether $element matches the pseudo rule such as nth-child() or [attribute="value"] */
9
class PseudoMatcher {
10
	private $pseudo;
11
	private $dataFunction;
12
13
	public function __construct($pseudo, DataFunction $dataFunction) {
14
		$this->pseudo = $pseudo;
15
		$this->dataFunction = $dataFunction;
16
	}
17
18
	public function matches($element) {
19
		$matches = true;
20
21
		foreach ($this->pseudo as $pseudo) {			
22
			$matches = $matches && $this->attribute($pseudo, $element) && $this->nth($pseudo, $element);			
23
		}		
24
		return $matches;
25
	}
26
27
	private function betweenBrackets($string, $openChr, $closingChr, $start = 0) {
28
		$open = strpos($string, $openChr, $start);
29
		$close = strpos($string, $closingChr, $open);
30
31
		$cPos = $open+1;
32
		while (($cPos = strpos($string, $openChr, $cPos+1)) !== false && $cPos < $close) $close = strpos($string, $closingChr, $close+1);
33
34
		return substr($string, $open+1, $close-$open-1);
35
	}
36
	
37
	private function attribute($pseudo, $element) {
38
		$pos = strpos($pseudo, '[');
39
		if ($pos === false) return true;
40
		
41
		$name = substr($pseudo, 0, $pos);
42
		if (!is_callable([$this->dataFunction, $name])) return true;
43
44
		$criteria = $this->betweenBrackets($pseudo, '[', ']');
45
46
		if (strpos($pseudo, '=') === false) {
47
			$lookupValue = $this->dataFunction->$name([$criteria], $element);
48
			return $lookupValue !== null;
49
		}
50
		list ($field, $value) = explode('=', $criteria);
51
52
		$operator = $this->getOperator($field);
53
54
		$field = trim($field, $operator);		
55
		$value = $this->parseValue(trim($value, '"'));
56
57
		$lookupValue = $this->dataFunction->$name([$field], $element);
58
		return $this->processOperator($operator, $lookupValue, $value);
59
	}
60
61
	//Currently only not is supported, but this is separated out to support others in future
62
	private function processOperator($operator, $lookupValue, $value) {
63
		$matched = $lookupValue == $value;
64
		return $operator === '!' ? !$matched : $matched;
65
	}
66
67
	private function parseValue($value) {
68
		if ($value == 'true') return true;
69
		else if ($value == 'false') return false;
70
		else return $value;
71
	}
72
73
	private function getOperator($field) {
74
		if ($field[strlen($field)-1] == '!') {
75
			return '!';
76
		}
77
		else return '';
78
	}
79
80
	private function nth($pseudo, $element) {
81
		if (strpos($pseudo, 'nth-child') === 0) {	
82
			$criteria = $this->getBetween($pseudo, '(', ')');
83
			$num = $this->getBetween($element->getNodePath(), '[', ']');
84
			
85
			if (is_callable([$this, $criteria])) return $this->$criteria($num);
86
			else return $num == $criteria;
87
			
88
		}
89
		return true;
90
	}
91
92
	public function attr() {
93
		foreach ($this->pseudo as $pseudo) {
94
			if (strpos($pseudo, 'attr') === 0) {
95
				$criteria = trim($this->getBetween($pseudo, '(', ')'));
96
				return $criteria;
97
			}
98
		}
99
100
		return false;
101
	}
102
103
	public function header($element)  {
104
		if ($this->matches($element)) {
105
			foreach ($this->pseudo as $pseudo) {
106
				if (strpos($pseudo, 'header') === 0) return $this->getBetween($pseudo, '[', ']');
107
			}
108
		}
109
	}
110
111
	private function odd($num) {
112
		return $num % 2 === 1;
113
	}
114
115
	private function even($num) {
116
		return $num % 2 === 0;
117
	}
118
119
	private function getBetween($string, $start, $end) {
120
		$open = strpos($string, $start);
121
		if ($open === false) return false;
122
		$close = strpos($string, $end, $open);
123
		return substr($string, $open+1, $close-$open-1);
124
	}
125
126
	public function getPseudo() {
127
		return $this->pseudo;
128
	}
129
}