Receiving external data in a Server Sent Event and PHP - php

I'm having trouble understanding Server Sent Events, particularly on the server side. All the examples I find are basically just sending the server time, which isn't terribly helpful.
I'm trying to bring data in from an external resource that is sending a POST request to the file, specifically a Twilio webhook whenever a new message is received.
The problem that I'm running into is that the data sent from the webhook isn't accessible from my request to the Server Sent Event, which makes sense when you think about it because they're two different sessions.
I could create create another file for the webhook to access and add the message data to an SQL database each time a new message is received. Then, in the file that is my serves as my EventSource, I could query that data and see if it's changed, but doesn't that seem horrible inefficient to be querying an SQL database every three seconds, even if I am comparing the data using a session variable or something?
Ideally, it would be something like this:
header('Content-Type: text/event-stream; charset=utf-8');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
header("Access-Control-Allow-Origin: *");
$body = $_REQUEST['Body'];
$finalbody = !empty($body) ? $body : 'nothing to see here...';
echo "id: $id" . PHP_EOL;
echo "data: $finalbody" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
But that obviously doesn't work when you use it as the EventSource because $_REQUEST['Body'] can't be found.
What good is are Server Sent Events if they have to be constantly querying an external source rather than having data sent to them on-demand and then using that data to send the event on it's way?
Am I missing something?
Any insight here would be much appreciated.

Related

How to execute a function after response send in PHP

I am trying to execute a callback function after response send in php.
For example in JAVA i made that using Threads. But in php after response it finish the process of script.Besides I try to implement pthreads but its too much complicated.
In my code:
if(isset($_REQUEST['x']) && $_REQUEST['x'] == "x") {
header('Content-type: application/json');
$data = json_decode(file_get_contents('php://input'), TRUE);
if (!empty($data)) {
$request = new XRequest($data['params']);
$customParams = unserialize(file_get_contents('customParams'));
$customParams->callCallback($request); //Calling from another PHP class
echo(json_encode(array('status' => 'OK')));
}
}
The request come from different server. I want to start first php echo response when response send i want to call $customParams->callCallback($request);
How can I do that? Any ideas?
In php i solved my problem using bottom code. But pay attention to fastcgi_finish_request . With out this my server can not stop the first response and start callback.
Thanks.
ob_start();
// Send your response.
echo json_encode(array('status' => 'ok')) ;
// Get the size of the output.
$size = ob_get_length();
// Disable compression (in case content length is compressed).
header("Content-Encoding: none");
header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted");
header("Status: 202 Accepted");
// Set the content length of the response.
header("Content-Length: {$size}");
// Close the connection.
header("Connection: close");
ignore_user_abort(true);
set_time_limit(0);
// Flush all output.
ob_end_flush();
ob_flush();
flush();
session_write_close();
fastcgi_finish_request();
// Do processing here
sleep(5);
callBackAfterResponse();
PHP's concurrency model is simple and based around the fact that multiple PHP scripts can be executed simultaneously by a Web server. So typically, the way you'd implement this is by
Placing the body of your callback function in its own, separate script; and
Invoking it from the parent script through an outgoing Web request (using cURL or similar).
That is, have the first PHP script request the second at a URL on (presumably) the same Web server, just as though a user had opened the two URLs sequentially in their Web browser. This way, the second script can continue to run after the first has completed its response and terminated.
More sophisticated approaches are possible, involving message queues or remote-procedure call mechanisms like XML-RPC and Apache Thrift, if the second PHP script is made to run separately and continuously in its own process. But I think this will be enough for what you're trying to do.

How to handle Session in Symfony2 after headers have been sent to client?

