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