Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
100.00% |
71 / 71 |
|
100.00% |
9 / 9 |
CRAP | |
100.00% |
1 / 1 |
Gate | |
100.00% |
71 / 71 |
|
100.00% |
9 / 9 |
42 | |
100.00% |
1 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
accepts | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addFields | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
accept | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addField | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
6 | |||
assert | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
10 | |||
setData | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
getData | |
100.00% |
19 / 19 |
|
100.00% |
1 / 1 |
9 | |||
get | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
__get | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
validate | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
6 | |||
getErrors | n/a |
0 / 0 |
n/a |
0 / 0 |
1 |
1 | <?php |
2 | |
3 | namespace Verja; |
4 | |
5 | use Verja\Exception\InvalidValue; |
6 | |
7 | class Gate |
8 | { |
9 | /** @var Field[] */ |
10 | protected $fields = []; |
11 | |
12 | /** @var array */ |
13 | protected $rawData = []; |
14 | |
15 | /** @var string */ |
16 | protected $filteredDataHash; |
17 | |
18 | /** @var array */ |
19 | protected $filteredData = []; |
20 | |
21 | /** @var array */ |
22 | protected $errors = []; |
23 | |
24 | public function __construct(array $data = null) |
25 | { |
26 | if ($data) { |
27 | $this->setData($data); |
28 | } |
29 | } |
30 | |
31 | /** |
32 | * Alias for addFields |
33 | * |
34 | * @param array $fields |
35 | * @return $this |
36 | * @see Gate::addFields() |
37 | */ |
38 | public function accepts(array $fields) |
39 | { |
40 | return $this->addFields($fields); |
41 | } |
42 | |
43 | /** |
44 | * Add an array of fields |
45 | * |
46 | * Definitions are defined as in addField. |
47 | * |
48 | * Accepts fields without filter and validator definitions as values. So the following examples are equal: |
49 | * |
50 | * `[ $key => [] ]` |
51 | * |
52 | * `[ $key => null ]` |
53 | * |
54 | * `[ $key ]` |
55 | * |
56 | * @param array $fields |
57 | * @return $this |
58 | * @see Gate::addField() how to pass definitions |
59 | */ |
60 | public function addFields(array $fields) |
61 | { |
62 | foreach ($fields as $key => $field) { |
63 | if (is_int($key)) { |
64 | $this->addField($field); // field is the key in numeric arrays |
65 | } else { |
66 | $this->addField($key, $field); |
67 | } |
68 | } |
69 | return $this; |
70 | } |
71 | |
72 | /** |
73 | * Alias for addField |
74 | * |
75 | * @param string $key |
76 | * @param mixed $field |
77 | * @return $this |
78 | * @see Gate::addField() |
79 | */ |
80 | public function accept($key, $field = null) |
81 | { |
82 | return $this->addField($key, $field); |
83 | } |
84 | |
85 | /** |
86 | * Add an accepted field to this gate |
87 | * |
88 | * The definition can be a single validator or filter as string or object, an array of validators and filters |
89 | * as string or object or an instance of Field. |
90 | * |
91 | * The following examples are equal: |
92 | * |
93 | * `(new Field)->addValidator('strLen:2:5')` |
94 | * |
95 | * `'strLen:2:5'` |
96 | * |
97 | * `['strLen:2:5']` |
98 | * |
99 | * `new Field(['strLen:2:5'])` |
100 | * |
101 | * @param string $key The key in the data array |
102 | * @param mixed $field Definition of the field |
103 | * @return $this |
104 | */ |
105 | public function addField($key, $field = null) |
106 | { |
107 | if ($field instanceof Field) { |
108 | $this->fields[$key] = $field; |
109 | } else { |
110 | $definitions = []; |
111 | if (is_array($field)) { |
112 | $definitions = $field; |
113 | } elseif (is_string($field) || $field instanceof ValidatorInterface || $field instanceof FilterInterface) { |
114 | $definitions = [$field]; |
115 | } |
116 | $this->fields[$key] = new Field($definitions); |
117 | } |
118 | |
119 | return $this; |
120 | } |
121 | |
122 | /** |
123 | * Quick assertion with filters and validators |
124 | * |
125 | * Asserts that $value in $context can be filtered and validated by $field. |
126 | * |
127 | * If not an InvalidValue exception is thrown. |
128 | * |
129 | * Field can be defined as for addField. |
130 | * |
131 | * @param mixed $field |
132 | * @param mixed $value |
133 | * @param array $context |
134 | * @return mixed |
135 | * @throws InvalidValue |
136 | * @see Gate::addField() |
137 | */ |
138 | public static function assert($field, $value, array $context = []) |
139 | { |
140 | if (!$field instanceof Field) { |
141 | $definitions = []; |
142 | if (is_array($field)) { |
143 | $definitions = $field; |
144 | } elseif (is_string($field) || |
145 | $field instanceof ValidatorInterface || |
146 | $field instanceof FilterInterface || |
147 | is_callable($field) |
148 | ) { |
149 | $definitions = [$field]; |
150 | } |
151 | $field = new Field($definitions); |
152 | } |
153 | |
154 | $filtered = $field->filter($value, $context); |
155 | if (!$field->validate($filtered, $context)) { |
156 | $errors = $field->getErrors(); |
157 | if (count($errors) === 1) { |
158 | throw new InvalidValue(sprintf('Assertion failed: %s', $errors[0]->message), ...$errors); |
159 | } elseif (count($errors) > 1) { |
160 | // Ignoring coverage because of error in coverage analysis |
161 | // @codeCoverageIgnoreStart |
162 | throw new InvalidValue(sprintf( |
163 | 'Assertion failed: %s', |
164 | implode('; ', array_map(function (Error $error) { |
165 | return $error->message; |
166 | }, $errors)) |
167 | ), ...$errors); |
168 | // @codeCoverageIgnoreEnd |
169 | } else { |
170 | throw new InvalidValue(sprintf( |
171 | 'Failed asserting that %s is valid (unknown error)', |
172 | json_encode($value) |
173 | )); |
174 | } |
175 | } |
176 | |
177 | return $filtered; |
178 | } |
179 | |
180 | /** |
181 | * Set the data that should be covered (the context) |
182 | * |
183 | * @param array $data |
184 | * @return $this |
185 | */ |
186 | public function setData(array $data) |
187 | { |
188 | $this->rawData = $data; |
189 | return $this; |
190 | } |
191 | |
192 | /** |
193 | * Get all data or the value for $key |
194 | * |
195 | * @param string $key |
196 | * @param bool $validate |
197 | * @return array|mixed |
198 | * @throws InvalidValue When value is invalid |
199 | */ |
200 | public function getData(string $key = null, $validate = true) |
201 | { |
202 | if ($key !== null) { |
203 | if (!isset($this->fields[$key])) { |
204 | return null; |
205 | } |
206 | $fields = [$key => $this->fields[$key]]; |
207 | } else { |
208 | $fields = $this->fields; |
209 | } |
210 | |
211 | $result = []; |
212 | foreach ($fields as $k => $field) { |
213 | $filtered = $field->filter($this->rawData[$k] ?? null, $this->rawData); |
214 | |
215 | if ($validate && !$field->validate($this->rawData[$k] ?? null, $this->rawData)) { |
216 | if ($field->isRequired()) { |
217 | $errors = $field->getErrors(); |
218 | if (count($errors) > 0) { |
219 | throw new InvalidValue(sprintf('Invalid %s: %s', $k, $errors[0]->message), ...$errors); |
220 | } |
221 | throw new InvalidValue(sprintf('The value %s is not valid for %s', json_encode($filtered), $k)); |
222 | } else { |
223 | $filtered = null; |
224 | } |
225 | } |
226 | |
227 | if ($key !== null) { |
228 | return $filtered; |
229 | } |
230 | |
231 | $result[$k] = $filtered; |
232 | } |
233 | |
234 | return $result; |
235 | } |
236 | |
237 | /** |
238 | * Alias for getData |
239 | * |
240 | * @param string $key |
241 | * @return mixed |
242 | * @see Gate::getData() |
243 | * @codeCoverageIgnore trivial |
244 | * @throws InvalidValue |
245 | */ |
246 | public function get(string $key = null) |
247 | { |
248 | return $this->getData($key); |
249 | } |
250 | |
251 | /** |
252 | * Alias for getData |
253 | * |
254 | * @param string $key |
255 | * @return mixed |
256 | * @see Gate::getData() |
257 | * @codeCoverageIgnore trivial |
258 | * @throws InvalidValue |
259 | */ |
260 | public function __get(string $key) |
261 | { |
262 | return $this->getData($key); |
263 | } |
264 | |
265 | /** |
266 | * Validate $data or previously stored data |
267 | * |
268 | * @param array $data |
269 | * @return bool |
270 | */ |
271 | public function validate(array $data = null) |
272 | { |
273 | if ($data) { |
274 | $this->setData($data); |
275 | } |
276 | |
277 | $valid = true; |
278 | $this->errors = []; |
279 | foreach ($this->fields as $key => $field) { |
280 | if (empty($this->rawData[$key]) && !$field->isRequired()) { |
281 | continue; |
282 | } |
283 | |
284 | if (!$field->validate($this->rawData[$key] ?? null, $this->rawData)) { |
285 | $valid = false; |
286 | $this->errors[$key] = $field->getErrors(); |
287 | } |
288 | } |
289 | return $valid; |
290 | } |
291 | |
292 | /** |
293 | * Get all reported errors |
294 | * |
295 | * @return array |
296 | * @codeCoverageIgnore trivial |
297 | */ |
298 | public function getErrors() |
299 | { |
300 | return $this->errors; |
301 | } |
302 | } |