PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.0.0
Defender Security – Malware Scanner, Login Security & Firewall v6.0.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / framework / base / class-model.php

class-model.php in Defender Security – Malware Scanner, Login Security & Firewall 6.0.0, at framework/base/class-model.php

443 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base model class.
4 *
5 * @package Calotes\Base
6 */
7
8 namespace Calotes\Base;
9
10 use Exception;
11 use ReflectionClass;
12 use Valitron\Validator;
13 use ReflectionProperty;
14
15 /**
16 * Base model class for all models.
17 */
18 abstract class Model extends Component {
19
20 /**
21 * Table name | option name | post type name.
22 *
23 * @var string
24 */
25 protected $table;
26
27 /**
28 * Let validate to run on special scenario.
29 *
30 * @var string
31 */
32 protected $scenario;
33
34 /**
35 * Validation errors.
36 *
37 * @var array
38 */
39 protected $errors = array();
40
41 /**
42 * Array of safe properties allow for mass assign, default is empty, mean accept all.
43 *
44 * @var array
45 */
46 protected $safe = array();
47
48 /**
49 * Store the attribute that should not be export.
50 *
51 * @var array
52 */
53 protected $exclude = array();
54
55 /**
56 * An array hold info to map class attributes to db field name.
57 *
58 * @var array
59 */
60 protected $mapping = array();
61
62 /**
63 * Get the table name.
64 *
65 * @return string|null
66 */
67 public function get_table(): ?string {
68 return $this->table;
69 }
70
71 /**
72 * Run the validation.
73 *
74 * @return bool
75 */
76 public function validate(): bool {
77 $this->before_validate();
78 if ( array() === $this->annotations ) {
79 $this->log( 'Empty annotations.', wd_internal_log() );
80 }
81 $validate = $this->get_validate_rules();
82 $validate->validate();
83 $this->errors = is_array( $validate->errors() ) ? $validate->errors() : array();
84 $this->after_validate();
85
86 return ! count( $this->errors );
87 }
88
89 /**
90 * Override this if you want to trigger something before validation process.
91 */
92 protected function before_validate(): void {
93 }
94
95 /**
96 * Override this if you want to trigger something after validation process.
97 */
98 protected function after_validate(): void {
99 }
100
101 /**
102 * Export the class data.
103 *
104 * @return array
105 */
106 public function export(): array {
107 if ( array() === $this->annotations ) {
108 return $this->export_oldway();
109 }
110 $return = array();
111 foreach ( array_keys( $this->annotations ) as $property ) {
112 if ( $this->has_property( $property ) ) {
113 $return[ $property ] = $this->$property;
114 }
115 }
116
117 return $return;
118 }
119
120 /**
121 * This is for backward compatibility.
122 *
123 * @return array
124 */
125 private function export_oldway(): array {
126 try {
127 $reflection = new ReflectionClass( $this );
128 $props = $reflection->getProperties( ReflectionProperty::IS_PUBLIC );
129 $values = array();
130 foreach ( $props as $prop ) {
131 if ( 'annotations' === $prop->getName() ) {
132 continue;
133 }
134
135 if ( in_array( $prop->getName(), $this->exclude, true ) ) {
136 continue;
137 }
138
139 $value = $prop->getValue( $this );
140 if ( is_null( $value ) ) {
141 $value = '';
142 }
143 $values[ $prop->getName() ] = $value;
144 }
145
146 $this->exclude = array();
147
148 return $values;
149 } catch ( Exception $e ) {
150 return array();
151 }
152 }
153
154 /**
155 * Get the validation errors.
156 *
157 * @return array The validation errors.
158 */
159 public function get_errors(): array {
160 return $this->errors;
161 }
162
163 /**
164 * Get error as formatted string.
165 *
166 * @return string
167 */
168 public function get_formatted_errors(): string {
169 return implode( '<br/>', $this->errors );
170 }
171
172 /**
173 * Get error as formatted string.
174 *
175 * @return int[]|string[]
176 */
177 public function get_error_keys() {
178 return array_keys( $this->errors );
179 }
180
181 /**
182 * Set properties that we should not return on export. Note that this will be wiped after an export done.
183 *
184 * @param array $properties Properties that we should not return on export.
185 */
186 public function set_excludes( array $properties ) {
187 $this->exclude = $properties;
188 }
189
190 /**
191 * Set properties allow for mass assign.
192 *
193 * @param array $properties Properties allow for mass assign.
194 */
195 public function set_safe( array $properties ) {
196 $this->safe = $properties;
197 }
198
199 /**
200 * Loops through the data array and imports the values if they meet certain conditions.
201 *
202 * @param mixed $data The data array to import values from.
203 *
204 * @return void
205 */
206 public function import_old_way( $data ): void {
207 foreach ( $data as $key => $val ) {
208 // Check if we have a safe list.
209 if ( array() !== $this->safe && ! in_array( $key, $this->safe, true ) ) {
210 continue;
211 }
212
213 $allowed = array_keys( $this->export() );
214 if ( ! in_array( $key, $allowed, true ) ) {
215 continue;
216 }
217
218 if ( $this->has_property( $key ) ) {
219 $this->$key = $val;
220 }
221 }
222 }
223
224 /**
225 * Import data into the model.
226 *
227 * @param mixed $data The data array to import values from.
228 *
229 * @return void
230 */
231 public function import( $data ): void {
232 if ( array() === $this->annotations ) {
233 $this->import_old_way( $data );
234 } else {
235 foreach ( array_keys( $this->annotations ) as $property ) {
236 if ( isset( $data[ $property ] ) && $this->has_property( $property ) ) {
237 $this->$property = $data[ $property ];
238 }
239 }
240
241 $this->sanitize();
242 }
243 }
244
245 /**
246 * This method prepares the data for saving in the database.
247 *
248 * @param array $data The data array to import values from.
249 *
250 * @return array The prepared data array.
251 */
252 protected function prepare_data( $data = array() ): array {
253 $scenario = 'import';
254 if ( ! count( $data ) ) {
255 $data = $this->export();
256 $scenario = 'export';
257 }
258 if ( array() === $this->mapping ) {
259 return $data;
260 }
261 foreach ( $this->mapping as $key => $val ) {
262 if ( 'export' === $scenario && isset( $data[ $key ] ) ) {
263 $data[ $val ] = $data[ $key ];
264 unset( $data[ $key ] );
265 } elseif ( 'import' === $scenario && isset( $data[ $val ] ) ) {
266 $data[ $key ] = $data[ $val ];
267 unset( $data[ $val ] );
268 }
269 }
270
271 return $data;
272 }
273
274 /**
275 * Run a filter for casting type.
276 *
277 * @return void
278 */
279 protected function sanitize() {
280 if ( array() === $this->annotations ) {
281 return;
282 }
283
284 foreach ( $this->annotations as $property => $meta ) {
285 if ( ! $this->has_property( $property ) ) {
286 // Todo: log it as this is not a good behavior.
287 continue;
288 }
289 $type = $meta['type'];
290 if ( false === $type ) {
291 // Without a type, won't allow it.
292 $this->$property = null;
293 continue;
294 }
295
296 $value = $this->$property;
297 // Cast it first.
298 if ( 'boolean' === $type || 'bool' === $type ) {
299 $value = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
300 } else {
301 settype( $value, $type );
302 }
303
304 if ( false !== $meta['sanitize'] ) {
305 $func = $meta['sanitize'];
306 if ( ! function_exists( $func ) ) {
307 // The formatting.php still need to be included.
308 include_once ABSPATH . WPINC . '/formatting.php';
309 }
310 if ( is_array( $value ) ) {
311 $value = $this->sanitize_array( $value, $func );
312 } else {
313 $value = $func( $value );
314 }
315 }
316
317 $this->$property = $value;
318 }
319 }
320
321 /**
322 * Sanitize an array recursively.
323 *
324 * @param array $arr The array to be sanitized.
325 * @param callable $sanitize The function to sanitize the array values.
326 *
327 * @return array The sanitized array.
328 */
329 protected function sanitize_array( $arr, $sanitize ) {
330 foreach ( $arr as &$value ) {
331 if ( is_array( $value ) ) {
332 $value = $this->sanitize_array( $value, $sanitize );
333 } else {
334 $value = $sanitize( $value );
335 }
336 }
337
338 return $arr;
339 }
340
341 /**
342 * Prepare the validation object.
343 *
344 * @return Validator
345 */
346 protected function get_validate_rules(): Validator {
347 $v = new Validator( $this->export() );
348 foreach ( $this->annotations as $property => $meta ) {
349 if ( ! $this->has_property( $property ) ) {
350 continue;
351 }
352 if ( false === $meta['rule'] ) {
353 continue;
354 }
355
356 $rules = explode( '|', $meta['rule'] );
357 foreach ( $rules as $str ) {
358 $v->rule( $str, $property );
359 }
360 }
361
362 return $v;
363 }
364
365 /**
366 * Export the data type of public properties.
367 *
368 * @since 4.8.0
369 * @return array
370 */
371 public function export_type(): array {
372 $reflection = new ReflectionClass( $this );
373 $props = $reflection->getProperties( ReflectionProperty::IS_PUBLIC );
374
375 if ( array() === $this->annotations ) {
376 return $this->export_type_oldway();
377 }
378
379 $types = array();
380 foreach ( array_keys( $this->annotations ) as $property ) {
381 if ( $this->has_property( $property ) ) {
382 $type = isset( $this->annotations[ $property ]['type'] )
383 ? (string) $this->annotations[ $property ]['type']
384 : 'string';
385
386 $types[ $property ] = $this->map_format( $type );
387 }
388 }
389
390 return $types;
391 }
392
393 /**
394 * Backward compatibility for exporting the data type of class properties.
395 *
396 * @since 4.8.0
397 * @return array
398 */
399 private function export_type_oldway(): array {
400 $types = array();
401 $reflection = new ReflectionClass( $this );
402 $props = $reflection->getProperties( ReflectionProperty::IS_PUBLIC );
403
404 foreach ( $props as $prop ) {
405 $rp = new ReflectionProperty( $prop->class, $prop->name );
406 if ( preg_match( '/@var\s+([^\s]+)/', $rp->getDocComment(), $matches ) ) {
407 $type = isset( $matches[1] ) ? (string) $matches[1] : 'string';
408 $types[] = $this->map_format( $type );
409 }
410 }
411
412 return $types;
413 }
414
415 /**
416 * Map the format for a given data type.
417 *
418 * @param string $type The data type.
419 *
420 * @since 4.8.0
421 * @return string
422 */
423 private function map_format( string $type ): string {
424 $format = '';
425 switch ( $type ) {
426 case 'int':
427 case 'integer':
428 case 'bool':
429 case 'boolean':
430 $format = '%d';
431 break;
432 case 'float':
433 $format = '%f';
434 break;
435 // Handle other data types like array, object, string.
436 default:
437 $format = '%s';
438 break;
439 }
440 return $format;
441 }
442 }
443