• I created my own contact form plugin years ago and have used it on several WP sites. To prevent multiple submissions, a random, unique stamp is generated and inserted into a database table field that only allows unique values. This form works fine on sites running WP 4.8 or 4.9 and earlier, but on sites running 4.9 or 5.0 plus, it generates a duplicate entry error on the very first submission. The email is still sent and duplicate submissions are still prevented, but it seems like the insert routine is running twice in the newer versions of WP and I can’t figure out why.

    The stamps table is created with the following code upon plugin activation:

    $charset_collate = $wpdb->get_charset_collate();
    $table_name = $wpdb->prefix."cf_stamps";
    $sql = "CREATE TABLE IF NOT EXISTS $table_name (
    	entry_id int(10) NOT NULL AUTO_INCREMENT,
    	entry_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
    	entry_stamp varchar(32) NOT NULL,
    	PRIMARY KEY  (entry_id),
    	UNIQUE KEY entry_stamp (entry_stamp)
    ) $charset_collate;";

    The contact form is inserted into a WP page via a shortcode:

    function contact_form_shortcode($atts) {
    	ob_start();
    	$a = shortcode_atts( array(
            'language' => 'en'
        ), $atts );
    	$lang = $a['language'];
    	contact_form_process($lang);// Processes the contact form after submission
    	contact_form_email($lang);// Checks for errors and emails form data if error free
    	contact_form_markup($lang);// Generates contact form markup
    	return ob_get_clean();
    }
    add_shortcode('contact_form', 'contact_form_shortcode');

    After submission and processing, form data checked for errors and emailed. Unique stamp is inserted into the database table upon first submission.:

    function contact_form_email($lang='en') {
    	global $showForm;// Form is only displayed if there are errors that need correcting or an email error has occurred
    	global $cf_form_errors, $cf_user_errors, $cf_form_missing;// Form submission and user errors
    	global $cf_email_main, $cf_emails_bc, $cf_emails_cc;// Email addresses to which to send contact form data
    	global $wpdb;
    	$cf_stamps_table = $wpdb->prefix.'cf_stamps';// Database table for stamps (to prevent multiple submissions)
    	$userErrorMsgs = cf_get_user_error_msgs();
    	if (isset($_POST['submitForm'])) {
    		if ($cf_form_errors) {// True if honeypot has been filled, cf_stamp empty or invalid, etc.
    			echo "\n\t\t\t", '<p class="align-center error">This form has been accessed in error.</p>', "\n";
    			$_POST = array();
    			$showForm = FALSE;
    		} elseif ($cf_form_missing || $cf_user_errors) {// True if any required fields are missing or any fields contain invalid data 
    			echo "\n\t\t\t", '<p class="align-center error">Please address the errors indicated below and resubmit your data.</p>', "\n";
    			$showForm = TRUE;
    		} else {// No errors, proceed with sending form data via email
    			$cf_stamp = $_POST['cf_stamp'];// Submitted via hidden field, value=md5(uniqid(rand(), true))
    			$wpdb->hide_errors();
    			if ($wpdb->insert($cf_stamps_table, array('entry_stamp' => $cf_stamp))) {
    				if (cf_contact_form_send_email($cf_email_main, $cf_emails_bc, $cf_emails_cc, $lang)) {// Composes email and sends to main, BC, CC using wp_mail()
    					echo "\n\t\t\t", '<p class="contact-thank">Thank you for contacting us. Your information has been submitted successfully. We will reply as soon as possible.</p>', "\n";
    					$showForm = FALSE;
    				} else {// Email did not send successfuly
    					echo "\n\t\t\t", '<p class="align-center error">A system error has occurred. We aplogize for the inconvenience. Please try again later.</p>', "\n";
    					$showForm = TRUE;
    				}
    			} else {// Form submitted multiple times (stamp already in database table)
    				echo "\n\t\t\t", '<p class="contact-thank">The form has already been submitted and processed. Thank you.</p>', "\n";
    				$showForm = FALSE;
    			}
    		}
    	} else {
    		return;
    	}
    }

    In older WP versions, I see the “Thank you for contacting us. Your information has been submitted successfully….” message upon first submission. In newer WP versions, I immediately see the “The form has already been submitted and processed….” message and my error log shows a duplicate entry error, but the email is still sent. So it seems like insert is running twice in newer WP versions. Can anyone take a guess as to why this is happening?

    This isn’t the end of the world since the form is still fulfilling its main function, but it’s driving me nuts and I’d really appreciate any advice on where I can look for a solution.

