1

Context

I just upgraded my website server to a VPS and, since moving my files to the new server, have been having some problems with the transition. One of them is that, on this server, one of my functions isn't running correctly. The function getRandomArticles() gets user-posted articles from my mySQL database. On my local server and on my previous shared server, it worked fine. However, when I run the following function on the VPS, I get the errors parserror and

SyntaxError: Unexpected end of JSON input at parse (<anonymous>) at VM3130  
jquery.min.js:2 at l (VM3130 jquery.min.js:2) at XMLHttpRequest.<anonymous>  
(VM3130 jquery.min.js:2)  

(from console.log(error) and console.log(errorThrown) in the error callback function)

Here's the function getRandomArticles() that returns the errors:

    var ids = [];

    function getRandomArticles() {

        $.ajax({
            url: 'includes/articles/getRandomArticles.php',
            type: 'GET',
            dataType: 'json',
            success: function(data) {

                //code irrelevant to problem

            },
            error: function(requestObject, error, errorThrown) {
                console.log(error);
                console.log(errorThrown);
            }
        });
    }

Here's my PHP code:

<?php

include '../dbh.inc.php';

if ($_SERVER['REQUEST_METHOD'] == 'GET') {

$q = "SELECT articles.article_id, articles.title, articles.article, articles.idUsers, users.uidUsers, articlecoverimages.image, profileimages.image AS profileImage 
      FROM articles 
      JOIN users 
      JOIN articlecoverimages 
      JOIN profileimages ON users.idUsers = articlecoverimages.idUsers AND articles.article_id = articlecoverimages.article_id AND profileimages.idUsers = users.idUsers 
      WHERE articles.published = ? AND articles.approved = 1 
      ORDER BY RAND()
      LIMIT 5";
$stmt = mysqli_prepare($conn, $q);
mysqli_stmt_bind_param($stmt, 's', $published);

$published = "yes";

mysqli_stmt_execute($stmt);

$r = mysqli_stmt_get_result($stmt);

if ($r) {

    if (mysqli_num_rows($r) == 0) {
    
        $data['error'] = "";
        $results[] = $data;

    } else {

        while ($row = mysqli_fetch_array($r)) {
            
            $data['articleId'] = $row['article_id'];
            $data['title'] = $row['title'];
            $data['article'] = strip_tags(htmlspecialchars_decode($row['article'], ENT_QUOTES));
            $data['idUsers'] = $row['idUsers'];
            $data['uidUsers'] = $row['uidUsers'];
            $data['image'] = $row['image'];
            $data['profileImage'] = $row['profileImage'];
            $results[] = $data;

        }       

    }

    echo json_encode($results);

}

mysqli_stmt_close($stmt);

mysqli_close($conn);

}

There is no response in the PHP code when these errors occur. Without the error, it responds with the JSON array. However, when I echo a specific row, such as echo $row['article'];, that specific piece of data sends, even with the error. So, nothing but the names of the articles would be returned in that scenario.

When I refresh the page many times, the code finally works and the article data loads. However, this only works about 30-40% of the time, seemingly randomly.

Things I Have Tried

I use echo json_encode($results) to return the values of the PHP file that the AJAX call points to, so I'm using JSON here. When I just say echo $results[], there is an error, and when I remove dataType: json from the AJAX parameters, there is also an error (in case those had any legitimate potential for solving the problem).

Question

Any idea what could be going wrong with this function?

Update

I find that when I remove the line

$data['article'] = strip_tags(htmlspecialchars_decode($row['article'], ENT_QUOTES));

All the other rows return in the PHP file. And when I remove every other line but the line above, the error continues. Every time. So, removing

$data['article'] = strip_tags(htmlspecialchars_decode($row['article'], ENT_QUOTES));

