$meta ) { if ( ! is_array( $meta ) ) { unset( $pack['schema']['props'][ $prop_key ] ); continue; } $meta['type'] = isset( $meta['type'] ) ? $meta['type'] : 'string'; if ( ! in_array( $meta['type'], $allowed_prop_types, true ) ) { unset( $pack['schema']['props'][ $prop_key ] ); continue; } // Drop non-serializable values. foreach ( $meta as $k => $v ) { if ( is_object( $v ) && ! ( $v instanceof JsonSerializable ) ) { unset( $meta[ $k ] ); } } $pack['schema']['props'][ $prop_key ] = $meta; } // Validate inspector groups/controls. if ( ! empty( $pack['inspector_ui']['groups'] ) ) { foreach ( $pack['inspector_ui']['groups'] as $g_i => $group ) { if ( ! is_array( $group ) ) { unset( $pack['inspector_ui']['groups'][ $g_i ] ); continue; } $group['controls'] = ( isset( $group['controls'] ) && is_array( $group['controls'] ) ) ? $group['controls'] : array(); foreach ( $group['controls'] as $c_i => $control ) { if ( ! is_array( $control ) ) { unset( $group['controls'][ $c_i ] ); continue; } if ( empty( $control['key'] ) || empty( $control['type'] ) || ! in_array( $control['type'], $allowed_control_types, true ) ) { unset( $group['controls'][ $c_i ] ); continue; } // Remove objects in control meta. foreach ( $control as $k => $v ) { if ( is_object( $v ) && ! ( $v instanceof JsonSerializable ) ) { unset( $control[ $k ] ); } } $group['controls'][ $c_i ] = $control; } $pack['inspector_ui']['groups'][ $g_i ] = $group; } } // Sanitize header action keys. if ( ! empty( $pack['header_actions'] ) ) { $san = array(); foreach ( $pack['header_actions'] as $act ) { $san[] = sanitize_key( $act ); } $pack['header_actions'] = array_values( array_unique( $san ) ); } return $pack; } /** * Merge a list of packs keyed by type. Later packs win on conflicts. * * @param array $packs Packs list. * * @return array Merged packs keyed by type. */ public static function merge_packs( $packs ) { $merged = array(); foreach ( (array) $packs as $pack ) { $pack = self::normalize_pack( $pack ); $pack = self::validate_pack( $pack ); if ( empty( $pack['type'] ) ) { continue; } $merged[ $pack['type'] ] = $pack; // last writer wins. } return $merged; } /** * Extract only the JS-safe schema bits for localization. * * @param array $packs_by_type Merged packs keyed by type. * * @return array Array keyed by type for JS (small payload). */ public static function extract_schemas_for_js( $packs_by_type ) { $out = array(); foreach ( (array) $packs_by_type as $type => $pack ) { $out[ $type ] = array( 'kind' => isset( $pack['kind'] ) ? $pack['kind'] : 'field', 'schema' => isset( $pack['schema'] ) ? $pack['schema'] : array( 'props' => array() ), 'inspector_ui' => isset( $pack['inspector_ui'] ) ? $pack['inspector_ui'] : array( 'groups' => array() ), 'header_actions' => isset( $pack['header_actions'] ) ? array_values( $pack['header_actions'] ) : array(), ); } return $out; } }