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 * @param array $input The input array
156 */
157 function __construct(array $input) {
158 $this->data = $input;
159 $this->binds = [];
160 $this->evaluation = [
161 'OK' => true,
162 'breakddown' => []
163 ];
164 }
165
166 /**
167 * Evaluates the data
168 *
169 * @author Art <a.molcanovas@gmail.com>
170 * @return Form
171 */
172 function evaluate() {
173 $ok = true;
174
175 foreach ($this->data as $data_key => $data_value) {
176 $breakdown = &$this->evaluation['breakdown'][$data_key];
177
178 if (!is_scalar($data_value)) {
179 $breakdown = [
180 'OK' => false,
181 'global_errors' => [self::E_NONSCALAR],
182 'breakdown' => []
183 ];
184 $ok = false;
185 } else {
186 $local_ok = true;
187 $breakdown['breakdown'] = [];
188
189 if (isset($this->binds[$data_key])) {
190 foreach ($this->binds[$data_key] as $bind_key => $bind_value) {
191 $breakdown['breakdown'][$bind_key] = self::eval_param($data_value, $bind_key, $bind_value);
192
193 if (!$breakdown['breakdown'][$bind_key]) {
194 $local_ok = $ok = false;
195 }
196 }
197 }
198
199 $breakdown['OK'] = $local_ok;
200 $breakdown['global_errors'] = [];
201 }
202 }
203
204 $this->evaluation['OK'] = $ok;
205
206 return $this;
207 }
208
209 /**
210 * Evaluates an element against a requirement
211 *
212 * @author Art <a.molcanovas@gmail.com>
213 * @param string $data_value Element value
214 * @param int $bind_key The requirement identifier constant
215 * @param mixed $bind_value The requirement specs if applicable
216 * @return boolean
217 */
218 protected static function eval_param($data_value, $bind_key, $bind_value) {
219 switch ($bind_key) {
220 case self::R_CONTAIN_LOWERCASE:
221 return (bool)preg_match('/[a-z]/', $data_value);
222 case self::R_CONTAIN_NONALPHANUM:
223 return !((bool)preg_match('/^[a-z0-9]+$/i', trim($data_value)));
224 case self::R_CONTAIN_NUMBER:
225 return (bool)preg_match('/[0-9]/', $data_value);
226 case self::R_CONTAIN_UPPERCASE:
227 return (bool)preg_match('/[A-Z]/', $data_value);
228 case self::R_EMAIL:
229 return (bool)preg_match('/^[a-z0-9_\.\+-]+@[a-z0-9-]+\.[a-z0-9-\.]+$/i', trim($data_value));
230 case self::R_LENGTH_MAX:
231 return strlen(trim($data_value)) <= $bind_value;
232 case self::R_LENGTH_MIN:
233 return strlen($data_value) >= $bind_value;
234 case self::R_NUMERIC:
235 return is_numeric(trim($data_value));
236 case self::R_REGEX:
237 return (bool)preg_match($bind_value, $data_value);
238 case self::R_REQUIRED:
239 return $data_value != '';
240 case self::R_VAL_GT:
241 return ((float)$data_value) > ((float)$bind_value);
242 case self::R_VAL_GTE:
243 return ((float)$data_value) >= ((float)$bind_value);
244 case self::R_VAL_LT:
245 return ((float)$data_value) < ((float)$bind_value);
246 case self::R_VAL_LTE:
247 return ((float)$data_value) <= ((float)$bind_value);
248 case self::R_VAL_RANGE:
249 return is_array($bind_value) ? in_array($data_value, $bind_value) : false;
250 }
251
252 return false;
253 }
254
255 /**
256 * Binds a set of requirements to an element
257 *
258 * @author Art <a.molcanovas@gmail.com>
259 * @param string $element The element key
260 * @param array $requirements Associative array of requirements, where the keys are one of this class' R_*
261 * constants and the values are TRUE, or, if applicable, the required values for that
262 * test.
263 * @return Form
264 */
265 function bind($element, $requirements) {
266 $e = &$this->binds[$element];
267 if (isset($e)) {
268 $e = array_merge($e, $requirements);
269 } else {
270 $e = $requirements;
271 }
272
273 return $this;
274 }
275
276 /**
277 * Returns the evaluation array
278 *
279 * @author Art <a.molcanovas@gmail.com>
280 * @return array
281 */
282 function getEvaluation() {
283 return $this->evaluation;
284 }
285
286 /**
287 * Returns the binds set
288 *
289 * @author Art <a.molcanovas@gmail.com>
290 * @return array
291 */
292 function getBinds() {
293 return $this->binds;
294 }
295
296 }