fixes the issue EXCEPT for the fact that I need to be able to return this line, which gives the article content itself. I have this same problem with three other files that get article content, all of which have the same line (and all of which are fixed by removing that line). It's weird, though, because other files that specify an article topic are able to get the article content just fine. I've tried resetting everything on my VPS; the database, the apache server, everything I could, to no avail. Based on this finding, it seems that the PHP file is the problem here, but no potential solution seems to be fixing the problem. Again, the file works just fine in my local server and worked just fine on my previous shared server, but on this new server, it's having problems. I even deleted the files and made new ones to see if the file itself caused any sort of error, which also didn't work.

Additionally, as previously stated, although

$data['article'] = strip_tags(htmlspecialchars_decode($row['article'], ENT_QUOTES))

is causing problems, when I remove echo json_encode($results) and all the other column values stored in the $results array and simply echo $row['article'] (like so):

while ($row = mysqli_fetch_array($r)) {

    echo strip_tags(htmlspecialchars_decode($row['article'], ENT_QUOTES));

}

the article content returns. Any idea why this problem may be occurring?

Here's the structure of the 'article' column in the articles database:

article TEXT NOT NULL
Carson D
  • 81
  • 13
  • What's in the response when you get an error? – Nick Jan 07 '21 at 02:40
  • @Nick there is no response when the error occurs. However, when I echo a specific row from the database, such as `echo $row['article']`, the article is returned despite the error. Let me know if that doesn't make sense. – Carson D Jan 07 '21 at 03:36
  • What happens if you initialise `$results = array('test');` before the `if` test do you still get no response? – Nick Jan 07 '21 at 03:43
  • @Nick Yes, if I do that, I still don't get any response. – Carson D Jan 07 '21 at 04:54
  • Sorry, meant to say you need a `echo json_encode($results);` after the `if` statement as well – Nick Jan 07 '21 at 05:06
  • In that case, I do get a result. `["test"] 0: "test"` – Carson D Jan 07 '21 at 08:01
  • @Nick I just updated my question with something I found - when I remove `$row['article']`, the PHP file returns the other rows every time. Any idea why this line may be problematic? – Carson D Jan 07 '21 at 23:05
  • Well that's weird. Have you checked the PHP error log? I can't see why those functions would cause PHP to crash though... – Nick Jan 07 '21 at 23:07
  • @Nick Yeah. Checked the logs and nothing showed up. Restarted the database and apache servers. Nothing works. This is a really weird problem. Other files that have similar queries and return the same values work just fine. There's just three or so files that use ```$row['article']``` that don't return that value for whatever reason. It seems like one of those problems that seems like it's genuinely just broken somehow, but I'll have to keep looking for the solution. – Carson D Jan 08 '21 at 04:00
  • I wish I could help more but without access to your systems there's not much else I can offer. Sorry! – Nick Jan 08 '21 at 04:05
  • Have you tried putting $published = "yes"; before the line mysqli_stmt_bind_param...? – René Pöpperl Jan 08 '21 at 13:18
  • @RenéPöpperl Yes, I tried that out without success. Thank you for the suggestion, though. – Carson D Jan 08 '21 at 13:31
  • I find it unorthodox to only write the ON clause on the final JOIN and pack it with all of the conditions. – mickmackusa Jan 09 '21 at 03:07
  • Why is `htmlspecialchars_decode($row['article'], ENT_QUOTES)` necessary at all? Why do you have encoded characters being stored in the db? Are you not using a prepared statement with placeholders when saving articles? If you don't need the tags which exist in the article, why aren't you sanitizing the data BEFORE saving? – mickmackusa Jan 09 '21 at 06:55
  • The result set object can be iterated by a `foreach()` (as an array of associative arrays) without making iterated fetch calls. If this were my application and the database only contained sanitized data, I'd enjoy the appropriate call of `fetchAll()`. – mickmackusa Jan 09 '21 at 07:14
  • Do you have curly quotes (multibyte characters) in your articles? Are you exercising "[UTF-8 All the way through](https://stackoverflow.com/q/279170/2943403)"? – mickmackusa Jan 09 '21 at 07:40
  • log contents of each loop and check for which loop it is giving that error and check the contents in that index – aRvi Jan 09 '21 at 11:05
  • @mickmackusa Those are good points. I'll have to rewrite some of this code. – Carson D Jan 10 '21 at 03:59
  • @CarsonD Stack Overflow does not want you promoting your website (anti-spam policy). Please remove your weblink and replace it with the db entry text that causes the issue. Your question body content needs to be reined in -- please remove all irrelevant details so that volunteers can focus on the text which is mutated by the 2 function calls and breaks the json encoding process. – mickmackusa Jan 10 '21 at 08:15

