1 <?php
2
3 namespace Alo\Validators;
4
5 if(!defined('GEN_START')) {
6 http_response_code(404);
7 die();
8 }
9
10 /**
11 * Form validator
12 *
13 * @author Art <a.molcanovas@gmail.com>
14 * @package Validators
15 */
16 class Form {
17
18 /**
19 * Defines a requirement as "email format"
20 *
21 * @var int
22 */
23 const R_EMAIL = 101;
24
25 /**
26 * Defines a requirement as "value required"
27 *
28 * @var int
29 */
30 const R_REQUIRED = 102;
31
32 /**
33 * Defines a requirement as "must be numeric"
34 *
35 * @var int
36 */
37 const R_NUMERIC = 103;
38
39 /**
40 * Defines a requirement as "minimum length"
41 *
42 * @var int
43 */
44 const R_LENGTH_MIN = 104;
45
46 /**
47 * Defines a requirement as "maximum length"
48 *
49 * @var int
50 */
51 const R_LENGTH_MAX = 105;
52
53 /**
54 * Defines a requirement as "must match regular expression"
55 *
56 * @var int
57 */
58 const R_REGEX = 106;
59
60 /**
61 * Defines a requirement as "must contain uppercase character"
62 *
63 * @var int
64 */
65 const R_CONTAIN_UPPERCASE = 107;
66
67 /**
68 * Defines a requirement as "must contain lowercase character"
69 *
70 * @var int
71 */
72 const R_CONTAIN_LOWERCASE = 108;
73
74 /**
75 * Defines a requirement as "must contain number"
76 *
77 * @var int
78 */
79 const R_CONTAIN_NUMBER = 109;
80
81 /**
82 * Defines a requirement as "must contain non-alphanumeric character"
83 *
84 * @var int
85 */
86 const R_CONTAIN_NONALPHANUM = 110;
87
88 /**
89 * Defines a requirement as "numeric value must be lower than"
90 *
91 * @var int
92 */
93 const R_VAL_LT = 111;
94
95 /**
96 * Defines a requirement as "numeric value must be greater than"
97 *
98 * @var int
99 */
100 const R_VAL_GT = 112;
101
102 /**
103 * Defines a requirement as "must be within a supplied range of values"
104 *
105 * @var int
106 */
107 const R_VAL_RANGE = 113;
108
109 /**
110 * Defines a requirement as "numeric value must be lower than or equal to"
111 *
112 * @var int
113 */
114 const R_VAL_LTE = 114;
115
116 /**
117 * Defines a requirement as "numeric value must be greater than or equal to"
118 *
119 * @var int
120 */
121 const R_VAL_GTE = 115;
122
123 /**
124 * Error when a value is non-scalar
125 *
126 * @var int
127 */
128 const E_NONSCALAR = 400;
129
130 /**
131 * Supplied data array
132 *
133 * @var array
134 */
135 protected $data;
136
137 /**
138 * Element requirements
139 *
140 * @var array
141 */
142 protected $binds;
143
144 /**
145 * Data array post-evaluation
146 *
147 * @var array
148 */
149 protected $evaluation;
150
151 /**
152 * Instantiates the class and loads the input array
153 *
154 * @author Art <a.molcanovas@gmail.com>
155 *
156 * @param array $input The input array
157 */
158 function __construct(array $input) {
159 $this->data = $input;
160 $this->binds = [];
161 $this->evaluation = [
162 'OK' => true,
163 'breakddown' => []
164 ];
165 }
166
167 /**
168 * Evaluates the data
169 *
170 * @author Art <a.molcanovas@gmail.com>
171 * @return Form
172 */
173 function evaluate() {
174 $ok = true;
175
176 foreach($this->data as $data_key => $data_value) {
177 $breakdown = &$this->evaluation['breakdown'][$data_key];
178
179 if(!is_scalar($data_value)) {
180 $breakdown = [
181 'OK' => false,
182 'global_errors' => [self::E_NONSCALAR],
183 'breakdown' => []
184 ];
185 $ok = false;
186 } else {
187 $local_ok = true;
188 $breakdown['breakdown'] = [];
189
190 if(isset($this->binds[$data_key])) {
191 foreach($this->binds[$data_key] as $bind_key => $bind_value) {
192 $breakdown['breakdown'][$bind_key] = self::eval_param($data_value, $bind_key, $bind_value);
193
194 if(!$breakdown['breakdown'][$bind_key]) {
195 $local_ok = $ok = false;
196 }
197 }
198 }
199
200 $breakdown['OK'] = $local_ok;
201 $breakdown['global_errors'] = [];
202 }
203 }
204
205 $this->evaluation['OK'] = $ok;
206
207 return $this;
208 }
209
210 /**
211 * Evaluates an element against a requirement
212 *
213 * @author Art <a.molcanovas@gmail.com>
214 *
215 * @param string $data_value Element value
216 * @param int $bind_key The requirement identifier constant
217 * @param mixed $bind_value The requirement specs if applicable
218 *
219 * @return boolean
220 */
221 protected static function eval_param($data_value, $bind_key, $bind_value) {
222 switch($bind_key) {
223 case self::R_CONTAIN_LOWERCASE:
224 return (bool)preg_match('/[a-z]/', $data_value);
225 case self::R_CONTAIN_NONALPHANUM:
226 return !((bool)preg_match('/^[a-z0-9]+$/i', trim($data_value)));
227 case self::R_CONTAIN_NUMBER:
228 return (bool)preg_match('/[0-9]/', $data_value);
229 case self::R_CONTAIN_UPPERCASE:
230 return (bool)preg_match('/[A-Z]/', $data_value);
231 case self::R_EMAIL:
232 return (bool)preg_match('/^[a-z0-9_\.\+-]+@[a-z0-9-]+\.[a-z0-9-\.]+$/i', trim($data_value));
233 case self::R_LENGTH_MAX:
234 return strlen(trim($data_value)) <= $bind_value;
235 case self::R_LENGTH_MIN:
236 return strlen($data_value) >= $bind_value;
237 case self::R_NUMERIC:
238 return is_numeric(trim($data_value));
239 case self::R_REGEX:
240 return (bool)preg_match($bind_value, $data_value);
241 case self::R_REQUIRED:
242 return $data_value != '';
243 case self::R_VAL_GT:
244 return ((float)$data_value) > ((float)$bind_value);
245 case self::R_VAL_GTE:
246 return ((float)$data_value) >= ((float)$bind_value);
247 case self::R_VAL_LT:
248 return ((float)$data_value) < ((float)$bind_value);
249 case self::R_VAL_LTE:
250 return ((float)$data_value) <= ((float)$bind_value);
251 case self::R_VAL_RANGE:
252 return is_array($bind_value) ? in_array($data_value, $bind_value) : false;
253 }
254
255 return false;
256 }
257
258 /**
259 * Binds a set of requirements to an element
260 *
261 * @author Art <a.molcanovas@gmail.com>
262 *
263 * @param string $element The element key
264 * @param array $requirements Associative array of requirements, where the keys are one of this class' R_*
265 * constants and the values are TRUE, or, if applicable, the required values for that
266 * test.
267 *
268 * @return Form
269 */
270 function bind($element, $requirements) {
271 $e = &$this->binds[$element];
272 if(isset($e)) {
273 $e = array_merge($e, $requirements);
274 } else {
275 $e = $requirements;
276 }
277
278 return $this;
279 }
280
281 /**
282 * Returns the evaluation array
283 *
284 * @author Art <a.molcanovas@gmail.com>
285 * @return array
286 */
287 function getEvaluation() {
288 return $this->evaluation;
289 }
290
291 /**
292 * Returns the binds set
293 *
294 * @author Art <a.molcanovas@gmail.com>
295 * @return array
296 */
297 function getBinds() {
298 return $this->binds;
299 }
300
301 }