$submitted_data Submitted data. * @param string $form_id Generic form identifier. * @param string $rate_limit_identifier Identifier used only for hashing. * @param int|null $timestamp Current timestamp. */ public function build_from_submitted_data( array $submitted_data, string $form_id, string $rate_limit_identifier = '', ?int $timestamp = null ): SubmissionContext { $timestamp = null === $timestamp ? time() : max( 1, $timestamp ); $started_at = $this->get_int_field( $submitted_data, ProtectionFieldRenderer::STARTED_AT_FIELD ); return SubmissionContext::from_array( array( 'form_id' => $form_id, 'honeypot_value' => $this->get_string_field( $submitted_data, ProtectionFieldRenderer::HONEYPOT_FIELD ), 'token' => $this->get_string_field( $submitted_data, ProtectionFieldRenderer::TOKEN_FIELD ), 'completion_time_seconds' => null === $started_at ? null : max( 0, $timestamp - $started_at ), 'rate_limit_identifier' => $rate_limit_identifier, 'field_values' => $this->get_public_field_values( $submitted_data ), 'timestamp' => $timestamp, ) ); } /** * Get scalar public field values, excluding protection internals. * * @param array $submitted_data Submitted data. * * @return array */ protected function get_public_field_values( array $submitted_data ): array { $values = array(); foreach ( $submitted_data as $key => $value ) { if ( $this->is_internal_field( (string) $key ) ) { continue; } if ( is_scalar( $value ) ) { $values[] = $this->sanitize_string( (string) $value ); } } return $values; } /** * Get a sanitized string field. * * @param array $submitted_data Submitted data. * @param string $field_name Field name. */ protected function get_string_field( array $submitted_data, string $field_name ): string { if ( ! isset( $submitted_data[ $field_name ] ) || ! is_scalar( $submitted_data[ $field_name ] ) ) { return ''; } return $this->sanitize_string( (string) $submitted_data[ $field_name ] ); } /** * Get a sanitized integer field. * * @param array $submitted_data Submitted data. * @param string $field_name Field name. */ protected function get_int_field( array $submitted_data, string $field_name ): ?int { if ( ! isset( $submitted_data[ $field_name ] ) || ! is_scalar( $submitted_data[ $field_name ] ) ) { return null; } return max( 0, (int) $submitted_data[ $field_name ] ); } /** * Whether a field is a 2o2code Spamless Forms internal field. * * @param string $field_name Field name. */ protected function is_internal_field( string $field_name ): bool { return in_array( $field_name, array( ProtectionFieldRenderer::HONEYPOT_FIELD, ProtectionFieldRenderer::TOKEN_FIELD, ProtectionFieldRenderer::STARTED_AT_FIELD, ), true ); } /** * Sanitize submitted scalar text with a WordPress-compatible fallback. * * @param string $value Raw value. */ protected function sanitize_string( string $value ): string { if ( function_exists( 'wp_unslash' ) ) { $value = (string) wp_unslash( $value ); } if ( function_exists( 'sanitize_text_field' ) ) { return sanitize_text_field( $value ); } if ( function_exists( 'wp_strip_all_tags' ) ) { return trim( str_replace( "\0", '', wp_strip_all_tags( $value ) ) ); } return trim( preg_replace( '/<[^>]*>/', '', str_replace( "\0", '', $value ) ) ?? '' ); } }