You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
8.1 KiB

<?php
/**
* Sanitize field value before saving.
*/
class RWMB_Sanitizer {
public function init() {
add_filter( 'rwmb_sanitize', [ $this, 'sanitize' ], 10, 4 );
}
/**
* Sanitize a field value.
*
* @param mixed $value The submitted new value.
* @param array $field The field settings.
* @param mixed $old_value The old field value in the database.
* @param int $object_id The object ID.
*/
public function sanitize( $value, $field, $old_value = null, $object_id = null ) {
// Allow developers to bypass the sanitization.
if ( 'none' === $field['sanitize_callback'] ) {
return $value;
}
$callback = $this->get_callback( $field );
return is_callable( $callback ) ? call_user_func( $callback, $value, $field, $old_value, $object_id ) : $value;
}
/**
* Get sanitize callback for a field.
*
* @param array $field Field settings.
* @return callable
*/
private function get_callback( $field ) {
// User-defined callback.
if ( is_callable( $field['sanitize_callback'] ) ) {
return $field['sanitize_callback'];
}
$callbacks = [
'autocomplete' => [ $this, 'sanitize_choice' ],
'background' => [ $this, 'sanitize_background' ],
'button_group' => [ $this, 'sanitize_choice' ],
'checkbox' => [ $this, 'sanitize_checkbox' ],
'checkbox_list' => [ $this, 'sanitize_choice' ],
'color' => [ $this, 'sanitize_color' ],
'date' => [ $this, 'sanitize_datetime' ],
'datetime' => [ $this, 'sanitize_datetime' ],
'email' => 'sanitize_email',
'fieldset_text' => [ $this, 'sanitize_text' ],
'file' => [ $this, 'sanitize_file' ],
'file_advanced' => [ $this, 'sanitize_object' ],
'file_input' => [ $this, 'sanitize_url' ],
'file_upload' => [ $this, 'sanitize_object' ],
'hidden' => 'sanitize_text_field',
'image' => [ $this, 'sanitize_file' ],
'image_advanced' => [ $this, 'sanitize_object' ],
'image_select' => [ $this, 'sanitize_choice' ],
'image_upload' => [ $this, 'sanitize_object' ],
'key_value' => [ $this, 'sanitize_text' ],
'map' => [ $this, 'sanitize_map' ],
'number' => [ $this, 'sanitize_number' ],
'oembed' => [ $this, 'sanitize_url' ],
'osm' => [ $this, 'sanitize_map' ],
'password' => 'sanitize_text_field',
'post' => [ $this, 'sanitize_object' ],
'radio' => [ $this, 'sanitize_choice' ],
'range' => [ $this, 'sanitize_number' ],
'select' => [ $this, 'sanitize_choice' ],
'select_advanced' => [ $this, 'sanitize_choice' ],
'sidebar' => [ $this, 'sanitize_text' ],
'single_image' => 'absint',
'slider' => [ $this, 'sanitize_slider' ],
'switch' => [ $this, 'sanitize_checkbox' ],
'taxonomy' => [ $this, 'sanitize_object' ],
'taxonomy_advanced' => [ $this, 'sanitize_taxonomy_advanced' ],
'text' => 'sanitize_text_field',
'text_list' => [ $this, 'sanitize_text' ],
'textarea' => 'wp_kses_post',
'time' => 'sanitize_text_field',
'url' => [ $this, 'sanitize_url' ],
'user' => [ $this, 'sanitize_object' ],
'video' => [ $this, 'sanitize_object' ],
'wysiwyg' => 'wp_kses_post',
];
$type = $field['type'];
return $callbacks[ $type ] ?? null;
}
/**
* Set the value of checkbox to 1 or 0 instead of 'checked' and empty string.
* This prevents using default value once the checkbox has been unchecked.
*
* @link https://github.com/rilwis/meta-box/issues/6
* @param string $value Checkbox value.
*/
private function sanitize_checkbox( $value ): int {
return (int) ! empty( $value );
}
/**
* Sanitize numeric value.
*
* @param string $value The number value.
* @return string
*/
private function sanitize_number( $value ) {
return is_numeric( $value ) ? $value : '';
}
private function sanitize_color( string $value ): string {
if ( str_contains( $value, 'hsl' ) ) {
return wp_unslash( $value );
}
if ( ! str_contains( $value, 'rgb' ) ) {
return sanitize_hex_color( $value );
}
// rgba value.
$red = '';
$green = '';
$blue = '';
$alpha = 1;
if ( str_contains( $value, 'rgba' ) ) {
sscanf( $value, 'rgba(%d,%d,%d,%f)', $red, $green, $blue, $alpha );
} else {
sscanf( $value, 'rgb(%d,%d,%d)', $red, $green, $blue );
}
return 'rgba(' . $red . ',' . $green . ',' . $blue . ',' . $alpha . ')';
}
/**
* Sanitize value for a choice field.
*
* @param string|array $value The submitted value.
* @param array $field The field settings.
* @return string|array
*/
private function sanitize_choice( $value, $field ) {
$options = RWMB_Choice_Field::transform_options( $field['options'] );
$options = wp_list_pluck( $options, 'value' );
$value = wp_unslash( $value );
return is_array( $value ) ? array_intersect( $value, $options ) : ( in_array( $value, $options ) ? $value : '' );
}
/**
* Sanitize object & media field.
*
* @param int|array $value The submitted value.
* @return int|array
*/
private function sanitize_object( $value ) {
return is_array( $value ) ? array_filter( array_map( 'absint', $value ) ) : ( $value ? absint( $value ) : '' );
}
/**
* Sanitize background field.
*
* @param array $value The submitted value.
* @return array
*/
private function sanitize_background( $value ) {
$value = wp_parse_args( $value, [
'color' => '',
'image' => '',
'repeat' => '',
'attachment' => '',
'position' => '',
'size' => '',
] );
$value['color'] = $this->sanitize_color( $value['color'] );
$value['image'] = esc_url_raw( $value['image'] );
$value['repeat'] = in_array( $value['repeat'], [ 'no-repeat', 'repeat', 'repeat-x', 'repeat-y', 'inherit' ], true ) ? $value['repeat'] : '';
$value['position'] = in_array( $value['position'], [ 'top left', 'top center', 'top right', 'center left', 'center center', 'center right', 'bottom left', 'bottom center', 'bottom right' ], true ) ? $value['position'] : '';
$value['attachment'] = in_array( $value['attachment'], [ 'fixed', 'scroll', 'inherit' ], true ) ? $value['attachment'] : '';
$value['size'] = in_array( $value['size'], [ 'inherit', 'cover', 'contain' ], true ) ? $value['size'] : '';
return $value;
}
/**
* Sanitize text field.
*
* @param string|array $value The submitted value.
* @return string|array
*/
private function sanitize_text( $value ) {
return is_array( $value ) ? array_map( __METHOD__, $value ) : sanitize_text_field( $value );
}
/**
* Sanitize file, image field.
*
* @param array $value The submitted value.
* @param array $field The field settings.
* @return array
*/
private function sanitize_file( $value, $field ) {
return $field['upload_dir'] ? array_map( 'esc_url_raw', $value ) : $this->sanitize_object( $value );
}
/**
* Sanitize slider field.
*
* @param mixed $value The submitted value.
* @param array $field The field settings.
* @return string|int|float
*/
private function sanitize_slider( $value, $field ) {
return true === $field['js_options']['range'] ? sanitize_text_field( $value ) : $this->sanitize_number( $value );
}
/**
* Sanitize datetime field.
*
* @param mixed $value The submitted value.
* @param array $field The field settings.
* @return float|string
*/
private function sanitize_datetime( $value, $field ) {
return $field['timestamp'] ? (float) $value : sanitize_text_field( $value );
}
private function sanitize_map( $value ): string {
$value = sanitize_text_field( $value );
list( $latitude, $longitude, $zoom ) = explode( ',', $value . ',,' );
$latitude = (float) $latitude;
$longitude = (float) $longitude;
$zoom = (int) $zoom;
return "$latitude,$longitude,$zoom";
}
private function sanitize_taxonomy_advanced( $value ): string {
return implode( ',', wp_parse_id_list( $value ) );
}
private function sanitize_url( string $value ): string {
return esc_url_raw( $value );
}
}