1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Highlighter |
4
|
|
|
* |
5
|
|
|
* Copyright (C) 2016, Some right reserved. |
6
|
|
|
* |
7
|
|
|
* @author Kacper "Kadet" Donat <[email protected]> |
8
|
|
|
* |
9
|
|
|
* Contact with author: |
10
|
|
|
* Xmpp: [email protected] |
11
|
|
|
* E-mail: [email protected] |
12
|
|
|
* |
13
|
|
|
* From Kadet with love. |
14
|
|
|
*/ |
15
|
|
|
|
16
|
|
|
namespace Kadet\Highlighter\Parser\Validator; |
17
|
|
|
|
18
|
|
|
|
19
|
|
|
use Kadet\Highlighter\Parser\Context; |
20
|
|
|
|
21
|
|
|
class Validator |
22
|
|
|
{ |
23
|
|
|
const CONTEXT_NOT_IN = 2; |
24
|
|
|
const CONTEXT_IN = 1; |
25
|
|
|
const CONTEXT_IN_ONE_OF = 4; |
26
|
|
|
const CONTEXT_EXACTLY = 8; |
27
|
|
|
const CONTEXT_ON_TOP = 16; |
28
|
|
|
const CONTEXT_REGEX = 32; |
29
|
|
|
|
30
|
|
|
private $_rules = []; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Validator constructor. |
34
|
|
|
* |
35
|
|
|
* @param array $rules |
36
|
|
|
*/ |
37
|
47 |
|
public function __construct(array $rules = []) { |
38
|
47 |
|
$this->setRules($rules); |
39
|
47 |
|
} |
40
|
|
|
|
41
|
30 |
|
public function validate(Context $context, $additional = []) { |
42
|
30 |
|
return $this->_validate($context->stack, $additional + $this->_rules); |
43
|
|
|
} |
44
|
|
|
|
45
|
45 |
|
public function setRules($rules) |
46
|
|
|
{ |
47
|
45 |
|
if(empty($rules)) { |
48
|
35 |
|
$this->_rules = [ 'none' => Validator::CONTEXT_IN_ONE_OF ]; |
49
|
35 |
|
} else { |
50
|
12 |
|
foreach ($rules as $key => $rule) { |
51
|
12 |
|
list($plain, $type) = $this->_parse($rule); |
52
|
12 |
|
$this->_rules[$plain] = $type; |
53
|
12 |
|
} |
54
|
|
|
} |
55
|
45 |
|
} |
56
|
|
|
|
57
|
28 |
|
private function _clean($rule, &$required) |
58
|
|
|
{ |
59
|
28 |
|
if (strpos($rule, '.') !== false) { |
60
|
|
|
foreach (array_filter(array_keys($required), function ($key) use ($rule) { |
61
|
10 |
|
return fnmatch($key . '.*', $rule); |
62
|
10 |
|
}) as $remove) { |
63
|
9 |
|
unset($required[$remove]); |
64
|
10 |
|
} |
65
|
10 |
|
} |
66
|
28 |
|
} |
67
|
|
|
|
68
|
32 |
|
protected function _validate($context, $rules, $result = false) { |
69
|
32 |
|
if(empty($context)) { |
70
|
26 |
|
$context = ['none']; |
71
|
26 |
|
} |
72
|
|
|
|
73
|
32 |
|
foreach($rules as $rule => &$type) { |
74
|
30 |
|
$matched = $this->_matches($context, $rule, $type); |
75
|
|
|
|
76
|
30 |
|
if ($type & Validator::CONTEXT_NOT_IN) { |
77
|
14 |
|
if ($matched) { |
78
|
10 |
|
return false; |
79
|
|
|
} |
80
|
13 |
|
$result = true; |
81
|
30 |
|
} elseif ($type & Validator::CONTEXT_IN) { |
82
|
16 |
|
if (!$matched) { |
83
|
12 |
|
return false; |
84
|
|
|
} |
85
|
15 |
|
$result = true; |
86
|
|
|
|
87
|
15 |
|
$this->_clean($rule, $rules); |
88
|
28 |
|
} elseif ($type & Validator::CONTEXT_IN_ONE_OF) { |
89
|
22 |
|
if ($matched) { |
90
|
21 |
|
$result = true; |
91
|
21 |
|
$this->_clean($rule, $rules); |
92
|
21 |
|
} |
93
|
22 |
|
} |
94
|
32 |
|
} |
95
|
|
|
|
96
|
31 |
|
return $result; |
97
|
|
|
} |
98
|
|
|
|
99
|
12 |
|
private function _parse($rule) |
100
|
|
|
{ |
101
|
|
|
$types = [ |
102
|
12 |
|
'!' => Validator::CONTEXT_NOT_IN, |
103
|
12 |
|
'+' => Validator::CONTEXT_IN, |
104
|
12 |
|
'*' => Validator::CONTEXT_IN_ONE_OF, |
105
|
12 |
|
'@' => Validator::CONTEXT_EXACTLY, |
106
|
|
|
// '^' => Validator::CONTEXT_ON_TOP, |
107
|
|
|
'~' => Validator::CONTEXT_REGEX |
108
|
12 |
|
]; |
109
|
|
|
|
110
|
12 |
|
if (!isset($types[$rule[0]])) { |
111
|
4 |
|
return [$rule, Validator::CONTEXT_IN]; |
112
|
|
|
} |
113
|
|
|
|
114
|
10 |
|
$type = 0; |
115
|
10 |
|
$pos = 0; |
116
|
10 |
|
foreach (str_split($rule) as $pos => $char) { |
117
|
10 |
|
if (!isset($types[$char])) { |
118
|
10 |
|
break; |
119
|
|
|
} |
120
|
|
|
|
121
|
10 |
|
$type |= $types[$char]; |
122
|
10 |
|
} |
123
|
|
|
|
124
|
10 |
|
$rule = substr($rule, $pos); |
125
|
|
|
|
126
|
10 |
|
if($type & self::CONTEXT_REGEX) { |
127
|
2 |
|
$rule = "/^$rule(\\.\\w+)?/i"; |
128
|
2 |
|
} |
129
|
|
|
|
130
|
10 |
|
return [$rule, $type]; |
131
|
|
|
} |
132
|
|
|
|
133
|
30 |
|
private function _matches($context, $rule, $type) { |
134
|
30 |
|
if($type & self::CONTEXT_EXACTLY) { |
135
|
1 |
|
return in_array($rule, $context, true); |
136
|
29 |
|
} elseif($type & self::CONTEXT_REGEX) { |
137
|
2 |
|
foreach($context as $item) { |
138
|
2 |
|
if(preg_match($rule, $item)) { |
139
|
2 |
|
return true; |
140
|
|
|
} |
141
|
2 |
|
} |
142
|
2 |
|
return false; |
143
|
|
|
} else { |
144
|
27 |
|
if(in_array($rule, $context, true)) { |
145
|
27 |
|
return true; |
146
|
|
|
} |
147
|
|
|
|
148
|
18 |
|
foreach($context as $item) { |
149
|
18 |
|
if(fnmatch("$rule.*", $item)) { |
150
|
7 |
|
return true; |
151
|
|
|
} |
152
|
18 |
|
} |
153
|
18 |
|
return false; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
37 |
|
public static function everywhere() |
158
|
|
|
{ |
159
|
37 |
|
static $validator; |
160
|
37 |
|
if (!$validator) { |
161
|
21 |
|
$validator = new DelegateValidator(function () { |
162
|
21 |
|
return true; |
163
|
|
|
}); |
164
|
|
|
} |
165
|
|
|
|
166
|
37 |
|
return $validator; |
167
|
|
|
} |
168
|
|
|
} |
169
|
|
|
|