Giter Site home page Giter Site logo

hhxsv5 / php-sse Goto Github PK

View Code? Open in Web Editor NEW
405.0 11.0 46.0 385 KB

A simple and efficient library implemented HTML5's server-sent events by PHP, is used to real-time push events from server to client, and easier than Websocket, instead of AJAX request.

License: MIT License

PHP 100.00%
sse server-sent-events eventsource events sever-events event-stream

php-sse's Introduction

PHP SSE: Server-sent Events

A simple and efficient library implemented HTML5's server-sent events by PHP, is used to real-time push events from server to client, and easier than Websocket, instead of AJAX request.

Requirements

  • PHP 5.4 or later

Installation via Composer(packagist)

composer require "hhxsv5/php-sse:~2.0" -vvv

Usage

Run demo

  • Run PHP webserver
cd examples
php -S 127.0.0.1:9001 -t .
  • Open url http://127.0.0.1:9001/index.html

Demo

Javascript demo

Client: receiving events from the server.

// withCredentials=true: pass the cross-domain cookies to server-side
const source = new EventSource('http://127.0.0.1:9001/sse.php', {withCredentials: true});
source.addEventListener('news', function (event) {
    console.log(event.data);
    // source.close(); // disconnect stream
}, false);

PHP demo

Server: Sending events by pure php.

use Hhxsv5\SSE\Event;
use Hhxsv5\SSE\SSE;
use Hhxsv5\SSE\StopSSEException;

// PHP-FPM SSE Example: push messages to client

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no'); // Nginx: unbuffered responses suitable for Comet and HTTP streaming applications

$callback = function () {
    $id = mt_rand(1, 1000);
    $news = [['id' => $id, 'title' => 'title ' . $id, 'content' => 'content ' . $id]]; // Get news from database or service.
    if (empty($news)) {
        return false; // Return false if no new messages
    }
    $shouldStop = false; // Stop if something happens or to clear connection, browser will retry
    if ($shouldStop) {
        throw new StopSSEException();
    }
    return json_encode(compact('news'));
    // return ['event' => 'ping', 'data' => 'ping data']; // Custom event temporarily: send ping event
    // return ['id' => uniqid(), 'data' => json_encode(compact('news'))]; // Custom event Id
};
(new SSE(new Event($callback, 'news')))->start();

Symfony and Laravel demo

Server: Sending events by Laravel or Symfony.

use Hhxsv5\SSE\SSE;
use Hhxsv5\SSE\Event;
use Hhxsv5\SSE\StopSSEException;

// Action method in controller
public function getNewsStream()
{
    $response = new \Symfony\Component\HttpFoundation\StreamedResponse();
    $response->headers->set('Content-Type', 'text/event-stream');
    $response->headers->set('Cache-Control', 'no-cache');
    $response->headers->set('Connection', 'keep-alive');
    $response->headers->set('X-Accel-Buffering', 'no'); // Nginx: unbuffered responses suitable for Comet and HTTP streaming applications
    $response->setCallback(function () {
        $callback = function () {
            $id = mt_rand(1, 1000);
            $news = [['id' => $id, 'title' => 'title ' . $id, 'content' => 'content ' . $id]]; // Get news from database or service.
            if (empty($news)) {
                return false; // Return false if no new messages
            }
            $shouldStop = false; // Stop if something happens or to clear connection, browser will retry
            if ($shouldStop) {
                throw new StopSSEException();
            }
            return json_encode(compact('news'));
            // return ['event' => 'ping', 'data' => 'ping data']; // Custom event temporarily: send ping event
            // return ['id' => uniqid(), 'data' => json_encode(compact('news'))]; // Custom event Id
        };
        (new SSE(new Event($callback, 'news')))->start();
    });
    return $response;
}

Swoole demo

Server: Sending events by Swoole Coroutine Http Server. Install Swoole 4.5.x: pecl install swoole.

use Hhxsv5\SSE\Event;
use Hhxsv5\SSE\SSESwoole;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Swoole\Http\Server;
use Hhxsv5\SSE\StopSSEException;

// Swoole SSE Example: push messages to client

$server = new Server('0.0.0.0', 5200);
$server->set([
    'enable_coroutine'   => true,
    'max_coroutine'      => 10000, // worker_num*10000
    'reactor_num'        => swoole_cpu_num() * 2,
    'worker_num'         => swoole_cpu_num() * 2,
    'max_request'        => 100000,
    'buffer_output_size' => 4 * 1024 * 1024, // 4MB
    'log_level'          => SWOOLE_LOG_WARNING,
    'log_file'           => __DIR__ . '/swoole.log',
]);

