У меня есть сервер с методом SendToAll ($ message). Мне нужен отдельный поток, который будет запускать этот метод (SendToAll) каждую секунду. Я использую pthreads.
class Sender extends Thread
{
public function __construct($server)
{
$this->server = $server;
}
public function run()
{
for (;;) {
$this->server->SendForAll("Hello");
sleep(1);
}
}
}
}
Я создал объект моего сервера и попытался поместить его в конструкцию моего Отправителя.
<?php
require_once('includes.php');
runServer();
function runServer()
{
$server = new WssService("0.0.0.0", 9001);
$asyncOp = new Sender($server);
try {
$asyncOp->start();
$server->StartServer();
}catch (Exception $e) {
echo $e->getMessage();
runServer();
}
}
Но у меня ошибка
Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Clos
ure' is not allowed' in D:\xampp\htdocs\admin_p\classes\AsyncStorage.php:6
Stack trace:
#0 D:\xampp\htdocs\admin_p\classes\AsyncStorage.php(6): Sender::__construct()
#1 D:\xampp\htdocs\admin_p\ss.php(9): Sender->__construct(Object(WssService))
#2 D:\xampp\htdocs\admin_p\ss.php(4): runServer()
#3 {main}
thrown in D:\xampp\htdocs\admin_p\classes\AsyncStorage.php on line 6
Постскриптум Извините, английский — это не мой родной язык.
class WssService
{
public $connects_storage = array();
private $server = null;
private $logger = null;
private $writer = null;
public $loop = null;
public function __construct($URI, $port)
{
$this->connected_users = array();
$this->loop = \React\EventLoop\Factory::create();
$this->Logger = new \Zend\Log\Logger();
$this->writer = new Zend\Log\Writer\Stream("php://output");
$this->Logger->addWriter($this->writer);
$this->server = new WebSocketServer("tcp://" . $URI . ":" . $port, $this->loop, $this->Logger);
$this->server->on("connect", function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Connected " . $user->getIp()));
array_push($this->connects_storage,$user);
});
$this->server->on("disconnect", function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Disconnected " . $user->getIp()));
});
$this->server->on("message", function (WebSocketTransportInterface $s_user, WebSocketMessageInterface $message) {
$s_user->sendString($message);
});
}
public function StartServer(){
$this->server->bind();
$this->loop->run();
}
public function SendForAll($message){
foreach($this->connects_storage as $client){
$client->sendString($message);
}
}
}
Обновлен метод SendForAll ()
public function SendForAll($message){
echo var_export($message,true) .PHP_EOL;
echo var_dump($this->connects_storage).PHP_EOL;
if (count($this->connects_storage) > 0) {
foreach ($this->connects_storage as $client) {
$client->sendString($message);
}
} else echo "There are empty storage";
}
Ваш WssService
объект не сериализуем. Чтобы сделать это так, сделайте $URL
а также $port
переменные-члены, переместите инициализацию в init()
метод и реализовать __sleep()
а также __wakeup()
методы:
class WssService
{
protected $URI, $port;
public $connects_storage = array();
private $server = null;
private $logger = null;
private $writer = null;
public $loop = null;
public function __construct($URI, $port)
{
$this->URI = $URI;
$this->port = $port;
$this->init();
}
protected function init() {
$this->connected_users = array();
$this->loop = \React\EventLoop\Factory::create();
$this->Logger = new \Zend\Log\Logger();
$this->writer = new Zend\Log\Writer\Stream("php://output");
$this->Logger->addWriter($this->writer);
$this->server = new WebSocketServer("tcp://" . $this->URI . ":" . $this->port, $this->loop, $this->Logger);
$this->server->on("connect", function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Connected " . $user->getIp()));
array_push($this->connects_storage,$user);
});
$this->server->on("disconnect", function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Disconnected " . $user->getIp()));
});
$this->server->on("message", function (WebSocketTransportInterface $s_user, WebSocketMessageInterface $message) {
$s_user->sendString($message);
});
}
public function StartServer(){
$this->server->bind();
$this->loop->run();
}
public function SendForAll($message){
foreach($this->connects_storage as $client){
$client->sendString($message)
}
}
public function __sleep() {
return array('URI', 'port');
}
public function __wakeup() {
$this->init();
}
}
__sleep()
говорит сериализатору только сериализоваться $URI
а также $port
переменные, это все, что вам нужно вызвать init()
в __wakeup()
во время десериализации.
$this->server->on("connect", function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Connected " . $user->getIp()));
array_push($this->connects_storage,$user);
});
$this->server->on("disconnect", function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Disconnected " . $user->getIp()));
});
$this->server->on("message", function (WebSocketTransportInterface $s_user, WebSocketMessageInterface $message) {
$s_user->sendString($message);
});
Анонимные функции / замыкания не могут быть сериализованы. Вам нужно будет найти способ хранить их по-разному.
Редактировать: у меня ушло некоторое время, чтобы протестировать этот код (оказывается, вы не можете использовать pthreads с последней версией PHP). В любом случае я смог решить вашу проблему, выполнив следующие действия:
Замените код выше на:
$this->server->on("connect", new Jeremeamia\SuperClosure\SerializableClosure(function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Connected " . $user->getIp()));
array_push($this->connects_storage,$user);
}));
$this->server->on("disconnect", new Jeremeamia\SuperClosure\SerializableClosure(function (WebSocketTransportInterface $user) {
$this->Logger->notice((" Disconnected " . $user->getIp()));
}));
$this->server->on("message", new Jeremeamia\SuperClosure\SerializableClosure(function (WebSocketTransportInterface $s_user, WebSocketMessageInterface $message) {
$s_user->sendString($message);
}));
После этого вам нужно будет включить правильное библиотека и, возможно, включить правильные файлы (я так или иначе не могу заставить композитора правильно загружать вещи)