I have an action in Symfony2 controller that is sending out a response back to the client (as detailed here). After the function sends out the response, I have an event subscriber that listens for onkernelTerminate event since I'll be doing some heavy work after the response is sent to the client.
Everything seems to be working normally for the part that's doing the heavy work except that there's line in the code that is referencing a service that utilizes Session, which needs it for storing tokens and eventually communicating with an external API.
The error is of course: Failed to start the session because headers have already been sent.
Is there a way to start a Session even headers have been sent? Or what would be a better approach to handle this issue?
I ended up fixing this issue by getting the session from the incoming request and storing it to a local variable in my action controller, then made sure the session got activated or started:
use Symfony\Component\HttpFoundation\Request;
public function myFunctionAction(Request $request) {
$session = $request->getSession();
$session->start();
next, I immediately sent the response and headers to the client:
ob_start();
$response = new Response('Status: OK', 200);
echo $response; // send the response
header('Connection: close');
header('Content-Length: ' . ob_get_length());
ob_end_flush();
if (ob_get_level() > 0) {
ob_flush();
}
flush();
Then the code continues doing the heavy work as usual. The key to solving this issue was, however, to make sure that the service that utilizes Session to provide the option to others who will be referencing it to save or not save the session within the service. If the session gets saved then it won't be active anymore and it gets closed, and once it is closed you cannot start it again since headers were already sent. The client code that references the service should however save and close the session in the end of course.
I ended up not listening for the kernel.terminate event since there was no need for it in my case.

Processing POST data from a third party

I have been struggling with the following problem for few days. I am expected to receive POST data from a third party company for certain real time events. I have been in contact with them as to why my script isn't seeing anything. Unfortunately, they are less then helpful and just tell me "it works for everybody else".
I setup a simple PHP script after doing some research on the web but it always shows no post data. This isn't my area of expertise so I may be missing something obvious.
Here is the only documentation they give:
This API will send a real-time http request upon every successful event with all of the conversion data. The data is sent via an http POST method, and the data is JSON formatted.
This is my script which for now is trying to just log it to a file. The file is created and I see the IP address of the request but the output is empty in terms of post data.
ob_start();
echo "Request from :" . $_SERVER[REMOTE_ADDR];
echo "print_r:".print_r($_POST,true);
if(array_key_exists('app_id', $_POST))// me attempting to access a specific key they claim is in the post data
{
echo "app id = " . $_POST['app_id'];
}
//I also tried both of these and neither output anything
//foreach ($_POST as $key => $value) //idea 1
foreach($_POST as $item) //idea 2
{
//echo "key=".$key." value=".$value; //idea 1 log
echo "next=";//idea 2 log
echo $item;
}
$contents = ob_get_flush();
file_put_contents("log.txt",$contents,FILE_APPEND);
There's not a lot to go on here -- who knows what the client is actually sending -- but here's a thought:
POST is just an HTTP command. It's traditional for the body of a POST to be a series of key-value pairs from a form, but it is not actually necessary. It's possible that the remote client is issuing a POST request to your server and then just delivering a JSON blob in the request body, which would not be successfully parsed into the $_POST array.
I recommend exploring the answer at How to get body of a POST in php? to see if that helps shed light on this problem.

Running php code chunk in background

I have a php script which is responsible for reading some request parameters from my iPhone app. Once I do some manipulations to it I save them in db and will need to send some push notification message using apple APNS. So currently its done like this in the code.
<?php
$param1 = $_POST['param1'];
$param2 = $_POST['param2'];
//saving part here
//push notifications
$pushService = new PushService();
$pushService -> init();
$pushService -> push($param1, $param2);
//json response
echo json_encode(array($success, $dbsavedid);
?>
Problem occurs with the push part. Now it takes lot of time for this push notification code chunk to execute because the table has grown with lot of data. Hence the iPhone app waits too long for this to execute (to get the success response to iPhone).
Hence is there any way to make this push part asynchronous and send a response to iPhone side using the echo other than using a separate script for push notifications? Also note that I need to get some data from saved records as well to iPhone side. So I will need the output to reach the iPhone side.
You can force PHP to send a response by using the flush() function for example. (there might be other possibilities to accomplish too)
So what you have to do is write with echo to the output buffer when your db operations finished (these should be really fast if you have 100-1000 records) and right after call the flush() function. Your client should get a response right away.
Also see this link about flush() itself, because there might be other parameters of your enviroment which prevents your response in reaching the client side as soon as expected.
http://php.net/manual/en/function.flush.php
<?php
$param1 = $_POST['param1'];
$param2 = $_POST['param2'];
//saving part here
//json response
echo json_encode(array($success, $dbsavedid);
//response should be sent right away, no need for wait on the pushservice operations
flush();
//push notifications
$pushService = new PushService();
$pushService -> init();
$pushService -> push($param1, $param2);
?>
The actual reasons for this is, my server provider has blocked port 2195 and port 2196 which is used by apple APNS. I believe once you allow it this will be fixed and should work like earlier.

How do I display a message after a file has been downloaded?

I have this PHP code and it works fine.
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="WTBak.zip"');
readfile($ArchiveFileName);
echo $ArchiveFileName;
unlink($ArchiveFileName);
My issue is, how do I give out a message after the last line (unlink) has been executed?
Thanks!
Assumptions:
The client user should receive a message, this is kind of message sent to the client
The response is binary
Abstract:
sending binary information to the client along with text response would be possible if it is mhtml format, used in mails and each browser has (some do nto have) the support for multipart response. Let us not chose this way
sending binary information to respond one request (download file) and another response to another request (status of download) - this is a popular practice.
Solution:
on server: persist the status of download
// pseudocode: log_download_event(seessionid, status='started')
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="WTBak.zip"');
readfile($ArchiveFileName);
echo $ArchiveFileName;
unlink($ArchiveFileName);
// pseudocode: log_download_event(seessionid, status='done')
on server: implement a php that will respond with a status of download
// pseudocode: $DownloadStatus=get_download_status(sessionid)
echo '{staus:' + $DownloadStatus + '}'
on client: on some event trigger the download
window.open("http://nowhere.com/download.php?resuorce=archive-file.zip");
window.theTrackInterval = window.setInterval(trackDownload, 1000);
var trackInterval = function(){
$.get('ajax/test.html', function(data) {
id(data.status=='ready'){
cleanInterval(window.theTrackInterval);
alert('download is done');
}
});
}
This solution will start sending the ajax requests to the server every one second asking "is download done" and when it will receive confirmation "yest it is done" client will stop tracking and alert a message
What is missed:
the implementation of status persistence. i am not PHP guy - forgive me this gap
Look, if you give a message, it will be sent in the file and not shown to the user corrupting / changing the contents of the file. You cannot modify the headers as they've already been sent!
So, I feel, there is now way!
Cheers
I had a similar requirement from client. So I created a tool to show message after a file gets downloaded.
You can see at http://www.iamkumaran.com/xdownloader-a-flash-javascript-library/ and look for demo link.

Categories