$server->on('Request', function (Request $request, Response $response) use ($server) {
    $response->header('Access-Control-Allow-Origin', '*');
    $response->header('Content-Type', 'text/event-stream');
    $response->header('Cache-Control', 'no-cache');
    $response->header('Connection', 'keep-alive');
    $response->header('X-Accel-Buffering', 'no');

    $event = new Event(function () {
        $id = mt_rand(1, 1000);
        $news = [['id' => $id, 'title' => 'title ' . $id, 'content' => 'content ' . $id]]; // Get news from database or service.
        if (empty($news)) {
            return false; // Return false if no new messages
        }
        $shouldStop = false; // Stop if something happens or to clear connection, browser will retry
        if ($shouldStop) {
            throw new StopSSEException();
        }
        return json_encode(compact('news'));
        // return ['event' => 'ping', 'data' => 'ping data']; // Custom event temporarily: send ping event
        // return ['id' => uniqid(), 'data' => json_encode(compact('news'))]; // Custom event Id
    }, 'news');
    (new SSESwoole($event, $request, $response))->start();
});
$server->start();

License

MIT

php-sse's People

Contributors

deepdiver1975 avatar dunglas avatar fabianofa avatar hhxsv5 avatar mlazze avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-sse's Issues

SE Eventsource Asp.net Core 3.1

[ApiController]
[Route("/SSE/[action]")]
public class SSEController : Controller
{
private static ConcurrentBag clients;
static SSEController()
{
clients = new ConcurrentBag();
}
[HttpPost]
public async Task SSECallbackMsg()
{
await CallbackMsg("test");
}
private async Task CallbackMsg(string test)
{
foreach (var client in clients)
{
try
{
var data = string.Format(test);
await client.WriteAsync(data);
await client.FlushAsync();
client.Dispose();
}
catch (Exception)
{
StreamWriter ignore;
clients.TryTake(out ignore);
}
}
}
[HttpGet]
public HttpResponseMessage GETSubscibe()
{
HttpResponseMessage response = new HttpResponseMessage();
response.Content = new PushStreamContent((a, b, c) =>
{ OnStreamAvailable(a, b, c); }, "text/event-stream");
return response;
}

private void OnStreamAvailable(Stream stream, HttpContent content,
    TransportContext context)
{
    var client = new StreamWriter(stream,Encoding.UTF8);
    clients.Add(client);
}

}
Javascript Method of calling above is like

function listenForServerEvents() {
var source = new EventSource('https://localhost:5002/SSE/GETSubscibe');
source.addEventListener("open", function (event) {
console.log('onopen');
}, false);
source.addEventListener("error", function (event) {
if (event.eventPhase == EventSource.CLOSED) {
source.close();
}
}, false);
source.addEventListener("message", function (event) {
console.log('onmessage: ' + event.data);
}, false);
}
when executing, above js function, i am getting error as EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection.

Should add anything in startup.cs or is there any mistake., If anyone knows ,kindly help

SSE creates new connection

I'm wondering the script below that I grabbed from Swoole website creates new connection or thread every couple of seconds and I don't like it. While yours is not which is I'm looking for. Sorry but this is not really an issue. Just want to ask or mentor me what's the idea to prevent SSE from creating new connection every couple of seconds. I want SSE to maintain that connection until that client closes or refresh the browser. Thank you in advance.

while(true):
        $data = "event: ping\n";
        $response->write($data);
        $curDate = date(DATE_ISO8601);
        $data = 'data: {"time": "' . $curDate . '"}';
        $data .= "\n\n";
        $response->write($data);
        $counter--;
        if (!$counter) {
            $data = 'data: This is a message at time ' . $curDate . "\n\n";
            $response->end($data);
            break;
        }
        Coroutine::sleep(3);
    endwhile;

Screenshot from 2021-03-02 08-18-35

Allow to set the retry value

Reviwing the code, I struggle to find where I could set a custom timeout between connections.
I understand that the block below uses a fixed value of 2 seconds:

SSE::start():

            ...
            if ($changedData !== false) {
                $event = [
                    'id'    => uniqid('', true),
                    'type'  => $eventType,
                    'data'  => (string)$changedData,
                    'retry' => 2000,//reconnect after 2s
                ];

Adding the parameter in the signature would allow the developer to handles how often the server should send data to the client, making changes on the signature and on definition of event like:

public function start(Update $update, int $milliRetry, $eventType = null)
{
   ...
                $event = [
                    'id'    => uniqid('', true),
                    'type'  => $eventType,
                    'data'  => (string)$changedData,
                    'retry' => $milliRetry,//reconnect after 2s
                ];
}

If approved I could make a Pull Request.

Handle connection reset by the client?

From my understanding we should check for connection_aborted() and exit the loop so that the server can finally terminate the script.

Or am I missing anything?

THX

Not working -> all console.log()'s appear at once after 2 min. of pending

Hello,

I have a symfony 4.4 project with v2 of your bundle installed. Unfortunately it doesn't work :-( I exactly used the example code you provided but the behavior is that the xhr request pends for 2 minutes and then returns a 200. Then all console.log()'s appear at once containing the data sent by the symfony controller (php).

So in my case it's not real time ouput as expected.

Am I doing something wrong? Is this normal behavior?

Thanks for any clearification!

Bye Defcon0

How to implement this with database?

It works but always fire the result within 3 secs.
How to implement this with real database which is just fire a new text if new data has been inserted?

How to make it work in Swoole Server

Hi, I'm trying to implement in Swoole Server but it returns nothing in my Network tab of Google Chrome. No Status, No Protocol, No Type and No Size. But it is working fine the terminal.

Here is the code...

$http->on('request', function (Request $request, Response $response) {
    $response->header('Content-Type', 'text/event-stream');
    $response->header('Cache-Control', 'no-cache');
    $response->header('Connection', 'keep-alive');
    $response->header('X-Accel-Buffering', 'no');
    
    $response->end( (new SSE())->start(new Update(function () {
        $time = date('Y-m-d H:i:s');
        $news = [['id' => $time, 'title' => 'title ' . $time, 'content' => 'content ' . $time]]; // Get news from database or service.
        if (empty($news)) {
            return false; // Return false if no new messages
        }
        return json_encode(compact('news'));
    }), 'news') );    
});

Thank you in advance.

StopSSEException() implementation

Hi, sorry for this noob question. When this $shouldStop variable be true? :) I'm trying to implement this condition taken from your sample demo but I don't fully get. BTW, I'm currently using your package in Swoole. Thanks again.

