PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.9.1
Secure Custom Fields v6.9.1
6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / includes / class-scf-json-schema-validator.php
secure-custom-fields / includes Last commit date
Blocks 1 week ago Datastore 1 month ago Meta 1 year ago abilities 1 week ago admin 1 week ago ajax 1 month ago api 3 days ago fields 3 days ago forms 3 days ago legacy 1 year ago locations 1 year ago post-types 2 months ago rest-api 1 week ago walkers 1 year ago acf-bidirectional-functions.php 1 year ago acf-field-functions.php 2 months ago acf-field-group-functions.php 7 months ago acf-form-functions.php 1 year ago acf-helper-functions.php 1 year ago acf-hook-functions.php 1 year ago acf-input-functions.php 7 months ago acf-internal-post-type-functions.php 7 months ago acf-meta-functions.php 3 weeks ago acf-post-functions.php 1 year ago acf-post-type-functions.php 1 year ago acf-taxonomy-functions.php 1 year ago acf-user-functions.php 1 week ago acf-utility-functions.php 1 year ago acf-value-functions.php 1 year ago acf-wp-functions.php 3 days ago assets.php 1 week ago blocks-auto-inline-editing.php 2 months ago blocks.php 3 weeks ago class-acf-data.php 10 months ago class-acf-internal-post-type.php 1 week ago class-acf-options-page.php 1 year ago class-acf-site-health.php 3 months ago class-scf-json-schema-validator.php 6 months ago class-scf-schema-builder.php 2 months ago compatibility.php 1 year ago datastore.php 1 month ago deprecated.php 1 year ago fields.php 10 months ago index.php 1 year ago l10n.php 1 year ago local-fields.php 1 year ago local-json.php 1 month ago local-meta.php 1 year ago locations.php 1 year ago loop.php 10 months ago media.php 1 year ago rest-api.php 10 months ago revisions.php 1 month ago scf-ui-options-page-functions.php 1 year ago third-party.php 7 months ago upgrades.php 3 weeks ago validation.php 10 months ago wpml.php 1 year ago
class-scf-json-schema-validator.php
276 lines
1 <?php
2 /**
3 * JSON Schema Validator for SCF entities
4 *
5 * @package SCF
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9 exit; // Exit if accessed directly
10 }
11
12 if ( ! class_exists( 'SCF_JSON_Schema_Validator' ) ) :
13
14 /**
15 * SCF JSON Schema Validator
16 *
17 * Validates JSON data against schemas for SCF entities. Currently supports post types.
18 * Uses the justinrainbow/json-schema library for validation.
19 *
20 * @since SCF 6.x
21 */
22 class SCF_JSON_Schema_Validator {
23
24 /**
25 * Required schema files for SCF abilities.
26 *
27 * @var array
28 */
29 public const REQUIRED_SCHEMAS = array( 'post-type', 'taxonomy', 'ui-options-page', 'field-group', 'internal-properties', 'scf-identifier' );
30
31 /**
32 * The last validation errors.
33 *
34 * @var array
35 */
36 private $validation_errors = array();
37
38 /**
39 * Base path for schema files.
40 *
41 * @var string
42 */
43 private $schema_path;
44
45 /**
46 * Constructor.
47 */
48 public function __construct() {
49 $this->schema_path = acf_get_path( 'schemas/' );
50 }
51
52
53
54 /**
55 * Smart validation method that auto-detects input type.
56 *
57 * @param mixed $input File path, JSON string, or parsed data to validate.
58 * @param string $schema_name The name of the schema file (without .schema.json extension).
59 * @return bool True if valid, false otherwise.
60 */
61 public function validate( $input, $schema_name ) {
62 // Auto-detect input type and handle appropriately
63 if ( is_string( $input ) ) {
64 if ( file_exists( $input ) ) {
65 // It's a file path
66 return $this->validate_file( $input, $schema_name );
67 } else {
68 // It's a JSON string
69 return $this->validate_json( $input, $schema_name );
70 }
71 }
72 // It's already parsed data
73 return $this->validate_data( $input, $schema_name );
74 }
75
76 /**
77 * Validates parsed data against a schema.
78 *
79 * @param array|object $data The data to validate (arrays are converted to objects).
80 * @param string $schema_name The name of the schema file (without .schema.json extension).
81 * @return bool True if valid, false otherwise.
82 */
83 public function validate_data( $data, $schema_name ) {
84 $this->clear_validation_errors();
85
86 $schema = $this->load_schema( $schema_name );
87 if ( ! $schema ) {
88 $this->add_validation_error( 'system', 'Failed to load schema: ' . $schema_name );
89 return false;
90 }
91
92 // Convert arrays to objects recursively for JsonSchema validation (library expects objects)
93 if ( is_array( $data ) ) {
94 $data = json_decode( wp_json_encode( $data ) );
95 }
96
97 // Create schema storage and register schemas for $ref support.
98 // Use full file:// URIs so relative refs resolve correctly within the schemas directory.
99 $schema_storage = new JsonSchema\SchemaStorage();
100
101 // Build base URI for the schemas directory.
102 $schemas_base_uri = 'file://' . realpath( $this->schema_path ) . '/';
103
104 // Register common schema (referenced by post-type, taxonomy, ui-options-page, field-group).
105 $common_schema_path = $this->schema_path . 'common.schema.json';
106 $common_schema_content = wp_json_file_decode( $common_schema_path );
107 $schema_storage->addSchema( $schemas_base_uri . 'common.schema.json', $common_schema_content );
108
109 // Register field schema (referenced by field-group).
110 $field_schema_path = $this->schema_path . 'field.schema.json';
111 $field_schema_content = wp_json_file_decode( $field_schema_path );
112 if ( $field_schema_content ) {
113 $schema_storage->addSchema( $schemas_base_uri . 'field.schema.json', $field_schema_content );
114 }
115
116 // Register main schema with full path so relative refs resolve to sibling schemas.
117 $main_schema_uri = $schemas_base_uri . $schema_name . '.schema.json';
118 $schema_storage->addSchema( $main_schema_uri, $schema );
119
120 $validator = new JsonSchema\Validator( new JsonSchema\Constraints\Factory( $schema_storage ) );
121 $validator->validate( $data, $schema );
122
123 foreach ( $validator->getErrors() as $error ) {
124 $this->add_validation_error( $error['property'], $error['message'] );
125 }
126
127 return $validator->isValid();
128 }
129
130 /**
131 * Loads a schema file.
132 *
133 * @param string $schema_name The name of the schema file (without .schema.json extension).
134 * @return object|null The loaded schema object, or null on failure.
135 */
136 public function load_schema( $schema_name ) {
137 $schema_file = $this->schema_path . $schema_name . '.schema.json';
138
139 if ( ! file_exists( $schema_file ) || ! is_readable( $schema_file ) ) {
140 return null;
141 }
142
143 $schema_content = file_get_contents( $schema_file );
144 if ( false === $schema_content ) {
145 return null;
146 }
147
148 try {
149 return json_decode( $schema_content, false, 512, JSON_THROW_ON_ERROR );
150 } catch ( JsonException $e ) {
151 return null;
152 }
153 }
154
155
156 /**
157 * Validates that all required schemas are available.
158 *
159 * @since 6.6.0
160 * @return bool True if all required schemas load successfully, false otherwise.
161 */
162 public function validate_required_schemas() {
163 foreach ( self::REQUIRED_SCHEMAS as $schema_name ) {
164 if ( ! $this->load_schema( $schema_name ) ) {
165 return false;
166 }
167 }
168 return true;
169 }
170
171 /**
172 * Gets the validation errors from the last validation attempt.
173 *
174 * @return array Array of validation errors with 'field' and 'message' keys.
175 */
176 public function get_validation_errors() {
177 return $this->validation_errors;
178 }
179
180 /**
181 * Checks if there are any validation errors.
182 *
183 * @return bool True if there are validation errors, false otherwise.
184 */
185 public function has_validation_errors() {
186 return ! empty( $this->validation_errors );
187 }
188
189 /**
190 * Gets validation errors formatted as a string.
191 *
192 * @param string $separator The separator between error messages.
193 * @return string The formatted error message.
194 */
195 public function get_validation_errors_string( $separator = '; ' ) {
196 $messages = array();
197 foreach ( $this->validation_errors as $error ) {
198 $field_info = ! empty( $error['field'] ) ? '[' . $error['field'] . '] ' : '';
199 $messages[] = $field_info . $error['message'];
200 }
201 return implode( $separator, $messages );
202 }
203
204 /**
205 * Adds a validation error.
206 *
207 * @param string $field The field that has the error.
208 * @param string $message The error message.
209 */
210 private function add_validation_error( $field, $message ) {
211 $this->validation_errors[] = array(
212 'field' => $field,
213 'message' => $message,
214 );
215 }
216
217 /**
218 * Clears all validation errors.
219 */
220 private function clear_validation_errors() {
221 $this->validation_errors = array();
222 }
223
224
225
226 /**
227 * Validates JSON string data.
228 *
229 * @param string $json_string The JSON string to validate.
230 * @param string $schema_name The name of the schema to validate against.
231 * @return bool True if valid, false otherwise.
232 */
233 public function validate_json( $json_string, $schema_name ) {
234 $this->clear_validation_errors();
235
236 try {
237 $data = json_decode( $json_string, false, 512, JSON_THROW_ON_ERROR );
238 } catch ( JsonException $e ) {
239 $this->add_validation_error( 'json', 'Invalid JSON: ' . $e->getMessage() );
240 return false;
241 }
242
243 return $this->validate_data( $data, $schema_name );
244 }
245
246 /**
247 * Validates a JSON file.
248 *
249 * @param string $file_path Path to the JSON file.
250 * @param string $schema_name The name of the schema to validate against.
251 * @return bool True if valid, false otherwise.
252 */
253 public function validate_file( $file_path, $schema_name ) {
254 $this->clear_validation_errors();
255
256 if ( ! file_exists( $file_path ) || ! is_readable( $file_path ) ) {
257 $this->add_validation_error( 'file', 'File does not exist: ' . $file_path );
258 return false;
259 }
260
261 $json_content = file_get_contents( $file_path );
262
263 if ( false === $json_content ) {
264 $this->add_validation_error( 'file', 'Could not read file: ' . $file_path );
265 return false;
266 }
267
268 return $this->validate_json( $json_content, $schema_name );
269 }
270 }
271
272 // Initialize validator instance.
273 acf_new_instance( 'SCF_JSON_Schema_Validator' );
274
275 endif; // class_exists check
276