Viewing 7 replies - 1 through 7 (of 7 total)
  • Moderator bcworkz

    (@bcworkz)

    wpdb::insert() wouldn’t attempt to do a double insertion in one call, but it’s conceivable your contact_form_email() function is called more than once. You were wise to ensure emails were not sent twice! Without knowing how your form submission is handled it’s hard to speculate why the email function is called more than once. If it is hooked into an action or filter, that’s likely the reason. Actions and filters can fire multiple times in one request despite it being illogical. Hook callbacks that must only be called once should remove themselves from the call stack upon execution to ensure they don’t get called again during the same request. Doing so should prevent the duplicate submittal message from appearing unless someone really submits twice.

    Thread Starter jjbte

    (@jjbte)

    Hi @bcworkz,

    Thank you so much for your detailed, helpful reply!

    The contact_form_email() function is called once from the shortcode function. I don’t have any action hooks in the plugin other than those for loading language and style files.

    I built this plugin several years ago and based it on this SitePoint tutorial.

    It runs as expected on a site running WP 4.8 on a host server running PHP 7.1.33. It does this double submission thing on two sites running WP 5.1 or 5.4 on a host server running PHP 7.3.16. All sites use many of the same plugins, but a different theme is used on each site.

    Upon submission, the contact_form_process() function checks for the unique stamp, honeypot fill-in, and nonce verification. If no issues, all inputs are sanitized and validated as appropriate. If all is well, the contact_form_email() function runs.

    The only other thing I can think of is something connected to ob_start() and ob_get_clean() as I admittedly don’t have an in-depth understanding of exactly what those functions do.

    Moderator bcworkz

    (@bcworkz)

    The ob_*() (output buffer) functions capture output from echo statements so the related function can be used within a shortcode callback. Nothing should be directly output from a shortcode callback, all intended output must be returned for output elsewhere. The functions would not be the cause of double calls.

    Shortcodes are expanded in content by hooking “the_content” with do_shortcode() as the callback. This is done in core WP. It’s conceivable something is applying the filter for some other reason besides producing output. Your send email function is indirectly involved in this so it ends up being called more than once in the process. While I cannot specifically explain why this would happen, it’s not entirely unexpected.

    Your function was involved in a filter callback by virtue of it being part of a shortcode callback. You had good foresight to ensure it would not send email more than once should that happen. All that’s needed now is to modify the user feedback to prevent confusion. The “already sent” message should not be shown due to the function being called twice but only for actual duplicate submissions. Maybe by defining a constant when the email is sent and suppressing the message if the constant is defined?

    Thread Starter jjbte

    (@jjbte)

    Thank you, @bcworkz, for another informative reply. I’ll look into the constant method when I have a chance. I found this post from someone having an issue with multiple shortcode invocations and a constant was suggested in that case also.

    I already changed the “repeat submission” message on the other site where this is happening so it’s not obvious to the user that anything is wrong. I’m currently working on a test site, so I haven’t changed the message yet. On the first site, I thought the theme I was using might be the cause; it was a theme I’d never used before. But now this test site is having the same issue with a different theme.

    I understand the shortcode could be causing the send email function to be getting called more than once, but why is it not happening in the older versions of WP? Has there been a major change in shortcode behavior between WP 4.8 and now? I guess I’ll have to do some file comparisons and study the WP changelogs to see if I can find any clues.

    Thanks again for your help.

    Moderator bcworkz

    (@bcworkz)

    It’s not the shortcode API itself AFAIK, rather when “the_content” filter is applied, which in turn calls shortcode callbacks. I’m unable to confirm shortcode handler functions are called more than once (in v5.3.2 with twentysixteen child theme) when the shortcode is in page content. I inserted an error_log() call into one of my shortcode handlers to check. This is for a POST request to a page with a custom template. This might be a clue where not to look if you’re intent on finding the root cause.

    The fact remains filters can be called more than once and this needs to be addressed where it matters.

    Thread Starter jjbte

    (@jjbte)

    Welp, I guess I’m going to have to just live with it, as much as it pains me to do so.

    I tried defining a constant right after the email is sent successfully. Then I added a check for whether that constant is defined along with the $wpdb->insert check. No success.

    I also tried turning on debug mode, but all I got in my debug log were the duplicate DB entry messages.

    I tried adding debug_print_backtrace() at various points in the contact_form_email() function. I did the same for the plugin on the site (running WP 4.8) that is working as expected/intended. I compared the output from the two. The 4.8 site shows the contact_form_email() function completing the step for sending the email. It only goes to the step for a repeat submission if I refresh the page and say Yes to resubmitting the form data.

    The test site (running WP 5.4) shows no evidence of the send email step at all. It goes straight to the repeat submission step. So it gives me no information as to whether the contact_form_email() function is bring run twice or, if so, why.

    Other than these two differences, the two sites appear to be running the same steps, functions, hooks, etc. in the process of submitting the contact form.

    Thank you again fro all your help, @bcworkz. I really appreciate all the trouble you’ve gone to in order to help me with this problem. ??

    Moderator bcworkz

    (@bcworkz)

    You’re welcome. I have another thought that might be worth investigating. Is there any chance the POST request from the form is being sent twice for some reason? Could happen if script sends the request without .preventDefault(); for example. Use your browser’s network developer tool. Clear the log if needed, then submit the form and note what requests are made.

Viewing 7 replies - 1 through 7 (of 7 total)
  • The topic ‘Custom Contact Form: $wpdb->insert running twice?’ is closed to new replies.