$shouldStop = false;
if ($shouldStop) {
    throw new StopSSEException();
}

How about the performance tuning?

If there are ten or more client connect to the event script, the server slow down very much, how to tune the performance?Thank you.

Push events only when POST method kicks in

Sorry, I have a lot of questions regarding implementing Swoole, because their documentation is not that detailed enough for newbie. Anyway, Is it possible to push the events when there's a POST method only? See pseudo code below.

if request method is POST
  Push the updated/new event 
else
  Sleep momentarily or send a SSE comment to make the connection alive 

Thank you again. You are a big help.

Send Ping

Hi, I have a situation that I need to send ping only if there's no new data on the database. And

$event = new Event(function () use ($request, $response, $db) {
            if ($request->server['request_method'] === 'GET') {                 
                $chat = $db->get('chat')->execBuilder();                             

                # IF no new data? Send ping
                // How to send ping in Swoole way?

                # ELSE
                return ['id' => $request->get['productId'], 'data' => json_encode(compact('chat'))];
            }

            return false;
        }, 'chat');

        (new SSESwoole($event, $request, $response))->start(2);

Fatal Error: Maximum execution time of 120 seconds exceeded (in SSE.php:20)

Hi,
I'm trying to use your library. So far, it working well with your example (Symfony 5.2, PHP 7.4), but, in my logs, I have this error :
php.CRITICAL: Fatal Error: Maximum execution time of 120 seconds exceeded {"exception":"[object] (Symfony\\Component\\ErrorHandler\\Error\\FatalError(code: 0): Error: Maximum execution time of 120 seconds exceeded at D:\\wamp\\www\\back\\vendor\\hhxsv5\\php-sse\\src\\SSE.php:20)"}
and the PHP process is restarted (variables in code are reinitialised) but it doesn't break the stream.
How can I can avoid these errors, and especially the restarting of the process?
Thanks

How to send a message during a "real" process

I read your docs but I don't really understand how I can send a message during a process.

In my case, for exemple, I'd like to "dispatch/broadcast/push" (I don't know what's the correct term) a message from server to specific clients after an Article is created.

For example, I go on a page /article/create and I submit the form to create the Article.
As soon as the Article is created, I'd like to push a message for all the clients "subscribed" to this article's category. The response of this request must a "classic" Response, for the page to reload, etc., soI don't know what to do (and where) to send the message to the clients.

Where do I need to put the logic to create the StreamedResponse and send the message ?

ps : I'm using Symfony 4.2

Im confused about server side ??

So in demo you use php -S to spawn a php's own little server on 9001, but this seems to be able to only talk to one client at a time ? ie while one browser or tab is loaded, no others can connect, just get blank page with loading...

How do you think we should use this to push updates to multiple users at a time ?

Interesting but not sure if it is what I want

I use drupal CMS to create websites. If you do not know drupal, it is quiet similar to wordpress in terms of server requirements. Drupal also allows to integrate 3rd party api.

What I want?

I want something free open source thing which can send messages or notifications (probably push notifications) from my website to users' mobile phones. The user might or might not have our app installed. In fact, I do not have any mobile app currently.

May be you can suggest something else. Actually, I want to try all possibilities to avoid relying on external services. There is a Drupal module which allows to send push notifications, but it relies on GCM and APN. I want to avoid this dependency.

EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection.

Hi,
I am getting proper response from when I call the specific route.
for example server.php is routed to 'http://127.0.0.1:8000/server' i am getting proper response as you said(but Eventstream still empty this was another issue I found this package). But when i tried to trigger the EventSource in my html file I am getting below error. From where the extra header is coming.

EventSource's response has a MIME type ("application/json") that is not "text/event-stream". Aborting the connection.

Please fix this issue.
Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.