'textarea' (sanitize_textarea_field()) or 'email' (sanitize_email()). Arrays are * sanitized value by value. Field-level validation still happens where each value is used. * * @param array $fields Fields the handler, and the extensions it passes the values to, read. * @return array */ public static function post_fields( string $nonce_action, array $fields, string $nonce_field = '_wpnonce' ): array { return Input::post( $nonce_action, $nonce_field )->fields( $fields ); } /** * Verifies the nonce, then returns a sanitized copy of one upload field of $_FILES, single or * multiple, with the same structure: file names through sanitize_file_name(), client MIME types * through sanitize_mime_type(), temporary paths through sanitize_text_field() (server paths, not * unslashed; callers still check them with is_uploaded_file()), error codes and sizes as * integers. Other keys are dropped. Empty when the field was not sent. * * @return array */ public static function files( string $nonce_action, string $key, string $nonce_field = '_wpnonce' ): array { if ( wp_doing_ajax() ) { check_ajax_referer( $nonce_action, $nonce_field ); } else { check_admin_referer( $nonce_action, $nonce_field ); } if ( ! isset( $_FILES[ $key ] ) || ! is_array( $_FILES[ $key ] ) ) { return array(); } $file = array(); foreach ( array( 'name', 'type', 'tmp_name', 'full_path', 'error', 'size' ) as $part ) { if ( ! isset( $_FILES[ $key ][ $part ] ) ) { continue; } if ( 'name' === $part ) { $file[ $part ] = map_deep( $_FILES[ $key ][ $part ], 'sanitize_file_name' ); } elseif ( 'type' === $part ) { $file[ $part ] = map_deep( $_FILES[ $key ][ $part ], 'sanitize_mime_type' ); } elseif ( 'error' === $part || 'size' === $part ) { $file[ $part ] = map_deep( $_FILES[ $key ][ $part ], 'absint' ); } else { $file[ $part ] = map_deep( $_FILES[ $key ][ $part ], 'sanitize_text_field' ); } } return $file; } /** * Navigation parameter of an admin screen: page, tab, filters, search, pagination, or * the id of the record being viewed. These links are bookmarkable and carry no nonce, so * a value read here must never change data: handlers that change data use Input. Every * screen that reads one checks the user's capability. Unknown types fall back to text. * * @param string $type 'text' (sanitize_text_field()), 'key' (sanitize_key()), 'int' (absint()), * or 'list' (array, sanitize_text_field() value by value). * @return mixed Sanitized value, null when the parameter is absent or has the wrong shape. */ public static function view( string $key, string $type = 'text' ) { // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only navigation parameters (see above); data changes always go through Input. if ( ! isset( $_GET[ $key ] ) ) { return null; } if ( 'list' === $type ) { return is_array( $_GET[ $key ] ) ? map_deep( wp_unslash( $_GET[ $key ] ), 'sanitize_text_field' ) : null; } if ( ! is_string( $_GET[ $key ] ) ) { return null; } if ( 'int' === $type ) { return absint( $_GET[ $key ] ); } if ( 'key' === $type ) { return sanitize_key( wp_unslash( $_GET[ $key ] ) ); } return sanitize_text_field( wp_unslash( $_GET[ $key ] ) ); // phpcs:enable WordPress.Security.NonceVerification.Recommended } /** Whether a navigation parameter is present in the query string (see view()). */ public static function has_view( string $key ): bool { return isset( $_GET[ $key ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Presence of a read-only navigation parameter. } /** * Navigation parameters present in the query string, as text (arrays value by value). * * @param string[] $keys * @return array */ public static function view_fields( array $keys ): array { $values = array(); foreach ( $keys as $key ) { $value = self::view( $key, 'list' ) ?? self::view( $key ); if ( null !== $value ) { $values[ $key ] = $value; } } return $values; } /** * Sanitized admin page slug from the query string (empty when missing). */ public static function page(): string { return (string) self::view( 'page', 'key' ); } }