blog de josé

Table Of Contents

0x

Saving POST Data as Variables in WS Forms

Environment

  • Nginx
  • PHP 8.2.30
    • Requires PHP 7.0+
  • MySQL 8.4.0
  • WP 7.0
  • Local by Flywheel

Github Repo

Too long? Want the code?

https://gist.github.com/JosChavz/17c0b64ada6724048fc60041fdf2e088

Editor’s Note

This issue came from my old job. It was a very specific task where an API was making a POST request to a specific page. The contents from the POST request were IDs and a temporary API key. That data was then meant to be sent back to the caller through an API request. So an API endpoint was created and called after submission. In that endpoint, logic occurred, data was sent back, and, lastly, the transient was deleted. There were some caveats in that, but also for this.

A scenario where you need this can be like when I needed it:
An external website wants to integrate your form through an iframe or just sends the customer to your form page through a POST, for whatever reason. With this code, you can either show those variables that were sent to the public or keep it to yourself, then do some of your own logic. Whether it be storing them as a meta value, creating an endpoint and sending the POST value there, or more!

This is a short and simple solution to this problem, but there is many ways to achieve this. If you would like to extend, shorten, or harden its security, please do so! Comment what you’ve made because I’m actually interested to see the other ways.

Disclaimer

Please do not use this code to handle very sensitive information.

Prerequisites

  1. WS Form Lite: Make sure to have this installed and activated
    • WS Form Pro will also work, but note that this is a paid plugin, whereas Lite is free
  2. Created a form and embedded to a page
    • This form can be simple for the time being
  3. Basic knowledge of PHP and WordPress
  4. IDE: I will be using PHPStorm
  5. Local Environment (Local, Studio, other)
    • Note: Although it is possible to development on a staging or production site, this tutorial will not cover that.

Creating a Temporary Form for POST

One way to test this is by using Postman to send POST requests to a page with a body. The body would have to be under form-data in order for it to work.

Another way is by actually making a form on your website, then redirecting it to the rightful page. Doing this way requires creating a shortcode.

function make_post_request_shortcode() : string {
    $redirect_to = "/contact"; // CHANGE ME
    ob_start();
    ?>
    <form action="$redirect_to" method="post">
        <label>
            <input type="text" name="example" />
            // Add more inputs with different names
            Name
        </label>
        <input type="submit" value="Submit">
    </form>
    <?php
        return ob_get_clean();
}
add_shortcode('make_post_form', 'make_post_request_shortcode');
PHP

Create a new page, and add this shortcode. $redirect_to will be the page where the form that will handle the POST request reside. Add more input elements with different names. These names will appear under the $_POST global array. For this example, I only have one called example.

The Flow

The flow is simple.

After our POST request, we will capture the values we want and store it as a transient for 10 minutes. The key will be a random key that will be passed to our query when we redirect to the same page but as a GET.

Once we are in the GET request, we will save the data under WS Forms’ variables. For that, we will be using the wsf_config_parse_variables hook.

Step 1: Capture the POST data

Step 1.1: Understanding the picture

Let’s take a step back for a bit to see the clear picture before going into code. Knowing that we are going to do a redirection to the same page as a GET request, we have to do the following:

  1. Save the value from $_POST
    • Requires sanitation
  2. Create a unique key
  3. Store as a transient
  4. Redirect
    • Use the URL Queries to send the unique key
  5. Get value from transient using the unique key from the GET request
    • Sanitize
  6. Store it a custom variable inside of WS Form’s variables

The hook that we will be using for this will be template_redirect, because as the docs stated:

It is a good hook to use if you need to do a redirect with full knowledge of the content that has been queried.

Step 1.2: Explaining the code (partial)

// Warning: Not the full code, just yet!
function pd_init() : void {
    // Only POST requests here!
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') return;

    if (isset($_POST['example'])) {
      // Get and sanitize the data
      $example = sanitize_text_field(wp_unslash($_POST['example']));
      // Create a unique key
      $token = bin2hex(random_bytes(32));

      // Store it inside a transient with the key
      set_transient("wsf_$token", $example, 10 * MINUTE_IN_SECONDS);
      wp_safe_redirect(get_permalink() . "?_nonce=$token");
      exit();
    }
	}
	add_action( "template_redirect", "pd_init" );
PHP

So let’s break it down a bit. The first thing we do is to check if it’s a POST request, because we don’t care about GETs in here!… One could say… GET out of here. Okay…

Secondly, we want to check if the name of the variable that we are expecting to receive exists in the POST object. If so, sanitize it and assign it to a variable. So, remove any slashes that were done in the POST variable, and then sanitize it.

After that, we create a token using the bin2hex(random_bytes(32)) function. Careful here! This can throw back an error if it doesn’t have enough entropy or the system is old. It’s rare, but it could happen. One way to catch it is to do the following:

  try {
      $token = bin2hex(random_bytes(32));
  } catch (Exception $e) {
      error_log('pd_init: failed to generate token — ' . $e->getMessage());
      wp_die('Something went wrong. Please try again.', 500);
  }
PHP

Essentially throw a 500 and have the user to try again.

Alright, lastly we set a transient for 10 minutes and store the value in there. The key we will be using is wsf_ and the token appended. The reason why we want to leave it for a few minutes is because we don’t want to leave it for too long and give the opportunity for someone to catch it.

