$value ) { $data[ $key ] = self::sanitize_array_recursive( $value ); } return $data; } if ( is_string( $data ) ) { return sanitize_text_field( $data ); } if ( is_numeric( $data ) ) { return $data; // Keep numeric values as-is. } if ( is_bool( $data ) ) { return $data; // Keep boolean values as-is. } if ( is_null( $data ) ) { return null; // Keep null as-is. } // For any other type, convert to string and sanitize. return sanitize_text_field( (string) $data ); } /** * Get user agent string. * * @since 1.0.0 * * @return string User agent string or empty string if not found. */ public static function get_user_agent(): string { if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && is_string( $_SERVER['HTTP_USER_AGENT'] ) ) { return sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ); } return ''; } /** * Sanitize mixed submission value into a single string. * * @since 1.0.0 * * @param mixed $value Raw value. * * @return string Sanitized string (empty when nothing useful). */ public static function sanitize_field_value( $value ): string { // phpcs:ignore Generic.Metrics.NestingLevel.MaxExceeded, Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.CyclomaticComplexity.MaxExceeded if ( $value === null || is_bool( $value ) ) { return ''; } if ( is_array( $value ) ) { $parts = []; array_walk_recursive( $value, static function ( $item ) use ( &$parts ): void { if ( is_scalar( $item ) || ( is_object( $item ) && method_exists( $item, '__toString' ) ) ) { $string = sanitize_text_field( (string) $item ); if ( $string !== '' ) { $parts[] = $string; } } } ); return implode( ', ', $parts ); } if ( is_object( $value ) && method_exists( $value, '__toString' ) ) { $value = (string) $value; } if ( ! is_scalar( $value ) ) { return ''; } return sanitize_text_field( (string) $value ); } /** * Sanitize submission data for database storage. * * @since 1.0.0 * * @param array $data Raw data. * * @return array Sanitized data. */ public static function sanitize_submission_data( array $data ): array { return self::sanitize_array_recursive( $data ); } /** * Convert a stored submission date to a Unix timestamp. * * The `created_at` and `processed_at` columns are written with * `current_time( 'mysql' )`, so they hold site-local time with no timezone * marker. PHP runs in UTC under WordPress, so a plain `strtotime()` would * read them as UTC and shift the result by the site's offset. * * @since 1.6.2 * * @param mixed $value MySQL datetime string or Unix timestamp. * * @return int Unix timestamp, or 0 when the value cannot be parsed. */ public static function to_timestamp( $value ): int { if ( empty( $value ) ) { return 0; } if ( is_numeric( $value ) ) { return (int) $value; } // Parses in the site timezone (DST aware) and returns UTC seconds. return (int) get_gmt_from_date( (string) $value, 'U' ); } /** * Format submission data for output. * * @since 1.0.0 * @since 1.6.2 Parse stored dates in the site timezone. * * @param array $raw_submission Raw submission from database. * * @return array Formatted submission. */ public static function format_submission( array $raw_submission ): array { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.CyclomaticComplexity.MaxExceeded $submission = $raw_submission; // Decode JSON fields. if ( ! empty( $submission['form_data'] ) && is_string( $submission['form_data'] ) ) { $decoded = json_decode( $submission['form_data'], true ); $submission['form_data'] = $decoded ? $decoded : []; } if ( ! empty( $submission['api_response'] ) && is_string( $submission['api_response'] ) ) { $decoded = json_decode( $submission['api_response'], true ); $submission['api_response'] = $decoded ? $decoded : []; } // Convert timestamps to Unix timestamps. if ( ! empty( $submission['created_at'] ) ) { $submission['created_at'] = self::to_timestamp( $submission['created_at'] ); } if ( ! empty( $submission['processed_at'] ) ) { $submission['processed_at'] = self::to_timestamp( $submission['processed_at'] ); } if ( isset( $submission['retry_count'] ) ) { $submission['retry_count'] = (int) $submission['retry_count']; } if ( isset( $submission['previous_status'] ) ) { $submission['previous_status'] = is_string( $submission['previous_status'] ) ? $submission['previous_status'] : ''; } return $submission; } }