PSR12
This commit is contained in:
627
src/Sse.php
627
src/Sse.php
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/** DomFramework
|
||||
* @package domframework
|
||||
* @author Dominique Fournier <dominique@fournier38.fr>
|
||||
@@ -19,305 +20,357 @@ namespace Domframework;
|
||||
*/
|
||||
class Sse
|
||||
{
|
||||
//////////////////////////
|
||||
//// PROPERTIES ////
|
||||
//////////////////////////
|
||||
/** The backend File to use
|
||||
*/
|
||||
private $files;
|
||||
//////////////////////////
|
||||
//// PROPERTIES ////
|
||||
//////////////////////////
|
||||
/** The backend File to use
|
||||
*/
|
||||
private $files;
|
||||
|
||||
/** Set the time between each ping (in seconds). 30 by default
|
||||
*/
|
||||
private $pingTime = 30;
|
||||
/** Set the time between each ping (in seconds). 30 by default
|
||||
*/
|
||||
private $pingTime = 30;
|
||||
|
||||
/** The filepointers storage
|
||||
*/
|
||||
private $fps = array ();
|
||||
/** The filepointers storage
|
||||
*/
|
||||
private $fps = array();
|
||||
|
||||
/** The handlers storage
|
||||
*/
|
||||
private $handlers = array ();
|
||||
/** The handlers parameters
|
||||
*/
|
||||
private $handlersParams = array ();
|
||||
/** The handlers storage
|
||||
*/
|
||||
private $handlers = array();
|
||||
/** The handlers parameters
|
||||
*/
|
||||
private $handlersParams = array();
|
||||
|
||||
//////////////////////////////
|
||||
//// SETTER METHODS ////
|
||||
//////////////////////////////
|
||||
/** The backend Files to use.
|
||||
* The backend directory used to store the files must exists and be readable
|
||||
* If array (namedEvent => filePath, "data" => dataFilePath);
|
||||
* If string dataFilePath
|
||||
* @param string|array $files The files to use
|
||||
* @return $this
|
||||
*/
|
||||
final public function setBackendFiles ($files)
|
||||
{
|
||||
if (! is_array ($files))
|
||||
$files = array ("data" => $files);
|
||||
foreach ($files as $event => $file)
|
||||
//////////////////////////////
|
||||
//// SETTER METHODS ////
|
||||
//////////////////////////////
|
||||
/** The backend Files to use.
|
||||
* The backend directory used to store the files must exists and be readable
|
||||
* If array (namedEvent => filePath, "data" => dataFilePath);
|
||||
* If string dataFilePath
|
||||
* @param string|array $files The files to use
|
||||
* @return $this
|
||||
*/
|
||||
final public function setBackendFiles($files)
|
||||
{
|
||||
if (! file_exists (dirname ($file)))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Can not use the backend file : ".
|
||||
"directory does not exists : %s"), dirname ($file)), 500);
|
||||
if (! is_readable (dirname ($file)))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Can not use the backend file : ".
|
||||
"directory not readable : %s"), dirname ($file)), 500);
|
||||
if (! ctype_graph ($event))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Can not create the backend file : ".
|
||||
"event name contains invalid chars : %s"), $event), 403);
|
||||
}
|
||||
$this->files = $files;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** The pingTime to use. Must be positive. If null, the ping is disabled
|
||||
* @param integer|float $pingTime The time in seconds between two keepalive
|
||||
* pings
|
||||
*/
|
||||
final public function setPingTime ($pingTime)
|
||||
{
|
||||
if (! is_float ($pingTime) && ! is_integer ($pingTime))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Can not use requested pingTime : not a float : %s"),
|
||||
$pingTime), 500);
|
||||
if ($pingTime < 0)
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Can not use requested pingTime : not positive : %s"),
|
||||
$pingTime), 500);
|
||||
$this->pingTime = $pingTime;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** The optional handler to use in DataOnly. Must be callable method
|
||||
* @param callable|null $handler The handler
|
||||
* If callable is null, remove the handler for this event
|
||||
* @param mixed... $params The optional needed parameters
|
||||
*/
|
||||
final public function setHandlerDataonly ($handler, $params = null)
|
||||
{
|
||||
if (! is_callable ($handler) && $handler !== null)
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : can not use handler : not callable"), 500);
|
||||
$this->handlers["data"] = $handler;
|
||||
$args = func_get_args ();
|
||||
array_shift ($args);
|
||||
$this->handlersParams["data"] = $args;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** The optional handler to use in Events. Must be array of callable methods
|
||||
* @param array $handlers The handlers method, array[event=>callable]
|
||||
* @param array|mixed $params The parameters of the handlers
|
||||
* array[event=>callable]
|
||||
* If callable is null, remove the handler for this event
|
||||
* @param array|null $params The optional needed parameters
|
||||
* array(event=>array(params))
|
||||
* if event=>null is set, remove the parameters for the event
|
||||
*/
|
||||
final public function setHandlersEvent ($handlers, $params = null)
|
||||
{
|
||||
if (! is_array ($handlers))
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : can not use handler : not array"), 500);
|
||||
if (! is_array ($params) && $params !== null)
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : can not use handler params : not array"), 500);
|
||||
foreach ($handlers as $event => $handler)
|
||||
{
|
||||
if ($handler === null && key_exists ($event, $this->handlers))
|
||||
{
|
||||
unset ($this->handlers[$event]);
|
||||
unset ($this->handlersParams[$event]);
|
||||
continue;
|
||||
}
|
||||
if (! is_callable ($handler))
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : can not use handler : not callable"), 500);
|
||||
$this->handlers[$event] = $handler;
|
||||
}
|
||||
if (is_array ($params))
|
||||
{
|
||||
foreach ($params as $event => $param)
|
||||
{
|
||||
if (! key_exists ($event, $this->handlers))
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : can not use parameter '$event' : no associated handler"), 500);
|
||||
if ($param === null)
|
||||
unset ($this->handlersParams[$event]);
|
||||
else
|
||||
$this->handlersParams[$event] = $param;
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//// PUBLIC METHODS ////
|
||||
//////////////////////////////
|
||||
/** This method is called by the user's browser.
|
||||
* It send the "ping" each X second and send the backend content if it
|
||||
* is updated.
|
||||
* Never return !
|
||||
*/
|
||||
final public function loop ()
|
||||
{
|
||||
if ($this->files === null)
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : Can not use the backend file : file is not defined"), 500);
|
||||
$this->backendInit();
|
||||
// The last ping in float
|
||||
$lastping = 0;
|
||||
@header ("Cache-Control: no-cache"); // Disable caching of response
|
||||
@header ("X-Accel-Buffering: no"); // Disable NGINX Buffering
|
||||
@header ("Content-Type: text/event-stream");
|
||||
// In tests, abort after 100ms * 50 = 5s. In production, never abort as the
|
||||
// counter is never decreased
|
||||
$testloop = 50;
|
||||
while ($testloop)
|
||||
{
|
||||
// Each $pingTime, send a comment ":ping" to keepalive the connection
|
||||
if ($this->pingTime > 0 && $lastping + $this->pingTime < microtime (true))
|
||||
{
|
||||
echo ": ping\n\n";
|
||||
$lastping = microtime (true);
|
||||
}
|
||||
|
||||
// Try to get data from backends
|
||||
foreach ($this->files as $event => $filePath)
|
||||
{
|
||||
$lines = $this->backendGet ($event);
|
||||
if ($lines === false)
|
||||
continue;
|
||||
$lines = rtrim ($lines);
|
||||
// As data may contains multiple lines, split them
|
||||
foreach (explode ("\n", $lines) as $line)
|
||||
{
|
||||
if (key_exists ($event, $this->handlers) &&
|
||||
is_callable ($this->handlers[$event]))
|
||||
{
|
||||
$array = array ($line);
|
||||
if (key_exists ($event, $this->handlersParams))
|
||||
$array = array_merge ($array, $this->handlersParams[$event]);
|
||||
$line = call_user_func_array ($this->handlers[$event], $array);
|
||||
}
|
||||
if ($line === false)
|
||||
continue;
|
||||
// If the event is data, do not display "event:data", but data:
|
||||
// immediately
|
||||
if ($event !== "data")
|
||||
{
|
||||
echo "event: $event\n";
|
||||
$event = "data";
|
||||
}
|
||||
echo "$event: ".implode ("\n$event: ", explode ("\n", rtrim ($line))).
|
||||
"\n\n";
|
||||
if (! is_array($files)) {
|
||||
$files = array("data" => $files);
|
||||
}
|
||||
}
|
||||
if (defined ("PHPUNIT"))
|
||||
{
|
||||
$testloop--;
|
||||
pcntl_signal_dispatch();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ob_end_flush();
|
||||
@flush();
|
||||
}
|
||||
// Loop each 100ms if there is no data.
|
||||
usleep(100000);
|
||||
foreach ($files as $event => $file) {
|
||||
if (! file_exists(dirname($file))) {
|
||||
throw new \Exception(sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not use the backend file : " .
|
||||
"directory does not exists : %s"
|
||||
), dirname($file)), 500);
|
||||
}
|
||||
if (! is_readable(dirname($file))) {
|
||||
throw new \Exception(sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not use the backend file : " .
|
||||
"directory not readable : %s"
|
||||
), dirname($file)), 500);
|
||||
}
|
||||
if (! ctype_graph($event)) {
|
||||
throw new \Exception(sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not create the backend file : " .
|
||||
"event name contains invalid chars : %s"
|
||||
), $event), 403);
|
||||
}
|
||||
}
|
||||
$this->files = $files;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
//// PRIVATE METHODS ////
|
||||
///////////////////////////////
|
||||
/** Initialize the backend : open all the files and go at the end
|
||||
*/
|
||||
private function backendInit ()
|
||||
{
|
||||
if ($this->files === null)
|
||||
throw new \Exception (dgettext ("domframework",
|
||||
"SSE : Can not use the backend files : files are not defined"), 500);
|
||||
foreach ($this->files as $event => $filePath)
|
||||
/** The pingTime to use. Must be positive. If null, the ping is disabled
|
||||
* @param integer|float $pingTime The time in seconds between two keepalive
|
||||
* pings
|
||||
*/
|
||||
final public function setPingTime($pingTime)
|
||||
{
|
||||
$this->backendInitEvent ($event, $filePath);
|
||||
if (key_exists ($event, $this->fps))
|
||||
{
|
||||
fseek ($this->fps[$event], 0, SEEK_END);
|
||||
stream_set_blocking ($this->fps[$event], false);
|
||||
}
|
||||
if (! is_float($pingTime) && ! is_integer($pingTime)) {
|
||||
throw new \Exception(sprintf(
|
||||
dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not use requested pingTime : not a float : %s"
|
||||
),
|
||||
$pingTime
|
||||
), 500);
|
||||
}
|
||||
if ($pingTime < 0) {
|
||||
throw new \Exception(sprintf(
|
||||
dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not use requested pingTime : not positive : %s"
|
||||
),
|
||||
$pingTime
|
||||
), 500);
|
||||
}
|
||||
$this->pingTime = $pingTime;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the pending lines from requested backend
|
||||
* @param string $event The event to get data
|
||||
* @return null if there is no new line pending
|
||||
* @return string the data lines stored in the lines
|
||||
*/
|
||||
private function backendGet ($event)
|
||||
{
|
||||
// If the file was previously opened but doesn't exists anymore. Close it
|
||||
// and reopen it just after
|
||||
if (key_exists ($event, $this->fps) && (
|
||||
! file_exists ($this->files[$event]) ||
|
||||
! is_readable ($this->files[$event])))
|
||||
/** The optional handler to use in DataOnly. Must be callable method
|
||||
* @param callable|null $handler The handler
|
||||
* If callable is null, remove the handler for this event
|
||||
* @param mixed... $params The optional needed parameters
|
||||
*/
|
||||
final public function setHandlerDataonly($handler, $params = null)
|
||||
{
|
||||
@fclose ($this->fps[$event]);
|
||||
unset ($this->fps[$event]);
|
||||
if (! is_callable($handler) && $handler !== null) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : can not use handler : not callable"
|
||||
), 500);
|
||||
}
|
||||
$this->handlers["data"] = $handler;
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
$this->handlersParams["data"] = $args;
|
||||
return $this;
|
||||
}
|
||||
// If the file was not previously opened, try to open it. Abort if it
|
||||
// doesn't exists again
|
||||
if (! key_exists ($event, $this->fps))
|
||||
{
|
||||
if ($this->backendInitEvent ($event, $this->files[$event]) === false)
|
||||
return false;
|
||||
}
|
||||
$fp = $this->fps[$event];
|
||||
// If the file is truncated by an external action, must restart at 0
|
||||
if (fstat($fp)["size"] < ftell ($fp))
|
||||
{
|
||||
rewind ($fp);
|
||||
}
|
||||
$res = false;
|
||||
while (($line = stream_get_line ($fp, 10000, "\n")) !== false)
|
||||
{
|
||||
$res .= "$line\n";
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/** Initialize the backend for one specific file.
|
||||
* @param string $event The event to start
|
||||
* @param string $filePath The file path where the event data are stored
|
||||
* @return boolean false if the file doesn't exists
|
||||
*/
|
||||
private function backendInitEvent ($event, $filePath)
|
||||
{
|
||||
clearstatcache (true, $filePath);
|
||||
if (! file_exists (dirname ($filePath)))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Backend directory no more exists : %s"), dirname ($filePath)),
|
||||
500);
|
||||
if (! is_readable (dirname ($filePath)))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Backend directory no more readable : %s"), dirname ($filePath)),
|
||||
500);
|
||||
if (! file_exists ($filePath))
|
||||
return false;
|
||||
if (! is_readable ($filePath))
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Backend event file not readable : %s"), $filePath),
|
||||
500);
|
||||
$fp = fopen ($filePath, "r");
|
||||
if ($fp === false)
|
||||
throw new \Exception (sprintf (dgettext ("domframework",
|
||||
"SSE : Backend event file not openable : %s"), $filePath),
|
||||
500);
|
||||
$this->fps[$event] = $fp;
|
||||
return $this;
|
||||
}
|
||||
/** The optional handler to use in Events. Must be array of callable methods
|
||||
* @param array $handlers The handlers method, array[event=>callable]
|
||||
* @param array|mixed $params The parameters of the handlers
|
||||
* array[event=>callable]
|
||||
* If callable is null, remove the handler for this event
|
||||
* @param array|null $params The optional needed parameters
|
||||
* array(event=>array(params))
|
||||
* if event=>null is set, remove the parameters for the event
|
||||
*/
|
||||
final public function setHandlersEvent($handlers, $params = null)
|
||||
{
|
||||
if (! is_array($handlers)) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : can not use handler : not array"
|
||||
), 500);
|
||||
}
|
||||
if (! is_array($params) && $params !== null) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : can not use handler params : not array"
|
||||
), 500);
|
||||
}
|
||||
foreach ($handlers as $event => $handler) {
|
||||
if ($handler === null && key_exists($event, $this->handlers)) {
|
||||
unset($this->handlers[$event]);
|
||||
unset($this->handlersParams[$event]);
|
||||
continue;
|
||||
}
|
||||
if (! is_callable($handler)) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : can not use handler : not callable"
|
||||
), 500);
|
||||
}
|
||||
$this->handlers[$event] = $handler;
|
||||
}
|
||||
if (is_array($params)) {
|
||||
foreach ($params as $event => $param) {
|
||||
if (! key_exists($event, $this->handlers)) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : can not use parameter '$event' : no associated handler"
|
||||
), 500);
|
||||
}
|
||||
if ($param === null) {
|
||||
unset($this->handlersParams[$event]);
|
||||
} else {
|
||||
$this->handlersParams[$event] = $param;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//// PUBLIC METHODS ////
|
||||
//////////////////////////////
|
||||
/** This method is called by the user's browser.
|
||||
* It send the "ping" each X second and send the backend content if it
|
||||
* is updated.
|
||||
* Never return !
|
||||
*/
|
||||
final public function loop()
|
||||
{
|
||||
if ($this->files === null) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not use the backend file : file is not defined"
|
||||
), 500);
|
||||
}
|
||||
$this->backendInit();
|
||||
// The last ping in float
|
||||
$lastping = 0;
|
||||
@header("Cache-Control: no-cache"); // Disable caching of response
|
||||
@header("X-Accel-Buffering: no"); // Disable NGINX Buffering
|
||||
@header("Content-Type: text/event-stream");
|
||||
// In tests, abort after 100ms * 50 = 5s. In production, never abort as the
|
||||
// counter is never decreased
|
||||
$testloop = 50;
|
||||
while ($testloop) {
|
||||
// Each $pingTime, send a comment ":ping" to keepalive the connection
|
||||
if ($this->pingTime > 0 && $lastping + $this->pingTime < microtime(true)) {
|
||||
echo ": ping\n\n";
|
||||
$lastping = microtime(true);
|
||||
}
|
||||
|
||||
// Try to get data from backends
|
||||
foreach ($this->files as $event => $filePath) {
|
||||
$lines = $this->backendGet($event);
|
||||
if ($lines === false) {
|
||||
continue;
|
||||
}
|
||||
$lines = rtrim($lines);
|
||||
// As data may contains multiple lines, split them
|
||||
foreach (explode("\n", $lines) as $line) {
|
||||
if (
|
||||
key_exists($event, $this->handlers) &&
|
||||
is_callable($this->handlers[$event])
|
||||
) {
|
||||
$array = array($line);
|
||||
if (key_exists($event, $this->handlersParams)) {
|
||||
$array = array_merge($array, $this->handlersParams[$event]);
|
||||
}
|
||||
$line = call_user_func_array($this->handlers[$event], $array);
|
||||
}
|
||||
if ($line === false) {
|
||||
continue;
|
||||
}
|
||||
// If the event is data, do not display "event:data", but data:
|
||||
// immediately
|
||||
if ($event !== "data") {
|
||||
echo "event: $event\n";
|
||||
$event = "data";
|
||||
}
|
||||
echo "$event: " . implode("\n$event: ", explode("\n", rtrim($line))) .
|
||||
"\n\n";
|
||||
}
|
||||
}
|
||||
if (defined("PHPUNIT")) {
|
||||
$testloop--;
|
||||
pcntl_signal_dispatch();
|
||||
} else {
|
||||
@ob_end_flush();
|
||||
@flush();
|
||||
}
|
||||
// Loop each 100ms if there is no data.
|
||||
usleep(100000);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
//// PRIVATE METHODS ////
|
||||
///////////////////////////////
|
||||
/** Initialize the backend : open all the files and go at the end
|
||||
*/
|
||||
private function backendInit()
|
||||
{
|
||||
if ($this->files === null) {
|
||||
throw new \Exception(dgettext(
|
||||
"domframework",
|
||||
"SSE : Can not use the backend files : files are not defined"
|
||||
), 500);
|
||||
}
|
||||
foreach ($this->files as $event => $filePath) {
|
||||
$this->backendInitEvent($event, $filePath);
|
||||
if (key_exists($event, $this->fps)) {
|
||||
fseek($this->fps[$event], 0, SEEK_END);
|
||||
stream_set_blocking($this->fps[$event], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the pending lines from requested backend
|
||||
* @param string $event The event to get data
|
||||
* @return null if there is no new line pending
|
||||
* @return string the data lines stored in the lines
|
||||
*/
|
||||
private function backendGet($event)
|
||||
{
|
||||
// If the file was previously opened but doesn't exists anymore. Close it
|
||||
// and reopen it just after
|
||||
if (
|
||||
key_exists($event, $this->fps) && (
|
||||
! file_exists($this->files[$event]) ||
|
||||
! is_readable($this->files[$event])
|
||||
)
|
||||
) {
|
||||
@fclose($this->fps[$event]);
|
||||
unset($this->fps[$event]);
|
||||
}
|
||||
// If the file was not previously opened, try to open it. Abort if it
|
||||
// doesn't exists again
|
||||
if (! key_exists($event, $this->fps)) {
|
||||
if ($this->backendInitEvent($event, $this->files[$event]) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$fp = $this->fps[$event];
|
||||
// If the file is truncated by an external action, must restart at 0
|
||||
if (fstat($fp)["size"] < ftell($fp)) {
|
||||
rewind($fp);
|
||||
}
|
||||
$res = false;
|
||||
while (($line = stream_get_line($fp, 10000, "\n")) !== false) {
|
||||
$res .= "$line\n";
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/** Initialize the backend for one specific file.
|
||||
* @param string $event The event to start
|
||||
* @param string $filePath The file path where the event data are stored
|
||||
* @return boolean false if the file doesn't exists
|
||||
*/
|
||||
private function backendInitEvent($event, $filePath)
|
||||
{
|
||||
clearstatcache(true, $filePath);
|
||||
if (! file_exists(dirname($filePath))) {
|
||||
throw new \Exception(
|
||||
sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Backend directory no more exists : %s"
|
||||
), dirname($filePath)),
|
||||
500
|
||||
);
|
||||
}
|
||||
if (! is_readable(dirname($filePath))) {
|
||||
throw new \Exception(
|
||||
sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Backend directory no more readable : %s"
|
||||
), dirname($filePath)),
|
||||
500
|
||||
);
|
||||
}
|
||||
if (! file_exists($filePath)) {
|
||||
return false;
|
||||
}
|
||||
if (! is_readable($filePath)) {
|
||||
throw new \Exception(
|
||||
sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Backend event file not readable : %s"
|
||||
), $filePath),
|
||||
500
|
||||
);
|
||||
}
|
||||
$fp = fopen($filePath, "r");
|
||||
if ($fp === false) {
|
||||
throw new \Exception(
|
||||
sprintf(dgettext(
|
||||
"domframework",
|
||||
"SSE : Backend event file not openable : %s"
|
||||
), $filePath),
|
||||
500
|
||||
);
|
||||
}
|
||||
$this->fps[$event] = $fp;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user