240 | | * Validate an object and its properties. |
241 | | * |
242 | | * @param object $object The value to validate as an object. |
243 | | * @param string $prop The name of the property, used in error reporting. |
244 | | * @param array $schema The schema for the property, used for validation. |
245 | | * |
246 | | * @return bool |
247 | | */ |
248 | | protected function validate_object( $object, $prop, $schema ) { |
249 | | if ( ! is_object( $object ) ) { |
250 | | $this->messages->add( |
251 | | 'error', |
252 | | sprintf( |
253 | | __( 'The %s property must contain an object value.', 'wporg-plugins' ), |
254 | | '<code>' . $prop . '</code>' |
255 | | ) |
256 | | ); |
257 | | $this->append_error_data( $prop, 'error' ); |
258 | | |
259 | | return false; |
260 | | } |
261 | | |
262 | | $results = array(); |
263 | | |
264 | | if ( isset( $schema['required'] ) ) { |
265 | | foreach ( $schema['required'] as $required_prop ) { |
266 | | if ( ! property_exists( $object, $required_prop ) ) { |
267 | | $this->messages->add( |
268 | | 'error', |
269 | | sprintf( |
270 | | __( 'The %1$s property is required in the %2$s object.', 'wporg-plugins' ), |
271 | | '<code>' . $required_prop . '</code>', |
272 | | '<code>' . $prop . '</code>' |
273 | | ) |
274 | | ); |
275 | | $this->append_error_data( "$prop:$required_prop", 'error' ); |
276 | | $results[] = false; |
277 | | } |
278 | | } |
279 | | } |
280 | | |
281 | | if ( isset( $schema['properties'] ) ) { |
282 | | foreach ( $schema['properties'] as $subprop => $subschema ) { |
283 | | if ( ! isset( $object->$subprop ) ) { |
284 | | continue; |
285 | | } |
286 | | |
287 | | if ( isset( $subschema['type'] ) ) { |
288 | | $results[] = $this->route_validation_for_type( |
289 | | $subschema['type'], |
290 | | $object->$subprop, |
291 | | "$prop:$subprop", |
292 | | $subschema |
293 | | ); |
294 | | } |
295 | | } |
296 | | } |
297 | | |
298 | | if ( isset( $schema['additionalProperties'] ) ) { |
299 | | if ( false === $schema['additionalProperties'] ) { |
300 | | foreach ( array_keys( get_object_vars( $object ) ) as $key ) { |
301 | | if ( ! isset( $schema['properties'][ $key ] ) ) { |
302 | | $this->messages->add( |
303 | | 'warning', |
304 | | sprintf( |
305 | | __( '%1$s is not a valid property in the %2$s object.', 'wporg-plugins' ), |
306 | | '<code>' . $key . '</code>', |
307 | | '<code>' . $prop . '</code>' |
308 | | ) |
309 | | ); |
310 | | $this->append_error_data( "$prop:$key", 'warning' ); |
311 | | $results[] = false; |
312 | | continue; |
313 | | } |
314 | | } |
315 | | } elseif ( isset( $schema['additionalProperties']['type'] ) ) { |
316 | | foreach ( $object as $subprop => $subvalue ) { |
317 | | $results[] = $this->route_validation_for_type( |
318 | | $schema['additionalProperties']['type'], |
319 | | $subvalue, |
320 | | "$prop:$subprop", |
321 | | $schema['additionalProperties'] |
322 | | ); |
323 | | } |
324 | | } |
325 | | } |
326 | | |
327 | | return ! in_array( false, $results, true ); |
328 | | } |
329 | | |
330 | | /** |
331 | | * Validate an array and its items. |
332 | | * |
333 | | * @param array $array The value to validate as an array. |
334 | | * @param string $prop The name of the property, used in error reporting. |
335 | | * @param array $schema The schema for the property, used for validation. |
336 | | * |
337 | | * @return bool |
338 | | */ |
339 | | protected function validate_array( $array, $prop, $schema ) { |
340 | | if ( ! is_array( $array ) ) { |
341 | | $this->messages->add( |
342 | | 'error', |
343 | | sprintf( |
344 | | __( 'The %s property must contain an array value.', 'wporg-plugins' ), |
345 | | '<code>' . $prop . '</code>' |
346 | | ) |
347 | | ); |
348 | | $this->append_error_data( $prop, 'error' ); |
349 | | |
350 | | return false; |
351 | | } |
352 | | |
353 | | if ( isset( $schema['items']['type'] ) ) { |
354 | | $results = array(); |
355 | | $index = 0; |
356 | | |
357 | | foreach ( $array as $item ) { |
358 | | $results[] = $this->route_validation_for_type( |
359 | | $schema['items']['type'], |
360 | | $item, |
361 | | $prop . "[$index]", |
362 | | $schema['items'] |
363 | | ); |
364 | | $index ++; |
365 | | } |
366 | | |
367 | | return ! in_array( false, $results, true ); |
368 | | } |
369 | | |
370 | | return true; |
371 | | } |
372 | | |
373 | | /** |
374 | | * Validate a string. |
375 | | * |
376 | | * @param string $string The value to validate as a string. |
377 | | * @param string $prop The name of the property, used in error reporting. |
378 | | * @param array $schema The schema for the property, used for validation. |
379 | | * |
380 | | * @return bool |
381 | | */ |
382 | | protected function validate_string( $string, $prop, $schema ) { |
383 | | if ( ! is_string( $string ) ) { |
384 | | $this->messages->add( |
385 | | 'error', |
386 | | sprintf( |
387 | | __( 'The %s property must contain a string value.', 'wporg-plugins' ), |
388 | | '<code>' . $prop . '</code>' |
389 | | ) |
390 | | ); |
391 | | $this->append_error_data( $prop, 'error' ); |
392 | | |
393 | | return false; |
394 | | } |
395 | | |
396 | | if ( isset( $schema['enum'] ) ) { |
397 | | if ( ! in_array( $string, $schema['enum'], true ) ) { |
398 | | $this->messages->add( |
399 | | 'warning', |
400 | | sprintf( |
401 | | __( '"%1$s" is not a valid value for the %2$s property.', 'wporg-plugins' ), |
402 | | esc_html( $string ), |
403 | | '<code>' . $prop . '</code>' |
404 | | ) |
405 | | ); |
406 | | $this->append_error_data( $prop, 'warning' ); |
407 | | } |
408 | | } |
409 | | |
410 | | if ( isset( $schema['pattern'] ) ) { |
411 | | if ( ! preg_match( '#' . $schema['pattern'] . '#', $string ) ) { |
412 | | $pattern_description = $this->get_human_readable_pattern_description( $schema['pattern'] ); |
413 | | if ( $pattern_description ) { |
414 | | $message = sprintf( |
415 | | $pattern_description, |
416 | | '<code>' . $prop . '</code>' |
417 | | ); |
418 | | } else { |
419 | | $message = sprintf( |
420 | | __( 'The value of %s does not match the required pattern.', 'wporg-plugins' ), |
421 | | '<code>' . $prop . '</code>' |
422 | | ); |
423 | | } |
424 | | |
425 | | $this->messages->add( 'warning', $message ); |
426 | | $this->append_error_data( $prop, 'warning' ); |
427 | | } |
428 | | } |
429 | | |
430 | | return true; |
431 | | } |
432 | | |
433 | | /** |
434 | | * Validate a boolean. |
435 | | * |
436 | | * @param bool $boolean The value to validate as a boolean. |
437 | | * @param string $prop The name of the property, used in error reporting. |
438 | | * |
439 | | * @return bool |
440 | | */ |
441 | | protected function validate_boolean( $boolean, $prop ) { |
442 | | if ( ! is_bool( $boolean ) ) { |
443 | | $this->messages->add( |
444 | | 'error', |
445 | | sprintf( |
446 | | __( 'The %s property must contain a boolean value.', 'wporg-plugins' ), |
447 | | '<code>' . $prop . '</code>' |
448 | | ) |
449 | | ); |
450 | | $this->append_error_data( $prop, 'error' ); |
451 | | |
452 | | return false; |
453 | | } |
454 | | |
455 | | return true; |
456 | | } |
457 | | |
458 | | /** |
459 | | * Send a property value to the correct validator depending on which type(s) it can be. |
460 | | * |
461 | | * @param string|array $valid_types |
462 | | * @param mixed $value |
463 | | * @param string $prop |
464 | | * @param array $schema |
465 | | * |
466 | | * @return bool |
467 | | */ |
468 | | protected function route_validation_for_type( $valid_types, $value, $prop, $schema ) { |
469 | | // There is a single valid type. |
470 | | if ( is_string( $valid_types ) ) { |
471 | | $method = "validate_$valid_types"; |
472 | | return $this->$method( $value, $prop, $schema ); |
473 | | } |
474 | | |
475 | | // There are multiple valid types in an array. |
476 | | foreach ( $valid_types as $type ) { |
477 | | switch ( $type ) { |
478 | | case 'boolean': |
479 | | $check = 'is_bool'; |
480 | | break; |
481 | | default: |
482 | | $check = "is_$type"; |
483 | | break; |
484 | | } |
485 | | |
486 | | if ( $check( $value ) ) { |
487 | | $method = "validate_$type"; |
488 | | return $this->$method( $value, $prop, $schema ); |
489 | | } |
490 | | } |
491 | | |
492 | | // Made it this far, it's none of the valid types. |
493 | | $this->messages->add( |
494 | | 'error', |
495 | | sprintf( |
496 | | __( 'The %1$s property must contain a value that is one of these types: %2$s', 'wporg-plugins' ), |
497 | | '<code>' . $prop . '</code>', |
498 | | // translators: used between list items, there is a space after the comma. |
499 | | '<code>' . implode( '</code>' . __( ', ', 'wporg-plugins' ) . '<code>', $valid_types ) . '</code>' |
500 | | ) |
501 | | ); |
502 | | $this->append_error_data( $prop, 'error' ); |
503 | | |
504 | | return false; |
505 | | } |
506 | | |
507 | | /** |