Initial commit

This commit is contained in:
2024-03-10 03:06:19 +01:00
commit df18073bbf
16 changed files with 4590 additions and 0 deletions

59
src/Broker/Message.php Normal file
View 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,
);
}
}

View 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
View 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
);
}
}

View 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);
}
);
}
}