1 | <?php |
||
2 | /** |
||
3 | * @author: RunnerLee |
||
4 | * @email: [email protected] |
||
5 | * @time: 17-2-20 15:22 |
||
6 | */ |
||
7 | |||
8 | namespace Runner\Validator; |
||
9 | |||
10 | use BadMethodCallException; |
||
11 | use Runner\Validator\Concerns\MessagesAttributes; |
||
12 | use Runner\Validator\Concerns\ValidatesAttributes; |
||
13 | |||
14 | /** |
||
15 | * Class Validator. |
||
16 | */ |
||
17 | class Validator |
||
18 | { |
||
19 | use ValidatesAttributes, MessagesAttributes; |
||
20 | |||
21 | /** |
||
22 | * @var array |
||
23 | */ |
||
24 | protected $data = []; |
||
25 | |||
26 | /** |
||
27 | * @var array |
||
28 | */ |
||
29 | protected $ruleGroups = []; |
||
30 | |||
31 | /** |
||
32 | * @var array |
||
33 | */ |
||
34 | protected $messages = []; |
||
35 | |||
36 | /** |
||
37 | * @var array |
||
38 | */ |
||
39 | protected static $forceRules = ['Required', 'RequiredIf', 'RequiredWith', 'RequiredUnless', 'RequiredWithout']; |
||
40 | |||
41 | /** |
||
42 | * @var array |
||
43 | */ |
||
44 | protected static $extensions = []; |
||
45 | |||
46 | /** |
||
47 | * @var array |
||
48 | */ |
||
49 | protected static $extensionTemplates = []; |
||
50 | |||
51 | /** |
||
52 | * Validator constructor. |
||
53 | * |
||
54 | * @param array $data |
||
55 | * @param array $rules |
||
56 | * @param array $customMessages |
||
57 | * @param string $file |
||
58 | */ |
||
59 | 4 | public function __construct(array $data, array $rules, array $customMessages = [], $file = __DIR__.'/messages/en.php') |
|
60 | { |
||
61 | 4 | $this->data = $data; |
|
62 | 4 | $this->parseRules($rules); |
|
63 | 4 | $this->loadMessageTemplate($file, $customMessages); |
|
64 | 4 | } |
|
65 | |||
66 | /** |
||
67 | * @param $name |
||
68 | * @param $callback |
||
69 | * @param bool $isForce |
||
70 | * @param string $message |
||
71 | */ |
||
72 | 3 | public static function addExtension($name, $callback, $isForce = false, $message = null) |
|
73 | { |
||
74 | 3 | $name = self::formatRuleName($name); |
|
75 | |||
76 | 3 | self::$extensions[$name] = $callback; |
|
77 | |||
78 | 3 | $isForce && self::$forceRules[] = $name; |
|
79 | |||
80 | 3 | !empty($message) && (static::$extensionTemplates[$name] = $message); |
|
81 | 3 | } |
|
82 | |||
83 | /** |
||
84 | * @return bool |
||
85 | */ |
||
86 | 4 | public function validate() |
|
87 | { |
||
88 | 4 | foreach ($this->ruleGroups as $field => $rules) { |
|
89 | 4 | if ($this->hasField($field)) { |
|
90 | 4 | $value = $this->getField($field); |
|
91 | 4 | foreach ($rules as $rule => $parameters) { |
|
92 | 4 | if (!$this->runValidateRule($rule, $field, $value, $parameters)) { |
|
93 | 4 | $this->messages[$field][$rule] = $this->buildMessage($rule, $field, $parameters); |
|
94 | } |
||
95 | } |
||
96 | 1 | } elseif ($forceRules = array_intersect(self::$forceRules, array_keys($rules))) { |
|
97 | 1 | foreach ($forceRules as $rule) { |
|
98 | 1 | if (!$this->runValidateRule($rule, $field, null, $rules[$rule])) { |
|
99 | 4 | $this->messages[$field][$rule] = $this->buildMessage($rule, $field, $rules[$rule]); |
|
100 | } |
||
101 | } |
||
102 | } |
||
103 | } |
||
104 | |||
105 | 4 | return 0 === count($this->messages); |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * @return array |
||
110 | */ |
||
111 | 1 | public function fails() |
|
112 | { |
||
113 | 1 | return array_keys($this->messages); |
|
114 | } |
||
115 | |||
116 | /** |
||
117 | * @return array |
||
118 | */ |
||
119 | 2 | public function messages() |
|
120 | { |
||
121 | 2 | return $this->messages; |
|
122 | } |
||
123 | |||
124 | /** |
||
125 | * @return array |
||
126 | */ |
||
127 | 1 | public function data() |
|
128 | { |
||
129 | 1 | return $this->data; |
|
130 | } |
||
131 | |||
132 | /** |
||
133 | * @param array $ruleGroups |
||
134 | */ |
||
135 | 4 | protected function parseRules(array $ruleGroups) |
|
136 | { |
||
137 | 4 | $map = []; |
|
138 | 4 | foreach ($ruleGroups as $field => $rules) { |
|
139 | 4 | foreach (explode('|', $rules) as $rule) { |
|
140 | 4 | list($rule, $parameters) = explode(':', (false === strpos($rule, ':') ? ($rule.':') : $rule), 2); |
|
141 | 4 | !isset($map[$rule]) && $map[$rule] = self::formatRuleName($rule); |
|
142 | 4 | $rule = $map[$rule]; |
|
143 | 4 | $this->ruleGroups[$field][$rule] = ('' === $parameters ? [] : explode(',', $parameters)); |
|
144 | } |
||
145 | } |
||
146 | 4 | } |
|
147 | |||
148 | /** |
||
149 | * @param $name |
||
150 | * |
||
151 | * @return string |
||
152 | */ |
||
153 | 4 | protected static function formatRuleName($name) |
|
154 | { |
||
155 | 4 | return implode( |
|
156 | 4 | '', |
|
157 | 4 | array_map( |
|
158 | 4 | function ($value) { |
|
159 | 4 | return ucfirst($value); |
|
160 | 4 | }, |
|
161 | 4 | explode('_', $name) |
|
162 | ) |
||
163 | ); |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * @param string $field |
||
168 | * |
||
169 | * @return bool |
||
170 | */ |
||
171 | 4 | protected function hasField($field) |
|
172 | { |
||
173 | 4 | $field = explode('.', $field); |
|
174 | 4 | $item = array_shift($field); |
|
175 | 4 | if (!array_key_exists($item, $this->data)) { |
|
176 | 1 | return false; |
|
177 | } |
||
178 | 4 | $value = $this->data[$item]; |
|
179 | |||
180 | 4 | foreach ($field as $item) { |
|
181 | 1 | if (!array_key_exists($item, $value)) { |
|
182 | 1 | return false; |
|
183 | } |
||
184 | 1 | $value = $value[$item]; |
|
185 | } |
||
186 | |||
187 | 4 | return true; |
|
188 | } |
||
189 | |||
190 | /** |
||
191 | * @param string $field |
||
192 | * |
||
193 | * @return mixed |
||
194 | */ |
||
195 | 4 | protected function getField($field) |
|
196 | { |
||
197 | 4 | $field = explode('.', $field); |
|
198 | 4 | $item = array_shift($field); |
|
199 | 4 | $value = $this->data[$item]; |
|
200 | 4 | foreach ($field as $item) { |
|
201 | 1 | $value = $value[$item]; |
|
202 | } |
||
203 | |||
204 | 4 | return $value; |
|
205 | } |
||
206 | |||
207 | /** |
||
208 | * @param $field |
||
209 | * @param $value |
||
210 | * @param $rule |
||
211 | * @param array $parameters |
||
212 | * |
||
213 | * @return bool |
||
214 | */ |
||
215 | 4 | protected function runValidateRule($rule, $field, $value, array $parameters = []) |
|
216 | { |
||
217 | 4 | $callback = array_key_exists($rule, self::$extensions) ? self::$extensions[$rule] : [$this, "validate{$rule}"]; |
|
218 | |||
219 | 4 | return (bool) call_user_func($callback, $field, $value, $parameters, $this); |
|
220 | } |
||
221 | |||
222 | 1 | public function __call($method, $arguments) |
|
223 | { |
||
224 | 1 | $rule = self::formatRuleName(substr($method, 8)); |
|
225 | |||
226 | 1 | if (!isset(self::$extensions[$rule])) { |
|
227 | 1 | throw new BadMethodCallException(sprintf('Method %s::%s does not exists', static::class, $method)); |
|
228 | } |
||
229 | |||
230 | 1 | return $this->runValidateRule($rule, ...$arguments); |
|
0 ignored issues
–
show
|
|||
231 | } |
||
232 | } |
||
233 |
This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.