Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require Topic for TopicPeriodicTimer::addPeriodicTimer #478

Open
wants to merge 1 commit into
base: 4.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Server/App/Dispatcher/TopicDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private function dispatch(
if ($appTopic instanceof TopicPeriodicTimerInterface) {
$appTopic->setPeriodicTimer($this->topicPeriodicTimer);

if (!$this->topicPeriodicTimer->isRegistered($appTopic) && 0 !== \count($topic)) {
if (!$this->topicPeriodicTimer->isRegistered($appTopic, $topic) && 0 !== \count($topic)) {
try {
$appTopic->registerPeriodicTimer($topic);
} catch (\Throwable $e) {
Expand Down Expand Up @@ -178,7 +178,7 @@ private function dispatch(
$appTopic->onUnSubscribe($conn, $topic, $request);

if (0 === \count($topic)) {
$this->topicPeriodicTimer->clearPeriodicTimer($appTopic);
$this->topicPeriodicTimer->clearPeriodicTimer($appTopic, $topic);
}

break;
Expand Down
60 changes: 35 additions & 25 deletions src/Topic/TopicPeriodicTimer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Gos\Bundle\WebSocketBundle\Topic;

use Ratchet\Wamp\Topic;
use React\EventLoop\LoopInterface;
use React\EventLoop\TimerInterface;

Expand All @@ -11,7 +12,7 @@
class TopicPeriodicTimer implements \IteratorAggregate
{
/**
* @var array<string, array<string, TimerInterface>>
* @var array<string, array<string, array<string, TimerInterface>>>
*/
protected array $registry = [];
protected LoopInterface $loop;
Expand All @@ -21,69 +22,78 @@ public function __construct(LoopInterface $loop)
$this->loop = $loop;
}

public function getAllPeriodicTimers(TopicInterface $topic, string $name): TimerInterface|bool
public function getPeriodicTimer(TopicInterface $appTopic, Topic $topic, string $name): TimerInterface|bool
{
if (!$this->isPeriodicTimerActive($topic, $name)) {
if (!$this->isPeriodicTimerActive($appTopic, $topic, $name)) {
return false;
}

$namespace = $this->getTopicNamespace($topic);
$namespace = $this->getTopicNamespace($appTopic);

return $this->registry[$namespace][$name];
return $this->registry[$namespace][$topic->getId()][$name];
}

/**
* @return array<string, TimerInterface>
*/
public function getPeriodicTimers(TopicInterface $topic): array
public function getPeriodicTimers(TopicInterface $appTopic, Topic $topic): array
{
$namespace = $this->getTopicNamespace($topic);
$namespace = $this->getTopicNamespace($appTopic);

return $this->registry[$namespace] ?? [];
return $this->registry[$namespace][$topic->getId()] ?? [];
}

public function addPeriodicTimer(TopicInterface $topic, string $name, int|float $timeout, callable $callback): void
public function addPeriodicTimer(TopicInterface $appTopic, Topic $topic, string $name, int|float $timeout, callable $callback): void
{
$namespace = $this->getTopicNamespace($topic);
$namespace = $this->getTopicNamespace($appTopic);

if (!isset($this->registry[$namespace])) {
$this->registry[$namespace] = [];
}

$this->registry[$namespace][$name] = $this->loop->addPeriodicTimer($timeout, $callback);
if (!isset($this->registry[$namespace][$topic->getId()])) {
$this->registry[$namespace][$topic->getId()] = [];
}

$this->registry[$namespace][$topic->getId()][$name] = $this->loop->addPeriodicTimer($timeout, $callback);
}

public function isRegistered(TopicInterface $topic): bool
public function isRegistered(TopicInterface $appTopic, Topic $topic): bool
{
$namespace = $this->getTopicNamespace($topic);
$namespace = $this->getTopicNamespace($appTopic);

return isset($this->registry[$namespace]);
return isset($this->registry[$namespace][$topic->getId()]);
}

public function isPeriodicTimerActive(TopicInterface $topic, string $name): bool
public function isPeriodicTimerActive(TopicInterface $appTopic, Topic $topic, string $name): bool
{
$namespace = $this->getTopicNamespace($topic);
$namespace = $this->getTopicNamespace($appTopic);

return isset($this->registry[$namespace][$name]);
return isset($this->registry[$namespace][$topic->getId()][$name]);
}

public function cancelPeriodicTimer(TopicInterface $topic, string $name): void
public function cancelPeriodicTimer(TopicInterface $appTopic, Topic $topic, string $name): void
{
$namespace = $this->getTopicNamespace($topic);
$namespace = $this->getTopicNamespace($appTopic);

if (!isset($this->registry[$namespace][$name])) {
if (!isset($this->registry[$namespace][$topic->getId()][$name])) {
return;
}

$timer = $this->registry[$namespace][$name];
$timer = $this->registry[$namespace][$topic->getId()][$name];
$this->loop->cancelTimer($timer);
unset($this->registry[$namespace][$name]);
unset($this->registry[$namespace][$topic->getId()][$name]);
}

public function clearPeriodicTimer(TopicInterface $topic): void
public function clearPeriodicTimer(TopicInterface $appTopic, Topic $topic): void
{
$namespace = $this->getTopicNamespace($topic);
unset($this->registry[$namespace]);
$namespace = $this->getTopicNamespace($appTopic);

foreach ($this->registry[$namespace][$topic->getId()] as $timer) {
$this->loop->cancelTimer($timer);
}

unset($this->registry[$namespace][$topic->getId()]);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/Server/App/Dispatcher/TopicDispatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ public function wasRegistered(): bool

$this->topicPeriodicTimer->expects(self::once())
->method('isRegistered')
->with($handler)
->with($handler, $topic)
->willReturn(false);

$this->dispatcher->onPublish($connection, $topic, $request, 'test', [], []);
Expand Down
56 changes: 36 additions & 20 deletions tests/Topic/TopicPeriodicTimerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Gos\Bundle\WebSocketBundle\Topic\TopicPeriodicTimer;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Ratchet\Wamp\Topic;
use React\EventLoop\LoopInterface;
use React\EventLoop\TimerInterface;

Expand Down Expand Up @@ -35,8 +36,11 @@ public function testRetrieveTheNamedPeriodicTimerWhenActive(): void
$callback = static function (): void {};
$timeout = 10;

/** @var MockObject&TopicInterface $topic */
$topic = $this->createMock(TopicInterface::class);
/** @var MockObject&TopicInterface $appTopic */
$appTopic = $this->createMock(TopicInterface::class);

/** @var MockObject&Topic $topic */
$topic = $this->createMock(Topic::class);

/** @var MockObject&TimerInterface $timer */
$timer = $this->createMock(TimerInterface::class);
Expand All @@ -46,26 +50,32 @@ public function testRetrieveTheNamedPeriodicTimerWhenActive(): void
->with($timeout, $callback)
->willReturn($timer);

$this->topicPeriodicTimer->addPeriodicTimer($topic, 'test', $timeout, $callback);
$this->topicPeriodicTimer->addPeriodicTimer($appTopic, $topic, 'test', $timeout, $callback);

self::assertSame($timer, $this->topicPeriodicTimer->getAllPeriodicTimers($topic, 'test'));
self::assertSame($timer, $this->topicPeriodicTimer->getPeriodicTimer($appTopic, $topic, 'test'));
}

public function testNoTimerIsReturnedWhenNotRegisteredAndActive(): void
{
/** @var MockObject&TopicInterface $topic */
$topic = $this->createMock(TopicInterface::class);
/** @var MockObject&TopicInterface $appTopic */
$appTopic = $this->createMock(TopicInterface::class);

/** @var MockObject&Topic $topic */
$topic = $this->createMock(Topic::class);

self::assertFalse($this->topicPeriodicTimer->getAllPeriodicTimers($topic, 'test'));
self::assertFalse($this->topicPeriodicTimer->getPeriodicTimer($appTopic, $topic, 'test'));
}

public function testRetrieveThePeriodicTimersForATopic(): void
{
$callback = static function (): void {};
$timeout = 10;

/** @var MockObject&TopicInterface $topic */
$topic = $this->createMock(TopicInterface::class);
/** @var MockObject&TopicInterface $appTopic */
$appTopic = $this->createMock(TopicInterface::class);

/** @var MockObject&Topic $topic */
$topic = $this->createMock(Topic::class);

/** @var MockObject&TimerInterface $timer */
$timer = $this->createMock(TimerInterface::class);
Expand All @@ -75,17 +85,20 @@ public function testRetrieveThePeriodicTimersForATopic(): void
->with($timeout, $callback)
->willReturn($timer);

$this->topicPeriodicTimer->addPeriodicTimer($topic, 'test', $timeout, $callback);
$this->topicPeriodicTimer->addPeriodicTimer($appTopic, $topic, 'test', $timeout, $callback);

self::assertSame(['test' => $timer], $this->topicPeriodicTimer->getPeriodicTimers($topic));
self::assertSame(['test' => $timer], $this->topicPeriodicTimer->getPeriodicTimers($appTopic, $topic));
}

public function testDetermineWhetherATopicHasBeenRegistered(): void
{
/** @var MockObject&TopicInterface $topic */
$topic = $this->createMock(TopicInterface::class);
/** @var MockObject&TopicInterface $appTopic */
$appTopic = $this->createMock(TopicInterface::class);

self::assertFalse($this->topicPeriodicTimer->isRegistered($topic));
/** @var MockObject&Topic $topic */
$topic = $this->createMock(Topic::class);

self::assertFalse($this->topicPeriodicTimer->isRegistered($appTopic, $topic));

$callback = static function (): void {};
$timeout = 10;
Expand All @@ -98,18 +111,21 @@ public function testDetermineWhetherATopicHasBeenRegistered(): void
->with($timeout, $callback)
->willReturn($timer);

$this->topicPeriodicTimer->addPeriodicTimer($topic, 'test', $timeout, $callback);
$this->topicPeriodicTimer->addPeriodicTimer($appTopic, $topic, 'test', $timeout, $callback);

self::assertTrue($this->topicPeriodicTimer->isRegistered($topic));
self::assertTrue($this->topicPeriodicTimer->isRegistered($appTopic, $topic));
}

public function testCancelTheNamedPeriodicTimerWhenActive(): void
{
$callback = static function (): void {};
$timeout = 10;

/** @var MockObject&TopicInterface $topic */
$topic = $this->createMock(TopicInterface::class);
/** @var MockObject&TopicInterface $appTopic */
$appTopic = $this->createMock(TopicInterface::class);

/** @var MockObject&Topic $topic */
$topic = $this->createMock(Topic::class);

/** @var MockObject&TimerInterface $timer */
$timer = $this->createMock(TimerInterface::class);
Expand All @@ -123,7 +139,7 @@ public function testCancelTheNamedPeriodicTimerWhenActive(): void
->method('cancelTimer')
->with($timer);

$this->topicPeriodicTimer->addPeriodicTimer($topic, 'test', $timeout, $callback);
$this->topicPeriodicTimer->cancelPeriodicTimer($topic, 'test');
$this->topicPeriodicTimer->addPeriodicTimer($appTopic, $topic, 'test', $timeout, $callback);
$this->topicPeriodicTimer->cancelPeriodicTimer($appTopic, $topic, 'test');
}
}