: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param array $fields List of form fields.
private function remove_raw_data_before_save( array $fields ): array {
foreach ( $fields as $key => $field ) {
if ( ! empty( $field['type'] ) && $field['type'] === 'password' ) {
unset( $fields[ $key ]['value_raw'] );
* Save payment to the database.
* @param array $entry User submitted data.
* @return int Payment ID.
private function payment_save( $entry ) {
if ( ! wpforms_has_payment( 'entry', $this->fields ) ) {
$entry['entry_id'] = $this->entry_id;
$form_submission = wpforms()->get( 'submission' )->register( $this->fields, $entry, $this->form_data['id'], $this->form_data );
// Prepare the payment data.
$payment_data = $form_submission->prepare_payment_data();
// Bail early in case payment field exists,
// but no payment data was provided (e.g. old payment addon is used).
if ( empty( $payment_data['gateway'] ) ) {
$payment_id = wpforms()->get( 'payment' )->add( $payment_data );
wpforms()->get( 'payment_meta' )->bulk_add( $payment_id, $form_submission->prepare_payment_meta() );
* Fire after payment was saved to database.
* @param int $payment_id Payment id.
* @param array $fields Form fields.
* @param array $form_data Form data.
do_action( 'wpforms_process_payment_saved', $payment_id, $this->fields, $this->form_data );
* Process AJAX form submit.
public function ajax_submit() {
// phpcs:disable WordPress.Security.NonceVerification.Missing
$form_id = isset( $_POST['wpforms']['id'] ) ? absint( $_POST['wpforms']['id'] ) : 0;
if ( empty( $form_id ) ) {
if ( isset( $_POST['wpforms']['post_id'] ) ) {
// We don't have a global $post when processing ajax requests.
// Therefore, it's needed to set a global $post manually for compatibility with functions used in smart tag processing.
// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
$post = WP_Post::get_instance( absint( $_POST['wpforms']['post_id'] ) );
// phpcs:enable WordPress.Security.NonceVerification.Missing
add_filter( 'wp_redirect', [ $this, 'ajax_process_redirect' ], 999 );
do_action( 'wpforms_ajax_submit_before_processing', $form_id );
// If redirect happens in listen(), ajax_process_redirect() gets executed because of the filter on `wp_redirect`.
// The code, that is below listen(), runs only if no redirect happened.
$form_data = $this->form_data;
if ( empty( $form_data ) ) {
$form_data = wpforms()->get( 'form' )->get( $form_id, [ 'content_only' => true ] );
$form_data = apply_filters( 'wpforms_frontend_form_data', $form_data );
if ( ! empty( $this->errors[ $form_id ] ) ) {
$this->ajax_process_errors( $form_id, $form_data );
wpforms()->get( 'frontend' )->confirmation( $form_data );
$response = apply_filters( 'wpforms_ajax_submit_success_response', [ 'confirmation' => ob_get_clean() ], $form_id, $form_data );
do_action( 'wpforms_ajax_submit_completed', $form_id, $response );
wp_send_json_success( $response );
* @todo This should be re-used/combined for AMP verify-xhr requests.
* @param int $form_id Form ID.
* @param array $form_data Form data and settings.
protected function ajax_process_errors( $form_id, $form_data ) {
$errors = isset( $this->errors[ $form_id ] ) ? $this->errors[ $form_id ] : [];
$errors = apply_filters( 'wpforms_ajax_submit_errors', $errors, $form_id, $form_data );
if ( empty( $errors ) ) {
// General errors are errors that cannot be populated with jQuery Validate plugin.
$general_errors = array_intersect_key( $errors, array_flip( [ 'header', 'footer', 'recaptcha' ] ) );
foreach ( $general_errors as $key => $error ) {
wpforms()->get( 'frontend' )->form_error( $key, $error, $form_data );
$general_errors[ $key ] = ob_get_clean();
$fields = isset( $form_data['fields'] ) ? $form_data['fields'] : [];
// Get registered fields errors only.
$field_errors = array_intersect_key( $errors, $fields );
// Transform field ids to field names for jQuery Validate plugin.
foreach ( $field_errors as $key => $error ) {
$name = $this->ajax_error_field_name( $fields[ $key ], $form_data, $error );
$field_errors[ $name ] = $error;
unset( $field_errors[ $key ] );
$response['errors']['general'] = $general_errors;
$response['errors']['field'] = $field_errors;
$response = apply_filters( 'wpforms_ajax_submit_errors_response', $response, $form_id, $form_data );
do_action( 'wpforms_ajax_submit_completed', $form_id, $response );
wp_send_json_error( $response );
* Get field name for an ajax error message.
* @param array $field Field settings.
* @param array $form_data Form data and settings.
* @param string|string[] $error Error message.
private function ajax_error_field_name( array $field, array $form_data, $error ): string {
$props = wpforms()->get( 'frontend' )->get_field_properties( $field, $form_data );
* Filter the field name for an ajax error message.
* @param string $name Error field name.
* @param array $field Field.
* @param array $props Field properties.
* @param string|string[] $error Error message.
return (string) apply_filters( 'wpforms_process_ajax_error_field_name', '', $field, $props, $error );
* @param string $url Redirect URL.
public function ajax_process_redirect( $url ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$form_id = isset( $_POST['wpforms']['id'] ) ? absint( $_POST['wpforms']['id'] ) : 0;
if ( empty( $form_id ) ) {
$response = apply_filters( 'wpforms_ajax_submit_redirect', $response, $form_id, $url );
do_action( 'wpforms_ajax_submit_completed', $form_id, $response );
wp_send_json_success( $response );