1 Answers1

1

It's best practice before echoing anything to set the header of the response to json with header('Content-Type: application/json'); (normally I set my headers at the beginning of the php script). sometimes the browser might see the reply as html, not as JSON and that might be one of the reasons why it's working inconsistently.

now you should be able to set article without stripping tags, because you are specifically telling the browser that this is json, not html so change this line as $data['article'] = $row['article'], let json_encode take care of escaping characters.

if it still doesn't work can you post the API answer as seen in the network tab of the browser inspector?

Edit: So apparently it is not working because there are some invalid characters in the string in the database. Those are usually introduced when copying text from other software like ms word that uses those characters for formatting and its not easy to get rid of all of them. I would suggest you to go and read this answer, you should apply this technique before adding new articles. And since you already have corrupted articles you have to apply it after reading an article from the db

Quick and dirty solution that might work in your case:

$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
brein
  • 1,409
  • 9
  • 11
  • Thanks for the response. After adding `header('Content-Type: application/json');` right after the opening ` – Carson D Jan 09 '21 at 05:15
  • it's weird that it works 30% of the time, maybe it's an issue with your VPS provider? Can you try edit your php file to return a dummy json right after setting the header to see if it always work? – brein Jan 09 '21 at 05:27
  • 1
    I recommend removing all irrelevant details from your question and boiling it down to a [mcve]. This way volunteers can focus on the singular aspect of your issue. We Need Debugging Details, not vague / 30% outcomes. – mickmackusa Jan 09 '21 at 06:47
  • @brein That's kind of what I was thinking — I can't think of any other reason right now. To answer your question, if this is sufficient, when I edit out the `$data['article'] = $row['article']` line, the PHP file returns all the other values every time (the article ID, title, date, etc.), which is why I think that line is somehow causing the problem. – Carson D Jan 09 '21 at 06:59
  • @mickmackusa I would agree. However, I have one question: do you have any specific suggestions on things I should remove? I can definitely remove the chunk of code in the `getRandomArticles()` function that doesn't affect the problem, but besides that, I feel like everything else contributes to the solving of the problem. – Carson D Jan 09 '21 at 07:04
  • It would be useful to know the db data that causes the error (not just, "it works sometimes). – mickmackusa Jan 09 '21 at 07:07
  • 1
    maybe there are some weird characters in the article cell that is preventing json_encode from generating a valid json. some articles don't have that character, that's why sometimes it works. so probably php is throwing notices or errors but they are muted because this server I assume is configured as production and so it's hiding notices/warnings/errors. You should check on your server the file where php is logging all the errors it encounters, you might find something useful. – brein Jan 09 '21 at 08:51
  • @brein Yeah, that's a good point. I found three specific articles that were problematic; when I took off their "published" statuses (meaning they wouldn't be included in the queries any longer), the articles loaded correctly every time. I couldn't find any similarities between these articles that differentiated them from the others (certain character usage, etc.). I recreated these articles word-for-word and these new ones didn't cause any problems. I wonder if it could be a problem related to importing the database or something of that sort. I'll keep looking and let you know. – Carson D Jan 10 '21 at 03:49
  • @Car sometimes there are invisible or multibyte characters which are transmitted in the copy-paste process (by the end user who is posting the new article text). `var_dump()` is especially good because it counts the true length of the string. – mickmackusa Jan 10 '21 at 08:18
  • @CarsonD I have updated my answer in light of this new information – brein Jan 10 '21 at 08:46