-1

So I've got a form where I can create multiple datalist-text-inputs of the same type that is later (onclick) put into an invisible input before submitted.

These datalist-text-inputs is created onload so that I can add more with an "+" button with the same function.

The options are imported from my PHP-database, and it worked fine. But suddenly it stopped working and I don't know why. I've tried a thousand things but can't figure it out.

I'm quite new to PHP. I think the problem has to do with JSON.parse() since the code breaks on that line.

script.js

var ajax = new XMLHttpRequest();
ajax.open("GET", "fetch data.php", true);
ajax.send();
ajax.onreadystatechange = function() {
    if(this.readyState == 4 && this.status == 200) {
        var data = JSON.parse(this.responseText);
        var html = "";
        for (var a = 0; a < data.length; a++) {
            var firstName = data[a].name;
            html += "<option value='" + firstName + "'></option>";  
        };
        document.getElementById(type+"list"+addnumber).innerHTML = html;
    };
};

type+"list"+addnumber it the name of the input-text-box. Type is an argument and addnumber an variable/integer.

fetch data.php

<?php
    $host = "localhost"; $user = "root"; $pass = ""; $db = "collection"; 
    $conn = mysqli_connect($host, $user, $pass, $db);
    $result = mysqli_query($conn, 'SELECT name FROM musicians ORDER BY name');
    $data = array();

    while ($row = mysqli_fetch_assoc($result)) {
        $data[]=$row;
    };
    
    echo json_encode($data);
?>

Also, I might add that this function creates objects in three places on the same page but the value is added/moved to three different invisible inputs.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
kattp
  • 1
  • 1
  • Can you add more details about `the code breaks`? Do you get an error? – user3783243 Jun 11 '22 at 21:14
  • what is the value of this.responseText at the time of the "break"? (eg: if you run `console.log(this.responseText)` before running `JSON.parse(this.responseText)`) – Will Palmer Jun 11 '22 at 21:20
  • The most likely explanation here is that your PHP code is not returning a JSON string. Maybe it's returning an HTML page, or an error message. Use the Network tab in your browser's Developer Tools to watch the AJAX request and examine the response. This might tell you what your problem is. – Tangentially Perpendicular Jun 11 '22 at 21:33
  • @WillPalmer It returns nothing. It's empty. – kattp Jun 11 '22 at 21:41
  • @user3783243 Uncaught SyntaxError: Unexpected end of JSON input at JSON.parse () at ajax.onreadystatechange (script.js:34:29) – kattp Jun 11 '22 at 21:41
  • @kattp do you get an exception if you call json_encode as: `echo json_encode($data, JSON_THROW_ON_ERROR);`? – Will Palmer Jun 11 '22 at 21:53
  • Why not use `this.response` directly? If `this.responseText`is a *json string*, `this.response` is a *json object* already. – Sanxofon Jun 11 '22 at 22:05
  • Always remember that JSON.parse [_will throw on bad input_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) so never have just `const blah JSON.parse(bleep)`, _always_ have `let blah; try { blah = JSON.parse(bleep); } catch (e) { ... }`. Sure, if everything works there will never be an error we need to catch but that error is _always_ looming, have the code in place to deal with it. – Mike 'Pomax' Kamermans Jun 11 '22 at 22:09
  • Check my answer please I'm sure, it will fix the error – Sevada 797 Jun 11 '22 at 22:35
  • @WillPalmer then it logs "Fatal error: Uncaught JsonException: Malformed UTF-8 characters, possibly incorrectly encoded in C:\wamp64\www\collection\fetch data.php on line 11" followed by all the names that I'm trying to pull from the database in this structure "['name' => 'Ben Swanson']" – kattp Jun 12 '22 at 06:54

2 Answers2

0

Based on the exception you're seeing, ie:

JsonException: Malformed UTF-8 characters, possibly incorrectly encoded in C:\wamp64\www\collection\fetch data.php on line <i>11</i>

My guess is that the data you are reading is not encoded in a way which json_encode expects.

The simplest (NOT RECOMMENDED) approach is to pass in the JSON_INVALID_UTF8_IGNORE or JSON_INVALID_UTF8_SUBSTITUTE flags, which will cause bad data to be silently skipped over (or replaced with the unicode REPLACEMENT CHARACTER \0xfffd), rather than treating it as an error. See the documentation for json_encode and predefined JSON constants.

If you want to get to the root of the problem so that all data is correctly encoded as JSON, however:

You can try to force the encoding by setting the mysql character set using PHP's mysqli_set_charset function, eg:

<?php
    $host = "localhost"; $user = "root"; $pass = ""; $db = "collection"; 
    $conn = mysqli_connect($host, $user, $pass, $db);
    if (!mysqli_set_charset($conn, 'utf8')) {
        throw new \Exception("failed to set mysql charset");
    }
    $result = mysqli_query($conn, 'SELECT name FROM musicians ORDER BY name');
    ...
?>

The most common charsets in my experience are utf8, utf8mb4. If you know your data to contain some other specific character set, you may need to translate it into utf8 before trying to encode it into JSON using PHP's mb_convert_encoding function.

Finally, it could be that the issue has occurred earlier in your application, resulting in bad (mixed-encoding) data. If this is the case, you'll need to detect the bad data row-by-row, perhaps outputting where exceptions were raised in a separate error report, and manually correct the encoding of that data. This can be prevented by ensuring the data is validated and correctly encoded as utf8 before it reaches your database. Note that ensuring the mysql character set is correctly set for all connections is also potentially a part of this solution. It may be that you'll want to configure your database to do this automatically.

example of detection and logging:

<?php
    $host = "localhost"; $user = "root"; $pass = ""; $db = "collection"; 
    $conn = mysqli_connect($host, $user, $pass, $db);
    if (!mysqli_set_charset($conn, 'utf8')) {
        throw new \Exception("failed to set mysql charset");
    }
    $result = mysqli_query($conn, 'SELECT name FROM musicians ORDER BY name');
    $data = array();

    while ($row = mysqli_fetch_assoc($result)) {
        try {
            // detect rows which cannot be encoded
            $discard = json_encode($row, JSON_THROW_ON_ERROR);
        } catch (\JsonException $e) {
            // keep track of failed rows so we can correct them later
            my_custom_logging_function($row);

            // and skip the row so we don't try to encode it later
            continue;
        }
        $data[]=$row;
    };

    echo json_encode($data, JSON_THROW_ON_ERROR);
?>
Will Palmer
  • 5,742
  • 1
  • 26
  • 33
  • The detection script you sent worked! But if I log it I can't tell what row causes the problem? It logs 140 posts and there are 140 rows. – kattp Jun 13 '22 at 08:59
  • The code works without the try/catch structure but not without the utf8 if-statement. – kattp Jun 13 '22 at 09:00
-2

OK just change "fetch data.php" to "data.php"

Sevada 797
  • 346
  • 1
  • 8
  • This worked for me, if you want to see result in html maybe you wanted to type `html += ""` instead of `html += ""`? – Sevada 797 Jun 11 '22 at 22:56
  • Please tell me what's exactly not working, as I came up to this answer after some debugging. – Sevada 797 Jun 12 '22 at 00:13