PHP服务器发送事件:从另一个文件触发自定义事件
我需要通知客户端是否有一些客户端对数据库进行了更改。我认为它是单向连接(它只是发送事件的服务器),所以我不需要WebSoCusits。 逻辑如下:PHP服务器发送事件:从另一个文件触发自定义事件,php,server-sent-events,Php,Server Sent Events,我需要通知客户端是否有一些客户端对数据库进行了更改。我认为它是单向连接(它只是发送事件的服务器),所以我不需要WebSoCusits。 逻辑如下: 客户机获取公共API(比如说/API.php?action=add&payload=…),并通过此获取对数据库进行更改 mysqli_result返回true后,应触发服务器发送事件,通知其他客户端(或其服务人员)数据库已更改 警告是: 我的SSE逻辑被放在一个单独的文件(/SSE.php)中,以避免API逻辑混乱,并侦听单独的端点client
- 客户机获取公共API(比如说
),并通过此获取对数据库进行更改/API.php?action=add&payload=…
返回true后,应触发服务器发送事件,通知其他客户端(或其服务人员)数据库已更改mysqli_result
- 我的SSE逻辑被放在一个单独的文件(
)中,以避免API逻辑混乱,并侦听单独的端点clientside。因此,我需要从/SSE.php
触发SSE,并将结果推送到所有客户端api.php
- 在
中,我定义了一个push函数,该函数将来自sse.php
的消息作为参数。不管我怎么做,这个函数从来没有被调用过api.php
- 我强烈希望避免创建守护进程和任何类型的无限循环。但是如果没有它们,客户端侦听器(
)的连接每3秒关闭并重新打开一次eventSource
api.php
<?php
header("Access-Control-Allow-Origin: *");
include 'connection.php'; // mysqli credentials and connection object
include_once 'functions.php'; // logics
$action = isset($_GET['a']) ? $_GET['a'] : 'fail';
switch ($action) {
case "add":
$result = api__handle_add(); // adds data to DB and returns stringified success/failure result
require "./sse.php"; // file with SSE logic with ev__ namespace
ev__send_data($result); // this defined function should send message to clients
break;
// ...
case "testquery":
require "./sse.php";
ev__send_data("Test event!!! Ololo");
break;
case "fail": default:
header('HTTP/1.1 403 Forbidden', true, 403);
}
<?php
header("Access-Control-Allow-Origin: *");
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
header("Connection: keep-alive");
$_fired = false; // flag checking for is anything happening right now
while (1) { // the infinite loop I strongly want to get rid of
if (!$_fired) {
usleep(5000); // nothing to send if nothing is fired, but I should keep the connection alive somehow
} else {
// here I should call 'ev__send_data($message_from_api_php)'
// thus I require this file to `api.php` and try to call 'ev__send_data' from there.
}
ob_flush();
flush();
}
function ev__send_data(string $msg) {
global $_fired;
$_fired = true;
echo "data: $msg". PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
usleep(1000);
$_fired = false;
}
客户端实现是典型的,onopen
、onerror
和onmessage
处理程序是eventSource
的处理程序。我想我不需要把它带到这里,因为它与我在这里看到的数千个例子以及其他来源没有区别
总而言之:我想从SSE文件外部触发SSE并从外部传递数据,我想去掉while(1){}
,只在真正发生事件时才发出事件。我遇到了成千上万的教程和主题,除了关于如何每n秒向客户端发送服务器时间之类的典型“教程案例”之外,什么都没有找到。而且我认为我不需要WebSocket(考虑到我的本地开发环境是Win10+XAMPP,我的prod环境将类似于Linux,我不认为我可以在没有第三方依赖项的情况下实现我自己的WebSocket,我不想安装这些依赖项)
我有机会吗
编辑
我发现了一个稍微不同的方法。现在,我的服务器事件生成依赖于MySQL数据库中单独的flags
表中设置的布尔标志。当我需要发出一个事件时,API会更改definite标志(在本例中,当一个客户端提交了数据库中的一些更改时,API还会查询标志
表,并将标志DB_CHANGED
设置为1
)。并且sse.php
在无限循环的每次迭代中(每5秒)对flags
表进行查询,并根据flags的状态发出事件,发出后,通过另一个查询将相应的flag设置为0
。这有点管用
当前sse.php
示例:
<?php
header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
require_once "connection.php"; // mysqli connection object
set_time_limit(600);
stream();
function stream() {
global $connection;
while (1) { // oh that infinite loop
$flags = []; // there will be fetched flags from DB
$flags_sqli = $connection->query("SELECT * FROM `flags`");
while ($flag = $flags_sqli->fetch_assoc()) {
$flags[$flag['key_']] = ev__h__to_bool_or_string($flag['value_']);
}
if ($flags['ON_TEST']) {
ev__send_data("Test event!!! Ololo");
ev__update_flag("ON_TEST", "0");
} elseif ($flags['ON_DB_ADD']) {
ev__handle_add();
} else {
echo PHP_EOL; // keep connection alive - push single "end of line"
}
ob_flush();
flush();
sleep(5); // interval per iteration - 5 secs
}
}
function ev__send_data(string $msg) : void {
echo "data: ".date("d-m-Y")." ".date("H:i:s").PHP_EOL;
echo "data: $msg".PHP_EOL;
echo PHP_EOL;
}
function ev__handle_add() : void {
global $connection;
$table = $connection->query('SELECT `value_` FROM `flags` WHERE `key_` = "LAST_ADDED_TABLE"')->fetch_row()[0];
$query = "SELECT * FROM `banners_".$table."` WHERE `id` = (SELECT MAX(`id`) FROM `banners_".$table."`)";
$row = $connection->query($query)->fetch_assoc();
echo "event: db_add_row".PHP_EOL;
echo 'data: { "manager": '.$row['manager_id'].', "banner_name": "'.$row['name'].'", "time": "'.date("d-m-Y").' '.date("H:i:s").'" }'.PHP_EOL;
echo PHP_EOL;
ev__update_flag("ON_DB_ADD", "0");
}
function ev__update_flag(string $key, string $value) {
global $connection;
$query = "UPDATE `flags` SET `value_` = '$value' WHERE `flags`.`key_` = '$key';";
$connection->query($query);
}
function ev__h__to_bool_or_string($value) {
return $value === "0" || $value === "1"
? boolval(intval($value))
: $value;
}