PluginProbe
ZIP AI – AI Website Builder & AI Agent (Beta) / 0.0.4
ZIP AI – AI Website Builder & AI Agent (Beta) v0.0.4
0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
zip-ai / classes / core / validator.php

validator.php in ZIP AI – AI Website Builder & AI Agent (Beta) 0.0.4, at classes/core/validator.php

105 lines 3.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Validator - Centralized schema-based validation and sanitation
4 *
5 * @package zip-ai
6 */
7
8 namespace ZipAI\Classes\Core;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * Class Validator
17 */
18 class Validator {
19
20 /**
21 * Validate and sanitize input against a JSON schema.
22 *
23 * @param array $schema The JSON schema to validate against.
24 * @param array $input The raw input data.
25 * @return array|\WP_Error Validated and sanitized data on success, WP_Error on failure.
26 */
27 public static function validate( $schema, $input ) {
28 $valid_data = array();
29 $properties = isset( $schema['properties'] ) ? $schema['properties'] : array();
30 $required = isset( $schema['required'] ) ? $schema['required'] : array();
31
32 // Check for missing required fields.
33 foreach ( $required as $field ) {
34 if ( ! isset( $input[ $field ] ) ) {
35 return new \WP_Error(
36 'missing_required_field',
37 sprintf( 'Missing required field: %s', $field )
38 );
39 }
40 }
41
42 // Validate and sanitize each property.
43 foreach ( $properties as $field => $config ) {
44 $value = isset( $input[ $field ] ) ? $input[ $field ] : ( isset( $config['default'] ) ? $config['default'] : null );
45
46 if ( null === $value ) {
47 continue;
48 }
49
50 $type = isset( $config['type'] ) ? $config['type'] : 'string';
51
52 switch ( $type ) {
53 case 'integer':
54 $value = (int) $value;
55 if ( isset( $config['minimum'] ) && $value < $config['minimum'] ) {
56 return new \WP_Error( 'invalid_range', sprintf( '%s is below minimum %d', $field, $config['minimum'] ) );
57 }
58 if ( isset( $config['maximum'] ) && $value > $config['maximum'] ) {
59 return new \WP_Error( 'invalid_range', sprintf( '%s is above maximum %d', $field, $config['maximum'] ) );
60 }
61 break;
62
63 case 'boolean':
64 $value = filter_var( $value, FILTER_VALIDATE_BOOLEAN );
65 break;
66
67 case 'string':
68 if ( ! is_string( $value ) && ! is_scalar( $value ) ) {
69 return new \WP_Error( 'invalid_type', sprintf( '%s must be a string', $field ) );
70 }
71 if ( isset( $config['enum'] ) && ! in_array( $value, $config['enum'], true ) ) {
72 return new \WP_Error( 'invalid_enum', sprintf( 'Invalid value for %s. Expected one of: %s', $field, implode( ', ', $config['enum'] ) ) );
73 }
74 if ( ! is_string( $value ) ) {
75 $value = (string) $value;
76 }
77 // Per-field sanitiser. Schemas may declare:
78 // 'sanitize' => 'sanitize_text_field' | 'sanitize_key'
79 // | 'sanitize_email' | 'esc_url_raw'
80 // | 'wp_kses_post' | callable
81 // Strings that hold rich content (HTML, multi-line) should
82 // omit `sanitize` and run their own per-leaf sanitiser at
83 // write-time.
84 if ( isset( $config['sanitize'] ) ) {
85 $sanitizer = $config['sanitize'];
86 if ( is_callable( $sanitizer ) ) {
87 $value = call_user_func( $sanitizer, $value );
88 }
89 }
90 break;
91
92 case 'array':
93 if ( ! is_array( $value ) ) {
94 return new \WP_Error( 'invalid_type', sprintf( '%s must be an array', $field ) );
95 }
96 break;
97 }
98
99 $valid_data[ $field ] = $value;
100 }
101
102 return $valid_data;
103 }
104 }
105