Although there is an expiration, the user is able to submit the form without any issues if it’s in a field. If it’s an action, then increase the time limit.

At the end, we redirect the user to the same page but with _nonce as the token.

Step 1.3: Hook Restrictions

When using hooks, it will always run. We don’t want to run this logic over and over, because what if somewhere in your website you have a POST request with the same name as the variable that you are expecting to save? If that ever happens, then guess what? It stores as a transient. We don’t want garbage to pile up, even if it deletes after 10 minutes.

We need to explicitly tell the hook to proceed in certain pages.

// ... code above
  $valid_ids = [6];
  $current_id = get_queried_object_id();
  if (! in_array($current_id, $valid_ids)) return;
  
  if (isset($_POST['example'])) {
// ... code below
PHP

Assign the valid page/post ID in an array, in the case you will be putting this form in different places. The way to know the ID from the post that we’re on, we could call the get_queried_object_id function. Remember we are on the template_redirect hook? Well, we have access to the ID and permalink in here.

If we are not on the post that we are supposed to be in, then we’d just exit from the hook. Simple!

Don’t know your post ID? Read this article by Kinsta.

Code

Full Code
function pd_init() : void {
        // Only POST requests here!
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') return;

        // Only run this in certain pages
        $valid_ids = [6];
        $current_id = get_queried_object_id();
        if (! in_array($current_id, $valid_ids)) return;

        if (isset($_POST['example'])) {
            // Get and sanitize the data
            $example = sanitize_text_field(wp_unslash($_POST['example']));
            // Create a unique key
            $token = bin2hex(random_bytes(32));

            // Store it inside a transient with the key
            set_transient("wsf_$token", $example, 10 * MINUTE_IN_SECONDS);
            wp_safe_redirect(get_permalink() . "?_nonce=$token");
            exit();
        }
	}
	add_action( "template_redirect", "pd_init" );
PHP

Step 2: Storing the Data into WS Form

Here comes the part in saving it as a variable. For this, we will be using the wsf_config_parse_variables hook. If you take a look at the example from the docs, we will essentially be copying that.

function pd_parse_variables($variables) : array {
  $example_val = "";
  
  $variables[ 'custom' ] = array(
    // Variable group label
    'label' => __( 'Custom Example', 'exampledomain' ),
    'variables' => array(
      'example_value' => array(
        'label' => __( 'Example value', 'exampledomain' ),
        'description' => __( 'Value from the POST data', 'exampledomain' ),
        'value' => $example_val,
        'usage' => array( 'client', 'action' )
      )
    )
  );
  
  return $variables;
}
add_filter('wsf_config_parse_variables', 'pd_parse_variables');
PHP

The first thing that we are doing is creating a new group, giving it a label, then assigning variables that will be grouped in there.

Step 2.1: Getting the data from its transient

In order to get the value, we have to capture the token that was assigned to the _nonce in the query parameters. Sanitize it, then we could get the transient. If it’s not found, then we could add some error handling. The following code is added before setting assigning values to the $variables.

if (isset($_GET['_nonce'])) {
    $token = sanitize_text_field(wp_unslash($_GET['_nonce']));
    $example_val = get_transient("wsf_$token") ?: "";

    if ($example_val === "") {
      error_log("Token not found or expired.");
    }
}
JavaScript

Step 2.2: Restricting Where it Runs

This part is optional, but if you’d like to only have this code run when it’s in the backend or when _nonce is in the query params, then you could add the following code at the beginning of the function:

$frontend_check = 'GET' === $_SERVER['REQUEST_METHOD'] && isset($_GET['_nonce']);
$backend_check = is_admin();
if (! $backend_check && ! $frontend_check) return $variables;
JavaScript
Full Code
function pd_parse_variables($variables) : array {
  $frontend_check = 'GET' === $_SERVER['REQUEST_METHOD'] && isset($_GET['_nonce']);
  $backend_check = is_admin();
  if (! $backend_check && ! $frontend_check) return $variables;
  
  $example_val = "";

  if (isset($_GET['_nonce'])) {
    $token = sanitize_text_field(wp_unslash($_GET['_nonce']));
    $example_val = get_transient("wsf_$token") ?: "";

    if ($example_val === "") {
      error_log("Token not found or expired.");
    }
  }

  $variables[ 'custom' ] = array(
    // Variable group label
    'label' => __( 'Custom Example', 'exampledomain' ),
    'variables' => array(
      'example_value' => array(
        'label' => __( 'Example value', 'exampledomain' ),
        'description' => __( 'Value from the POST data', 'exampledomain' ),
        'value' => $example_val,
        'usage' => array( 'client', 'action' )
      )
    )
  );
  
  return $variables;
}
add_filter('wsf_config_parse_variables', 'pd_parse_variables');
PHP

That’s all! If there are any simpler way in getting this to work, please share it! This code is short and simple, but you could do many things with it. Store all of the POST variables as a JSON, increase the time limit of the transient and delete it after the form is submitted with wsf_submit_create, and more.

Holá, I'm Jose, Mexican-American web developer living in North County San Diego, California. I would like to write more about cafes, programming, and life (eh). Thank you for visiting my blog site!

headshot of jose manuel chavez, the author of this blog
You can find me at:
blog de José
© 2026 blog de José