0

I'm attempting to send a JSON document to a PHP script using AJAX. The JSON document is constructed from the value of a <textarea>.

I have successfully executed the solution using JQuery, and (for fun?!) am working on achieving the same result with vanilla AJAX.

The calling PHP script:

<script>
  function preview() {
    var xhttp;
    xhttp = new XMLHttpRequest();
    xhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        document.getElementById("output").innerHTML = this.responseText;
      }
    };
    var postData = {
        'html' : document.getElementById("editor").value,
    };
    xhttp.open("POST", "markuppreview.php");
    xhttp.setRequestHeader('Content-type', 'application/json');
    xhttp.send(postData);
  };
</script>

<pre><textarea id="editor" name="content" placeholder="Enter your markup"></textarea></pre><br />
<button value="Preview" onclick="preview();">Preview</button>
<h2>Preview</h2>
<div id="output" style="height:100px"></div>

The receiving PHP:

$Parsedown = new Parsedown();
$Parsedown->setSafeMode(true);

$data['success'] = false;
$data['output'] = '';
if ($_POST['html']) {
    $data['success'] = true;
    $data['output'] = $Parsedown->text($_POST['html']);
}
echo json_encode($data);

I receive the following error, and can't work out why the postData.html isn't being received.

Warning: Undefined array key "html" in /Library/WebServer/Documents/markuppreview.php on line 8
{"success":false,"output":""}

I also tried a Javascript object method for constructing the JSON document, but received the same message. When I alert the JSON document, it does show an html element with the data from the <textarea>.

    var postData = new Object();
    postData.html = document.getElementById("editor").value;
    postData = JSON.stringify(postData);
    alert(postData);
MuppetDance
  • 144
  • 10
  • You might consider either having your ` – David784 Jan 29 '22 at 19:04
  • Is the alternating use of `print` and `printf` for no obvious reason a part of the Muppet Dance routine? – Markus AO Jan 29 '22 at 19:52
  • OP updated as per the above comments – MuppetDance Jan 29 '22 at 20:23

3 Answers3

3

There are multiple problems coming together here:

  1. You seem to attempt to send JSON (according to your Content-Type: application/json header), yet PHP natively only supports form data (query string or multipart). If you want to use JSON, then you have to access the raw body with file_get_contents('php://input') and use json_decode on it to get an object (instead of using the associative array superglobal $_POST).

  2. You don't actually send anything useful, because you are passing an object to XMLHttpRequest#send which will get coerced to a string and end up as just [object Object]. If you do want to send JSON, you'll have to apply JSON.stringify on the data first and send the result.

Going from here, you can either change both client and server to fully use JSON or update the client to send form data.

However, I would recommend to switch to a more modern (and easy-to-use) solution than XMLHttpRequest anyway, ideally fetch. With the existing server code (using form data), the following client code would work:

async function preview () {
  const response = await fetch('markuppreview.php', {
    method: 'POST',
    body: new URLSearchParams({ html: document.getElementById("editor").value })
  })
  document.getElementById('output').innerHTML = await response.text()
}

If you intend to display only the output property of the JSON object that you return from your server and only if success is true, then you'd need to use this instead of the await response.text() line above:

  const { success, output } = await response.json()
  if (success) document.getElementById('output').innerHTML = output
CherryDT
  • 25,571
  • 5
  • 49
  • 74
  • Thanks @CherryDT. Only just saw your edited response. I was having quite the challenge figuring out how to parse the resulting JSON document. I had gone with `.then(response => response.json()) .then(response => { document.getElementById('output').innerHTML = response.output })`. Your update is more elegant and also contains some error handling. Thanks again! – MuppetDance Jan 29 '22 at 22:03
1

Because you're attempting to send JSON-formatted data and $_POST works with form data, not a JSON payload.

You'll need to do

$json = json_decode(file_get_contents('php://input'));

to read the POST input as a raw string, then JSON decode it.

Also, please use fetch() instead of the (pretty legacy) XMLHTTPRequest API.

AKX
  • 152,115
  • 15
  • 115
  • 172
1

Final solution as per @CherryDT's suggestions.

Calling PHP script:

<script>
async function preview () {
    const response = await fetch('markuppreview.php', {
          method: 'POST',
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded',
          body: new URLSearchParams({ html: document.getElementById("editor").value })
    })
    const { success, output } = await response.json()
    if (success) document.getElementById('output').innerHTML = output
}
</script>
<pre><textarea id="editor" name="content" placeholder="Enter your markup"></textarea></pre><br />
<button value="Preview" onclick="preview();">Preview</button>
<h2>Preview</h2>
<div id="output" style="height:100px"></div>

Receiving PHP:

$Parsedown = new Parsedown();
$Parsedown->setSafeMode(true);

$data['success'] = false;
$data['output'] = '';
if ($_POST['html']) {
    $data['success'] = true;
    $data['output'] = $Parsedown->text($_POST['html']);
}
echo json_encode($data);
MuppetDance
  • 144
  • 10