phpwordpressformspostwordpress-shortcode

Wordpress Plugin PHP Multiple Forms in a Single Page Empty Post Request


I'm building a WordPress plugin that has a page that consists of multiple forms. Each form is a step. Here's my form-handler:

<?php

class Form_Handler
{
    public function init_funnel()
    {
        ob_start();
        $this->handle_steps();
        return ob_get_clean();
    }

    private function handle_steps()
    {
        error_log('CURRENT STEP: ' . ($_SESSION['current_step'] ?? 'not set'));
        error_log('POST: ' . print_r($_POST, true));
        error_log('SESSION: ' . print_r($_SESSION, true));

        if (!isset($_SESSION['current_step']))
            $_SESSION['current_step'] = 1;

        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['funnel_nonce'])) {
            $this->process_form();
        }
        error_log('AFTER PROCESS SESSION: ' . print_r($_SESSION, true));

        switch ($_SESSION['current_step']) {
            case 1:
                include FUNNEL_PLUGIN_PATH . 'templates/step1.php';
                break;
            case 2:
                include FUNNEL_PLUGIN_PATH . 'templates/step2.php';
                break;
            case 3:
                include FUNNEL_PLUGIN_PATH . 'templates/step3.php';
                break;
            default:
                include FUNNEL_PLUGIN_PATH . 'templates/final.php';
                break;
        }

    }

    private function process_form()
    {
        if (!isset($_POST['funnel_nonce']) || !wp_verify_nonce($_POST['funnel_nonce'], 'funnel_nonce')) {
            wp_die('Security check failed.');
        }

        switch ($_SESSION['current_step']) {
            case 1:
                $_SESSION['step1'] = [
                    'satisfaction' => sanitize_text_field($_POST['satisfaction'])
                ];
                $_SESSION['current_step'] = 2;
                break;
            case 2:
                if (!empty($_POST['email']) && !empty($_POST['name'])) {
                    $_SESSION['email'] = sanitize_email($_POST['email']);
                    $_SESSION['name'] = sanitize_text_field($_POST['name']);
                    $_SESSION['current_step'] = 3;
                } else {
                    $_SESSION['form_error'] = 'Please fill in all required fields.';
                }
                break;
            case 3:
                if (isset($_POST['review']) && strlen(trim($_POST['review'])) >= 40) {
                    $_SESSION['review'] = sanitize_textarea_field($_POST['review']);
                    $_SESSION['current_step'] = 4;
                } else {
                    $_SESSION['form_error'] = 'Please write at least 40 characters review.';
                }
                break;
        }
    }
}

if (isset($_SESSION['form_error'])): ?>
    <div class="error-message">
        <?php
        echo esc_html($_SESSION['form_error']);
        unset($_SESSION['form_error']);
        ?>
    </div>
<?php endif; ?>

When I run this, I get step1.php rendered properly. Logs printed properly. I fill in the step1 form, I submit it. step2.php is rendered properly, I get step2.php rendered. When I submit the step2.php, I see the post request on the dev tools network tab but nothing gets printed on the logs, no code works. After submitting the step2, I get Page not Found error on the browser. When I click to the back button in browser and refresh the page, I get logs printed sometimes but even then the post request is empty. So it doesn't work because it's empty.

step1.php:

<div class="funnel-container">
    <h2>We value your feedback!</h2>
    <p>Please answer the following questions:</p>

    <form method="POST">
        <?php wp_nonce_field('funnel_nonce', 'funnel_nonce'); ?>


        <!-- Question 1 -->
        <div class="form-group">
            <label for="marketplace">Which marketplace did you purchase from? <span class="required">*</span></label>
            <select name="marketplace" id="marketplace" required>
                <option value="">Select a marketplace</option>
                <option selected="selected" value="United States">United States</option>
                <option value="United Kingdom">United Kingdom</option>
                <option value="Canada">Canada</option>
                <option value="Germany">Germany</option>
            </select>
        </div>

        <!-- Question 2 -->
        <div class="form-group">
            <label for="order_number">Amazon Order Number <span class="required">*</span> <small><a target="_new"
                        href="https://www.amazon.com/gp/css/order-history">(What is this?)</a></small></label>
            <input type="text" name="order_number" id="order_number" placeholder="000-0000000-0000000" required
                pattern="\d{3}-\d{7}-\d{7}">
        </div>

        <!-- Question 3 -->
        <div class="form-group">
            <label>How happy are you with our product? <span class="required">*</span></label><br>
            <label><input type="radio" name="satisfaction" value="Very Satisfied" required> Very Satisfied</label><br>
            <label><input type="radio" name="satisfaction" value="Somewhat Satisfied" required> Somewhat
                Satisfied</label><br>
            <label><input type="radio" name="satisfaction" value="Neither Satisfied Nor Dissatisfied" required> Neither
                Satisfied Nor Dissatisfied</label><br>
            <label><input type="radio" name="satisfaction" value="Somewhat Dissatisfied" required> Somewhat
                Dissatisfied</label><br>
            <label><input type="radio" name="satisfaction" value="Very Dissatisfied" required> Very Dissatisfied</label>
        </div>
        <button type="submit" class="funnel-button">Continue ></button>
    </form>
</div>

step2.php:

<div class="funnel-container">
    <img src="/path-to-your-ebook-image.jpg" alt="Free Ebook" class="ebook-image">
    <h2>Free Amazon $5 Gift Card</h2>

    <form method="POST">
        <?php wp_nonce_field('funnel_nonce', 'funnel_nonce'); ?>

        <!-- Name Field -->
        <div class="form-group">
            <label for="name">Your Name <span class="required">*</span></label>
            <input type="text" name="name" id="name" required>
        </div>

        <!-- Email Field -->
        <div class="form-group">
            <label for="email">E-mail Address <span class="required">*</span></label>
            <input type="email" name="email" id="email" required>
        </div>

        <!-- Info Note -->
        <p class="small-note">Your gift will be sent to your e-mail address</p>
        <button type="submit" class="funnel-button">Continue ></button>
    </form>
</div>

I'd really appreciate any help. I register the Form Handler as a short code in my main plugin file.

add_shortcode('custom_funnel', [new Form_Handler(), 'init_funnel']);

I tried adding filters to resolve 404 like:

add_action('template_redirect', function() {
    if (is_404() && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['funnel_nonce'])) {
        global $wp, $wp_query;

        $page = get_page_by_path(trim($wp->request, '/'));

        if ($page) {
            $wp_query->is_404 = false;
            $wp_query->is_page = true;
            $wp_query->queried_object = $page;
            $wp_query->queried_object_id = $page->ID;
            $wp_query->post = $page;
            $wp_query->post_type = 'page';
            $wp_query->posts = [$page];
            status_header(200);
        }
    }
});

But didn't solve anything.


Solution

  • I found out that the problem is some input names are reserved by WordPress and you can not use them. When I changed name attribute of the inputs, It's solved.

            <div class="form-group">
                <label for="name_user">Your Name <span class="required">*</span></label>
                <input type="text" name="name_user" id="name" required>
            </div>
    
            <!-- Email Field -->
            <div class="form-group">
                <label for="email_user">E-mail Address <span class="required">*</span></label>
                <input type="email" name="email_user" id="email" required>
            </div>