PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.8.1
Defender Security – Malware Scanner, Login Security & Firewall v5.8.1
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 5.8.1, at framework/base/class-model.php

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