Implemented subscription logic

This commit is contained in:
2024-03-10 23:06:00 +01:00
parent 39869d605c
commit 87d47f8ce8
11 changed files with 125 additions and 43 deletions

View File

@ -44,6 +44,16 @@ class Message
return time() - $this->created;
}
public function toString(): string
{
$msg = [];
if ($this->type) $msg[] = "event: ".$this->type;
if ($this->retry) $msg[] = "retry: ".$this->retry;
if ($this->id) $msg[] = "id: ".$this->id;
if ($this->data) $msg[] = "data: ".$this->data;
return join("\n", $msg)."\n\n";
}
public static function fromData(array $data): Message
{
return new Message(

View File

@ -0,0 +1,24 @@
<?php
namespace NoccyLabs\Mercureact\Broker;
use React\Stream\WritableStreamInterface;
class SseSubscriber implements SubscriberInterface
{
public function __construct(
private WritableStreamInterface $stream
)
{
}
public function deliver(Message $message): void
{
$this->stream->write($message->toString());
}
public function isAuthorized(string $topics): bool
{
return true;
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace NoccyLabs\Mercureact\Broker;
interface SubscriberInterface
{
public function deliver(Message $message): void;
public function isAuthorized(string $topics): bool;
}

View File

@ -1,28 +0,0 @@
<?php
namespace NoccyLabs\Mercureact\Broker;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use SplObjectStorage;
use Traversable;
class SubscriptionList implements Countable, IteratorAggregate
{
private array $subscriptions = [];
public function count(): int
{
return count($this->subscriptions);
}
public function getIterator(): Traversable
{
return new ArrayIterator($this->subscriptions);
}
}

View File

@ -10,17 +10,19 @@ class Topic
/** @var string Topic name */
private string $topic;
/** @var array<string,Message> */
private array $messages = [];
/** @var int Creation unixtime */
private int $created;
private SubscriptionList $subscribers;
private SplObjectStorage $subscribers;
public function __construct(string $topic)
{
$this->topic = $topic;
$this->subscribers = new SubscriptionList();
$this->subscribers = new SplObjectStorage();
$this->created = time();
}
@ -31,8 +33,10 @@ class Topic
foreach ($this->subscribers as $subscriber) {
if ($message->private === true) {
// TODO check subscriber access
$subscriber->deliver($message);
} else {
// TODO deliver to subscriber
$subscriber->deliver($message);
}
}
}
@ -52,6 +56,16 @@ class Topic
return count($this->subscribers);
}
public function addSubscriber(SubscriberInterface $subscriber)
{
$this->subscribers->attach($subscriber);
}
public function removeSubscriber(SubscriberInterface $subscriber)
{
$this->subscribers->detach($subscriber);
}
/**
* Garbage collect histry
*

View File

@ -9,6 +9,12 @@ class TopicManager
/** @var array<string,Topic> */
private array $topics = [];
private SplObjectStorage $subscribers;
public function __construct()
{
$this->subscribers = new SplObjectStorage();
}
public function getTopic(string $topic): Topic
{
@ -25,6 +31,26 @@ class TopicManager
}
}
public function subscribe(SubscriberInterface $subscriber, array $topics): void
{
foreach ($topics as $topic) {
$this->getTopic($topic)->addSubscriber($subscriber);
}
}
public function unsubscribe(SubscriberInterface $subscriber, ?array $topics=null): void
{
if (!$topics) {
foreach ($this->topics as $topic) {
$topic->removeSubscriber($subscriber);
}
return;
}
foreach ($topics as $topic) {
$this->getTopic($topic)->removeSubscriber($subscriber);
}
}
public function getTopicCount(): int
{
return count($this->topics);
@ -32,7 +58,7 @@ class TopicManager
public function getSubscriberCount(): int
{
return array_sum(array_map(fn($t) => $t->getSubscriberCount(), $this->topics));
return count($this->subscribers);
}
public function garbageCollect(): void