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

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