0

I'm facing a problem with my app. It's in a JS-PHP environment. I've made a minimal reproduced example below.

script.js:

function postToHandler(){

    let xhr = new XMLHttpRequest();
    xhr.open("POST", "./apps/researcher.php");

    let form = new FormData();
    form.append('triggerLoop', "mockData");

    xhr.send(form);

    activateGlobalsListener();
}

function activateGlobalsListener(){
    
    setInterval(function(){
        let xhr_listen = new XMLHttpRequest();
        xhr_listen.open("POST", "./apps/researcher.php");

        let form = new FormData();
        form.append('listen', "mockData");
        xhr_listen.send(form);

        xhr_listen.onreadystatechange = function (){

            if(xhr_listen.readyState === XMLHttpRequest.DONE){
                console.log("RECEIVED LISTEN DATA");
                console.log(xhr_listen.response);   
            }
        }
    },1000);
}

And here is the php file

<?php

//SOURCE OF PROBLEM BELOW: When I uncomment the session_start line, the listening process stops working
//session_start();

$_SESSION['globalVar'] = "undefinedData";

if (!empty($_POST['triggerLoop'])) {

    for ($x = 0; $x < 10; $x++) {
        $_SESSION['globalVar'] = getResult();
        sleep(1);
    }
}

//------------------------------------------------------
//Listen
if (!empty($_POST['listen'])) {
    echo  $_SESSION['globalVar'];
}

function getResult()
{
    return rand(10, 100);
}

?>

My script.js file creates multiple XMLHttpRequests to the same researcher.php file.

The first request will initiate a loop within the PHP file. This loop will update a SESSION variable with a random int. There is a 1 second sleep call that also occurs during every iteration of that loop.

Then after the script.js has triggered this, my script.js file starts an interval (also every 1 second), where it will send a request to that same PHP file, attempting to read the value of that SESSION variable (which is supposed to get updated with a new value every 1 second).

But I find that the value of that SESSION variable is always the initial value with which it was defined.

So where am I wrong?

I have looked at similar questions on Stackexchange and some say that you should add a "session_start()" line at the top of the PHP file. But when I do that I've noticed a peculiar thing......the script.js is not getting the responses (within the setInterval) immediately. It seems to get those responses delayed....only after the entire PHP loop is finished. Why though? Why does starting a session have that effect?

Other similar questions also say that you should add a redirect call every time a SESSION variable is updated. Like

header("location:./Researcher.php");

To redirect the file to itself? But if I do that, won't it break the current loop and all the ongoing processes?

1
  • Instead of polling the server every second for changes you might want to consider Server Sent Events. It might also be easier on your session file. Commented Apr 22 at 20:39

3 Answers 3

0

TL;DR: php session are not meant for this.

How to do this properly depends on you exact usecase. But (as a blind implementation) I would rewrite this all to use a queue (a proper MQ implementation would be good, but not necessary, as a low-effort option you can use DB or Redis for this) to store requests.

You would have three separate parts:

  • endpoint for dumping data
  • endpoint for requesting current state
  • background worker for doing computation

The goal would be to decouple the JS code that POSTs data and JS code that read the current state. That would be facilitated by the worker (long running CLI php script) that look for new messages in the queue. When the message is found, you compute it and update the current state in the database. The other endpoint just makes quick read fro the current state in database and renders the result.

Might be an overkill ... as I said, depends on what you code is actually doing.

P.S. in 2020s we use fetch() for "ajax".

Sign up to request clarification or add additional context in comments.

Comments

0

To allow simultaneous reads (or even writes) without locking up the rest of the app, you need to release the session lock in the triggerLoop request after you're done writing to the session.

Here’s how to modify your PHP:

<?php
session_start();

if (!isset($_SESSION['globalVar'])) {
    $_SESSION['globalVar'] = "undefinedData";
}

if (!empty($_POST['triggerLoop'])) {
    // We start the session and set up the initial var
    session_write_close(); // 🚀 RELEASE THE LOCK HERE

    for ($x = 0; $x < 10; $x++) {
        session_start(); // Reopen session to write
        $_SESSION['globalVar'] = getResult();
        session_write_close(); // Write and close again
        sleep(1);
    }
    exit;
}

if (!empty($_POST['listen'])) {
    session_start();
    echo $_SESSION['globalVar'];
    session_write_close(); // Always close after reading
    exit;
}

function getResult()
{
    return rand(10, 100);
}
?>

DO NOT redirect to the same file with header("Location:...") in a loop That would create an infinite redirect loop and never allow your current process to complete, breaking everything.

Using sessions like this is okay for prototyping, but in production, you'd be better off using:

Redis

Memcached

Database storage

This allows truly concurrent reads and writes and avoids blocking issues.

Comments

0

If you use session_open() each PHP response sends PHPSESSID cookie. And next time it tries to read this cookie from requests. Probably your js does not send it back. You should add property to xmlhttprequest:

let xhr = new XMLHttpRequest();
xhr.open("POST", "./apps/researcher.php");
xhr.withCredentials = true;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.