Initial commit
This commit is contained in:
59
src/Broker/Message.php
Normal file
59
src/Broker/Message.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Mercureact\Broker;
|
||||
|
||||
use SplObjectStorage;
|
||||
|
||||
class Message
|
||||
{
|
||||
/** @var array<int,string> The topic, if more than one the first is canonical, but all receive delivery */
|
||||
public readonly array $topic;
|
||||
/** @var string|null The SSE event type */
|
||||
public readonly ?string $type;
|
||||
/** @var string|null Message data */
|
||||
public readonly ?string $data;
|
||||
/** @var bool|null Private update*/
|
||||
public readonly ?bool $private;
|
||||
/** @var string|null Message ID */
|
||||
public readonly ?string $id;
|
||||
/** @var int SSE retry interval */
|
||||
public readonly ?int $retry;
|
||||
|
||||
private readonly int $created;
|
||||
|
||||
public function __construct(
|
||||
array $topic,
|
||||
?string $type,
|
||||
?string $data,
|
||||
?bool $private,
|
||||
?string $id,
|
||||
?int $retry
|
||||
)
|
||||
{
|
||||
$this->topic = $topic;
|
||||
$this->type = $type;
|
||||
$this->data = $data;
|
||||
$this->private = $private;
|
||||
$this->id = $id;
|
||||
$this->retry = $retry;
|
||||
$this->created = time();
|
||||
}
|
||||
|
||||
public function getAge(): int
|
||||
{
|
||||
return time() - $this->created;
|
||||
}
|
||||
|
||||
public static function fromData(array $data): Message
|
||||
{
|
||||
return new Message(
|
||||
topic: (array)$data['topic'],
|
||||
type: $data['type']??null,
|
||||
data: $data['data']??null,
|
||||
private: match ($data['private']??null) { "on" => true, null => null, default => false },
|
||||
id: $data['id']??null,
|
||||
retry: $data['retry']??null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
20
src/Broker/SubscriptionList.php
Normal file
20
src/Broker/SubscriptionList.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Mercureact\Broker;
|
||||
|
||||
use Countable;
|
||||
use SplObjectStorage;
|
||||
|
||||
class SubscriptionList implements Countable
|
||||
{
|
||||
|
||||
private array $subscriptions = [];
|
||||
|
||||
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->subscriptions);
|
||||
}
|
||||
}
|
||||
|
67
src/Broker/Topic.php
Normal file
67
src/Broker/Topic.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Mercureact\Broker;
|
||||
|
||||
use SplObjectStorage;
|
||||
|
||||
class Topic
|
||||
{
|
||||
const MAX_HISTORY_AGE = 180;
|
||||
|
||||
/** @var string Topic name */
|
||||
private string $topic;
|
||||
/** @var array<string,Message> */
|
||||
private array $messages = [];
|
||||
/** @var int Creation unixtime */
|
||||
private int $created;
|
||||
|
||||
private SubscriptionList $subscribers;
|
||||
|
||||
public function __construct(string $topic)
|
||||
{
|
||||
$this->topic = $topic;
|
||||
$this->subscribers = new SubscriptionList();
|
||||
$this->created = time();
|
||||
}
|
||||
|
||||
public function publish(Message $message)
|
||||
{
|
||||
// TODO check if message id has already been published
|
||||
|
||||
foreach ($this->subscribers as $subscriber) {
|
||||
if ($message->private === true) {
|
||||
// TODO check subscriber access
|
||||
} else {
|
||||
// TODO deliver to subscriber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAge(): int
|
||||
{
|
||||
return time() - $this->created;
|
||||
}
|
||||
|
||||
public function getHistorySize(): int
|
||||
{
|
||||
return count($this->messages);
|
||||
}
|
||||
|
||||
public function getSubscriberCount(): int
|
||||
{
|
||||
return count($this->subscribers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Garbage collect histry
|
||||
*
|
||||
*/
|
||||
public function garbageCollect(): void
|
||||
{
|
||||
$this->messages = array_filter(
|
||||
$this->messages,
|
||||
fn($message) => $message->getAge() < self::MAX_HISTORY_AGE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
50
src/Broker/TopicManager.php
Normal file
50
src/Broker/TopicManager.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace NoccyLabs\Mercureact\Broker;
|
||||
|
||||
use SplObjectStorage;
|
||||
|
||||
class TopicManager
|
||||
{
|
||||
/** @var array<string,Topic> */
|
||||
private array $topics = [];
|
||||
|
||||
|
||||
public function getTopic(string $topic): Topic
|
||||
{
|
||||
if (!isset($this->topics[$topic])) {
|
||||
$this->topics[$topic] = new Topic($topic);
|
||||
}
|
||||
return $this->topics[$topic];
|
||||
}
|
||||
|
||||
public function publish(Message $message): void
|
||||
{
|
||||
foreach ($message->topic as $topic) {
|
||||
$this->getTopic($topic)->publish($message);
|
||||
}
|
||||
}
|
||||
|
||||
public function getTopicCount(): int
|
||||
{
|
||||
return count($this->topics);
|
||||
}
|
||||
|
||||
public function getSubscriberCount(): int
|
||||
{
|
||||
return array_sum(array_map(fn($t) => $t->getSubscriberCount(), $this->topics));
|
||||
}
|
||||
|
||||
public function garbageCollect(): void
|
||||
{
|
||||
$this->topics = array_filter(
|
||||
$this->topics,
|
||||
function (Topic $topic) {
|
||||
$topic->garbageCollect();
|
||||
return ($topic->getHistorySize() > 0 && $topic->getSubscriberCount() > 0) || ($topic->getAge() < 60);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user