| 1 |
<?php |
| 2 |
/** |
| 3 |
* Normalization for executable-free catalog editing field contracts. |
| 4 |
* |
| 5 |
* @package Booking Calendar |
| 6 |
* @since 11.6.0 |
| 7 |
*/ |
| 8 |
|
| 9 |
if ( ! defined( 'ABSPATH' ) ) { |
| 10 |
exit; |
| 11 |
} |
| 12 |
|
| 13 |
/** |
| 14 |
* Normalize the common field shape without deciding domain field availability. |
| 15 |
* |
| 16 |
* This class exists so every catalog can expose the same safe browser contract |
| 17 |
* while keeping field selection, authorization, validation, and mutations in |
| 18 |
* its domain module. |
| 19 |
* |
| 20 |
* @since 11.6.0 |
| 21 |
*/ |
| 22 |
final class WPBC_UI_Catalog_Inline_Field_Schema { |
| 23 |
|
| 24 |
/** |
| 25 |
* Supported browser control types. |
| 26 |
* |
| 27 |
* @var array<int,string> |
| 28 |
*/ |
| 29 |
const CONTROL_TYPES = array( 'text', 'textarea', 'number', 'select' ); |
| 30 |
|
| 31 |
/** |
| 32 |
* Normalize a list of domain-authorized field definitions. |
| 33 |
* |
| 34 |
* Unknown properties, callbacks, objects, and malformed options are removed. |
| 35 |
* A domain must still validate the field and its value again at preview and |
| 36 |
* apply time. |
| 37 |
* |
| 38 |
* @param mixed $raw_fields Domain field definitions. |
| 39 |
* @return array<int,array<string,mixed>> Browser-safe field definitions. |
| 40 |
*/ |
| 41 |
public static function normalize_fields( $raw_fields ) { |
| 42 |
$normalized_fields = array(); |
| 43 |
|
| 44 |
foreach ( is_array( $raw_fields ) ? $raw_fields : array() as $raw_field ) { |
| 45 |
$normalized_field = self::normalize_field( $raw_field ); |
| 46 |
if ( ! empty( $normalized_field ) ) { |
| 47 |
$normalized_fields[] = $normalized_field; |
| 48 |
} |
| 49 |
} |
| 50 |
|
| 51 |
return $normalized_fields; |
| 52 |
} |
| 53 |
|
| 54 |
/** |
| 55 |
* Index normalized fields by their stable key. |
| 56 |
* |
| 57 |
* @param mixed $raw_fields Domain field definitions. |
| 58 |
* @return array<string,array<string,mixed>> Fields keyed by field key. |
| 59 |
*/ |
| 60 |
public static function index_fields( $raw_fields ) { |
| 61 |
$indexed_fields = array(); |
| 62 |
|
| 63 |
foreach ( self::normalize_fields( $raw_fields ) as $field ) { |
| 64 |
$indexed_fields[ $field['key'] ] = $field; |
| 65 |
} |
| 66 |
|
| 67 |
return $indexed_fields; |
| 68 |
} |
| 69 |
|
| 70 |
/** |
| 71 |
* Normalize one common field definition. |
| 72 |
* |
| 73 |
* @param mixed $raw_field Raw field definition. |
| 74 |
* @return array<string,mixed> Normalized definition or an empty array. |
| 75 |
*/ |
| 76 |
private static function normalize_field( $raw_field ) { |
| 77 |
if ( ! is_array( $raw_field ) ) { |
| 78 |
return array(); |
| 79 |
} |
| 80 |
|
| 81 |
$field_key = isset( $raw_field['key'] ) && is_scalar( $raw_field['key'] ) ? sanitize_key( (string) $raw_field['key'] ) : ''; |
| 82 |
$control_type = isset( $raw_field['type'] ) && is_scalar( $raw_field['type'] ) ? sanitize_key( (string) $raw_field['type'] ) : ''; |
| 83 |
if ( '' === $field_key || ! in_array( $control_type, self::CONTROL_TYPES, true ) ) { |
| 84 |
return array(); |
| 85 |
} |
| 86 |
|
| 87 |
$normalized_field = array( |
| 88 |
'key' => $field_key, |
| 89 |
'column' => isset( $raw_field['column'] ) && is_scalar( $raw_field['column'] ) ? sanitize_key( (string) $raw_field['column'] ) : '', |
| 90 |
'label' => isset( $raw_field['label'] ) && is_scalar( $raw_field['label'] ) ? wp_strip_all_tags( (string) $raw_field['label'] ) : $field_key, |
| 91 |
'type' => $control_type, |
| 92 |
'value' => isset( $raw_field['value'] ) && is_scalar( $raw_field['value'] ) ? (string) $raw_field['value'] : '', |
| 93 |
'options' => self::normalize_choices( isset( $raw_field['options'] ) ? $raw_field['options'] : array() ), |
| 94 |
); |
| 95 |
|
| 96 |
foreach ( array( 'prefix', 'suffix', 'help', 'section' ) as $property_name ) { |
| 97 |
if ( isset( $raw_field[ $property_name ] ) && is_scalar( $raw_field[ $property_name ] ) ) { |
| 98 |
$normalized_field[ $property_name ] = wp_strip_all_tags( (string) $raw_field[ $property_name ] ); |
| 99 |
} |
| 100 |
} |
| 101 |
foreach ( array( 'min', 'max', 'maxlength', 'default_value', 'slider_min', 'slider_max', 'slider_step' ) as $property_name ) { |
| 102 |
if ( isset( $raw_field[ $property_name ] ) && is_numeric( $raw_field[ $property_name ] ) ) { |
| 103 |
$normalized_field[ $property_name ] = (float) $raw_field[ $property_name ]; |
| 104 |
} |
| 105 |
} |
| 106 |
if ( isset( $raw_field['step'] ) && is_scalar( $raw_field['step'] ) ) { |
| 107 |
$raw_step = strtolower( trim( (string) $raw_field['step'] ) ); |
| 108 |
if ( 'any' === $raw_step ) { |
| 109 |
$normalized_field['step'] = 'any'; |
| 110 |
} elseif ( is_numeric( $raw_step ) ) { |
| 111 |
$normalized_field['step'] = (float) $raw_step; |
| 112 |
} |
| 113 |
} |
| 114 |
if ( isset( $raw_field['operations'] ) ) { |
| 115 |
$normalized_field['operations'] = self::normalize_choices( $raw_field['operations'], 'id' ); |
| 116 |
} |
| 117 |
|
| 118 |
return $normalized_field; |
| 119 |
} |
| 120 |
|
| 121 |
/** |
| 122 |
* Normalize select options or bulk operations into value-label records. |
| 123 |
* |
| 124 |
* @param mixed $raw_choices Raw choices. |
| 125 |
* @param string $value_key Choice key containing the stable value. |
| 126 |
* @return array<int,array<string,string>> Browser-safe choices. |
| 127 |
*/ |
| 128 |
private static function normalize_choices( $raw_choices, $value_key = 'value' ) { |
| 129 |
$normalized_choices = array(); |
| 130 |
|
| 131 |
foreach ( is_array( $raw_choices ) ? $raw_choices : array() as $raw_key => $raw_choice ) { |
| 132 |
if ( is_array( $raw_choice ) ) { |
| 133 |
$choice_value = isset( $raw_choice[ $value_key ] ) && is_scalar( $raw_choice[ $value_key ] ) ? (string) $raw_choice[ $value_key ] : ''; |
| 134 |
$choice_label = isset( $raw_choice['label'] ) && is_scalar( $raw_choice['label'] ) ? (string) $raw_choice['label'] : $choice_value; |
| 135 |
} else { |
| 136 |
$is_list_choice = is_int( $raw_key ); |
| 137 |
$choice_value = $is_list_choice && is_scalar( $raw_choice ) ? (string) $raw_choice : ( is_scalar( $raw_key ) ? (string) $raw_key : '' ); |
| 138 |
$choice_label = is_scalar( $raw_choice ) ? (string) $raw_choice : $choice_value; |
| 139 |
} |
| 140 |
$choice_value = sanitize_key( $choice_value ); |
| 141 |
if ( '' !== $choice_value ) { |
| 142 |
$normalized_choices[] = array( |
| 143 |
$value_key => $choice_value, |
| 144 |
'label' => wp_strip_all_tags( $choice_label ), |
| 145 |
); |
| 146 |
} |
| 147 |
} |
| 148 |
|
| 149 |
return $normalized_choices; |
| 150 |
} |
| 151 |
} |
| 152 |
|