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
|
|
|
$functions = ['attribute', 'nth', 'not']; |
21
|
|
|
|
22
|
|
|
foreach ($this->pseudo as $pseudo) { |
23
|
|
|
foreach ($functions as $func) { |
24
|
|
|
$matches = $matches && $this->$func($pseudo, $element); |
25
|
|
|
} |
26
|
|
|
} |
27
|
|
|
return $matches; |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
private function attribute($pseudo, $element) { |
31
|
|
|
$pos = strpos($pseudo, '['); |
32
|
|
|
if ($pos === false) return true; |
33
|
|
|
|
34
|
|
|
$name = substr($pseudo, 0, $pos); |
35
|
|
|
if (!is_callable([$this->dataFunction, $name])) return true; |
36
|
|
|
|
37
|
|
|
$bracketMatcher = new \Transphporm\Parser\BracketMatcher($pseudo); |
38
|
|
|
$criteria = $bracketMatcher->match('[', ']'); |
39
|
|
|
|
40
|
|
|
if (strpos($pseudo, '=') === false) { |
41
|
|
|
$lookupValue = $this->dataFunction->$name([$criteria], $element); |
42
|
|
|
return $lookupValue !== null; |
43
|
|
|
} |
44
|
|
|
list ($field, $value) = explode('=', $criteria); |
45
|
|
|
|
46
|
|
|
$operator = $this->getOperator($field); |
47
|
|
|
$lookupValue = $this->dataFunction->$name([trim($field, $operator)], $element); |
48
|
|
|
|
49
|
|
|
return $this->processOperator($operator, $lookupValue, $this->parseValue(trim($value, '"'))); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
//Currently only not is supported, but this is separated out to support others in future |
53
|
|
|
private function processOperator($operator, $lookupValue, $value) { |
54
|
|
|
$matched = $lookupValue == $value; |
55
|
|
|
return $operator === '!' ? !$matched : $matched; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
private function parseValue($value) { |
59
|
|
|
if ($value == 'true') return true; |
60
|
|
|
else if ($value == 'false') return false; |
61
|
|
|
else return $value; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
private function getOperator($field) { |
65
|
|
|
if ($field[strlen($field)-1] == '!') { |
66
|
|
|
return '!'; |
67
|
|
|
} |
68
|
|
|
else return ''; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
private function nth($pseudo, $element) { |
72
|
|
|
if (strpos($pseudo, 'nth-child') === 0) { |
73
|
|
|
$bracketMatcher = new \Transphporm\Parser\BracketMatcher($pseudo); |
74
|
|
|
$criteria = $bracketMatcher->match('(', ')'); |
75
|
|
|
|
76
|
|
|
$bracketMatcher = new \Transphporm\Parser\BracketMatcher($element->getNodePath()); |
77
|
|
|
$num = $bracketMatcher->match('[', ']'); |
78
|
|
|
|
79
|
|
|
if (is_callable([$this, $criteria])) return $this->$criteria($num); |
80
|
|
|
else return $num == $criteria; |
81
|
|
|
} |
82
|
|
|
return true; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
private function not($pseudo, $element) { |
86
|
|
|
if (strpos($pseudo, 'not') === 0) { |
87
|
|
|
$valueParser = new \Transphporm\Parser\Value($this->dataFunction); |
88
|
|
|
$bracketMatcher = new \Transphporm\Parser\BracketMatcher($pseudo); |
89
|
|
|
$css = explode(',', $bracketMatcher->match('(', ')')); |
90
|
|
|
|
91
|
|
|
foreach ($css as $selector) { |
92
|
|
|
$cssToXpath = new \Transphporm\Parser\CssToXpath($selector, $valueParser); |
93
|
|
|
$xpathString = $cssToXpath->getXpath(); |
94
|
|
|
$xpath = new \DomXpath($element->ownerDocument); |
95
|
|
|
|
96
|
|
|
foreach ($xpath->query($xpathString) as $matchedElement) { |
97
|
|
|
if ($element->isSameNode($matchedElement)) return false; |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
return true; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
public function attr() { |
105
|
|
View Code Duplication |
foreach ($this->pseudo as $pseudo) { |
|
|
|
|
106
|
|
|
if (strpos($pseudo, 'attr') === 0) { |
107
|
|
|
$bracketMatcher = new \Transphporm\Parser\BracketMatcher($pseudo); |
108
|
|
|
$criteria = trim($bracketMatcher->match('(', ')')); |
109
|
|
|
return $criteria; |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
return false; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
public function header($element) { |
117
|
|
|
if ($this->matches($element)) { |
118
|
|
View Code Duplication |
foreach ($this->pseudo as $pseudo) { |
|
|
|
|
119
|
|
|
if (strpos($pseudo, 'header') === 0) { |
120
|
|
|
$bracketMatcher = new \Transphporm\Parser\BracketMatcher($pseudo); |
121
|
|
|
return $bracketMatcher->match('[', ']'); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
private function odd($num) { |
128
|
|
|
return $num % 2 === 1; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
private function even($num) { |
132
|
|
|
return $num % 2 === 0; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
public function getPseudo() { |
136
|
|
|
return $this->pseudo; |
137
|
|
|
} |
138
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.