nonce_action = $nonce_action; $this->nonce_field = $nonce_field; $this->query = $query; } /** * Fields of a POST form or AJAX request protected by $nonce_action. * The nonce is verified immediately and again at every read. */ public static function post( string $nonce_action, string $nonce_field = '_wpnonce' ): self { $input = new self( $nonce_action, $nonce_field, false ); $input->verify(); return $input; } /** * Query-string fields of a nonce-protected admin action link (wp_nonce_url()). * The nonce is verified immediately and again at every read. */ public static function query( string $nonce_action, string $nonce_field = '_wpnonce' ): self { $input = new self( $nonce_action, $nonce_field, true ); $input->verify(); return $input; } /** * Stops the request unless it carries a valid nonce for this handler. */ public function verify(): void { if ( wp_doing_ajax() ) { check_ajax_referer( $this->nonce_action, $this->nonce_field ); } else { check_admin_referer( $this->nonce_action, $this->nonce_field ); } } public function has( string $key ): bool { return (bool) $this->value( $key, 'exists' ); } /** Whether the field was sent with a non-empty value (as empty() sees it), for flags and traps. */ public function filled( string $key ): bool { return (bool) $this->value( $key, 'filled' ); } /** Single-line text (sanitize_text_field()); $default when the field was not sent. */ public function text( string $key, string $default = '' ): string { $value = $this->value( $key, 'text' ); return null === $value ? $default : (string) $value; } /** Multi-line text (sanitize_textarea_field()). */ public function textarea( string $key, string $default = '' ): string { $value = $this->value( $key, 'textarea' ); return null === $value ? $default : (string) $value; } /** Lower-case key (sanitize_key()). */ public function key( string $key, string $default = '' ): string { $value = $this->value( $key, 'key' ); return null === $value ? $default : (string) $value; } /** Email address (sanitize_email()). */ public function email( string $key, string $default = '' ): string { $value = $this->value( $key, 'email' ); return null === $value ? $default : (string) $value; } /** Non-negative integer (absint()). */ public function int( string $key, int $default = 0 ): int { $value = $this->value( $key, 'int' ); return null === $value ? $default : (int) $value; } /** Post-safe HTML (wp_kses_post()). */ public function html( string $key, string $default = '' ): string { $value = $this->value( $key, 'html' ); return null === $value ? $default : (string) $value; } /** URL (esc_url_raw()), so percent-encoded characters are kept. */ public function url( string $key, string $default = '' ): string { $value = $this->value( $key, 'url' ); return null === $value ? $default : (string) $value; } /** Whether the field was sent as an array. */ public function has_list( string $key ): bool { return null !== $this->value( $key, 'text_list' ); } /** * Array field, every string through sanitize_text_field(). Empty when not an array. * * @return array */ public function text_list( string $key ): array { $value = $this->value( $key, 'text_list' ); return is_array( $value ) ? $value : array(); } /** * Array field, every string through sanitize_textarea_field(). Empty when not an array. * * @return array */ public function textarea_list( string $key ): array { $value = $this->value( $key, 'textarea_list' ); return is_array( $value ) ? $value : array(); } /** * One string entry of an array field, for example booking_fields[phone], sanitized for * $type ('text' or 'textarea'). Null when absent or not a string. */ public function item( string $key, string $item, string $type = 'text' ): ?string { $value = $this->item_value( $key, $item, $type ); return is_string( $value ) ? $value : null; } /** Whether one entry of an array field was sent with a non-empty value (checkboxes). */ public function item_filled( string $key, string $item ): bool { return (bool) $this->item_value( $key, $item, 'filled' ); } /** * Listed fields only, for handlers that pass a field set on to extensions. * A field is a key (text) or key => 'textarea'|'email'|'key'|'int'. Fields that * were not sent are left out; arrays are sanitized value by value. * * @param array $fields * @return array */ public function fields( array $fields ): array { $values = array(); foreach ( $fields as $key => $type ) { if ( is_int( $key ) ) { $key = $type; $type = 'text'; } if ( ! $this->has( $key ) ) { continue; } if ( 'textarea' === $type ) { $values[ $key ] = $this->value( $key, 'textarea_list' ) ?? $this->value( $key, 'textarea' ); } elseif ( 'text' === $type ) { $values[ $key ] = $this->value( $key, 'text_list' ) ?? $this->value( $key, 'text' ); } else { $values[ $key ] = $this->value( $key, $type ); } } return $values; } /** * @return mixed */ private function item_value( string $key, string $item, string $type ) { if ( wp_doing_ajax() ) { check_ajax_referer( $this->nonce_action, $this->nonce_field ); } else { check_admin_referer( $this->nonce_action, $this->nonce_field ); } if ( 'filled' === $type ) { return ! empty( $_POST[ $key ][ $item ] ); } if ( ! isset( $_POST[ $key ][ $item ] ) || ! is_scalar( $_POST[ $key ][ $item ] ) ) { return null; } if ( 'textarea' === $type ) { return sanitize_textarea_field( wp_unslash( $_POST[ $key ][ $item ] ) ); } return sanitize_text_field( wp_unslash( $_POST[ $key ][ $item ] ) ); } /** * @return mixed Sanitized value, null when the field was not sent. */ private function value( string $key, string $type ) { if ( wp_doing_ajax() ) { check_ajax_referer( $this->nonce_action, $this->nonce_field ); } else { check_admin_referer( $this->nonce_action, $this->nonce_field ); } if ( $this->query ) { if ( 'exists' === $type ) { return isset( $_GET[ $key ] ); } if ( ! isset( $_GET[ $key ] ) ) { return null; } switch ( $type ) { case 'int': return is_scalar( $_GET[ $key ] ) ? absint( $_GET[ $key ] ) : 0; case 'key': return is_scalar( $_GET[ $key ] ) ? sanitize_key( wp_unslash( $_GET[ $key ] ) ) : ''; case 'text_list': case 'textarea_list': return is_array( $_GET[ $key ] ) ? map_deep( wp_unslash( $_GET[ $key ] ), 'sanitize_text_field' ) : null; default: return is_scalar( $_GET[ $key ] ) ? sanitize_text_field( wp_unslash( $_GET[ $key ] ) ) : ''; } } if ( 'exists' === $type ) { return isset( $_POST[ $key ] ); } if ( 'filled' === $type ) { return ! empty( $_POST[ $key ] ); } if ( ! isset( $_POST[ $key ] ) ) { return null; } switch ( $type ) { case 'int': return is_scalar( $_POST[ $key ] ) ? absint( $_POST[ $key ] ) : 0; case 'key': return is_scalar( $_POST[ $key ] ) ? sanitize_key( wp_unslash( $_POST[ $key ] ) ) : ''; case 'email': return is_scalar( $_POST[ $key ] ) ? sanitize_email( wp_unslash( $_POST[ $key ] ) ) : ''; case 'textarea': return is_scalar( $_POST[ $key ] ) ? sanitize_textarea_field( wp_unslash( $_POST[ $key ] ) ) : ''; case 'html': return is_scalar( $_POST[ $key ] ) ? wp_kses_post( wp_unslash( $_POST[ $key ] ) ) : ''; case 'url': return is_scalar( $_POST[ $key ] ) ? esc_url_raw( wp_unslash( $_POST[ $key ] ) ) : ''; case 'text_list': return is_array( $_POST[ $key ] ) ? map_deep( wp_unslash( $_POST[ $key ] ), 'sanitize_text_field' ) : null; case 'textarea_list': return is_array( $_POST[ $key ] ) ? map_deep( wp_unslash( $_POST[ $key ] ), 'sanitize_textarea_field' ) : null; default: return is_scalar( $_POST[ $key ] ) ? sanitize_text_field( wp_unslash( $_POST[ $key ] ) ) : ''; } } }