From f12459d1ce89250f9b2977e2c4825cda3d1c5723 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Wed, 10 Aug 2022 01:45:39 +0300 Subject: [PATCH 1/7] Emit a new UpdateEvent (backported from SDK v4) --- src/Events/UpdateEvent.php | 45 +++++++++++++++++++++++++++ src/Methods/Update.php | 26 ++++++++++++++++ tests/Integration/TelegramApiTest.php | 29 ++++++++++++++++- 3 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 src/Events/UpdateEvent.php diff --git a/src/Events/UpdateEvent.php b/src/Events/UpdateEvent.php new file mode 100644 index 00000000..a9ac3baf --- /dev/null +++ b/src/Events/UpdateEvent.php @@ -0,0 +1,45 @@ +telegram = $telegram; + $this->update = $update; + } + + /** + * @internal + * @deprecated Will be removed in SDK v4 + */ + public function cloneWithCustomName(string $eventName): self + { + return new class ($this->telegram, $this->update, $eventName) extends UpdateEvent { + /** @var string */ + private $eventName; + + public function __construct(Api $telegram, Update $update, string $eventName) + { + $this->eventName = $eventName; + parent::__construct($telegram, $update); + } + + /** @inheritDoc */ + public function getName(): string + { + return $this->eventName; + } + }; + } +} diff --git a/src/Methods/Update.php b/src/Methods/Update.php index 0f90cb1c..aa26b644 100644 --- a/src/Methods/Update.php +++ b/src/Methods/Update.php @@ -2,7 +2,9 @@ namespace Telegram\Bot\Methods; +use League\Event\EmitterInterface; use Psr\Http\Message\RequestInterface; +use Telegram\Bot\Events\UpdateEvent; use Telegram\Bot\Events\UpdateWasReceived; use Telegram\Bot\Exceptions\TelegramSDKException; use Telegram\Bot\FileUpload\InputFile; @@ -56,6 +58,7 @@ public function getUpdates(array $params = [], $shouldEmitEvents = true): array if ($shouldEmitEvents) { $this->emitEvent(new UpdateWasReceived($update, $this)); } + $this->dispatchUpdateEvent($update); return $update; }) @@ -165,6 +168,7 @@ public function getWebhookUpdate($shouldEmitEvent = true, ?RequestInterface $req if ($shouldEmitEvent) { $this->emitEvent(new UpdateWasReceived($update, $this)); } + $this->dispatchUpdateEvent($update); return $update; } @@ -220,4 +224,26 @@ private function getRequestBody(?RequestInterface $request) return json_decode($rawBody, true); } + + /** Dispatch Update Event. */ + private function dispatchUpdateEvent(UpdateObject $update): void + { + if (! property_exists($this, 'eventEmitter') || ! $this->eventEmitter instanceof EmitterInterface) { + return; + } + + $eventEmitter = $this->eventEmitter; + + $event = new UpdateEvent($this, $update); + + $eventEmitter->emit($event->cloneWithCustomName(UpdateEvent::NAME)); + $updateType = $update->objectType(); + if (is_string($updateType)) { + $eventEmitter->emit($event->cloneWithCustomName($updateType)); + + if (null !== $update->getMessage()->objectType()) { + $eventEmitter->emit($event->cloneWithCustomName($updateType . '.' . $update->getMessage()->objectType())); + } + } + } } diff --git a/tests/Integration/TelegramApiTest.php b/tests/Integration/TelegramApiTest.php index 4ce7db06..b923c0fa 100644 --- a/tests/Integration/TelegramApiTest.php +++ b/tests/Integration/TelegramApiTest.php @@ -9,6 +9,7 @@ use Prophecy\Argument; use Telegram\Bot\Api; use Telegram\Bot\Commands\CommandBus; +use Telegram\Bot\Events\UpdateEvent; use Telegram\Bot\Events\UpdateWasReceived; use Telegram\Bot\Exceptions\CouldNotUploadInputFile; use Telegram\Bot\Exceptions\TelegramResponseException; @@ -46,6 +47,12 @@ protected function getApi($client = null, $token = 'TELEGRAM_TOKEN', $async = fa return new Api($token, $async, $client); } + /** Create Request to emulate income Request from Telegram. */ + private function createIncomeWebhookRequestInstance(array $updateData): Request + { + return new Request('POST', 'any', [], json_encode($updateData, \JSON_THROW_ON_ERROR)); + } + /** * @test */ @@ -543,7 +550,27 @@ public function check_the_webhook_works_and_can_emmit_an_event() //We can't pass test data to the webhook because it relies on the read only stream php://input $this->assertEmpty($update); $this->assertInstanceOf(Update::class, $update); - $emitter->emit(Argument::type(UpdateWasReceived::class))->shouldHaveBeenCalled(); + $emitter->emit(Argument::type(UpdateWasReceived::class))->shouldHaveBeenCalledOnce(); + } + + /** @test */ + public function it_emits_3_events_of_update_event_type() + { + $emitter = $this->prophesize(Emitter::class); + + $api = $this->getApi(); + $api->setEventEmitter($emitter->reveal()); + + $incomeWebhookRequest = $this->createIncomeWebhookRequestInstance([ + 'message' => [ // to help SDK to detect Update of "message" type and send 2nd event (with name "message") + 'text' => 'any', // to help SDK to detect message type and send 3rd event (with name "message.text") + ], + ]); + + $update = $api->getWebhookUpdate(true, $incomeWebhookRequest); + + $this->assertInstanceOf(Update::class, $update); + $emitter->emit(Argument::type(UpdateEvent::class))->shouldHaveBeenCalledTimes(3); } /** @test */ From 81a8961011c862d4c64caf60c903e9288847b0d4 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Wed, 10 Aug 2022 01:53:11 +0300 Subject: [PATCH 2/7] Do not use typed properties to make it compatible with PHP 7.3 --- src/Events/UpdateEvent.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Events/UpdateEvent.php b/src/Events/UpdateEvent.php index a9ac3baf..f2bf968d 100644 --- a/src/Events/UpdateEvent.php +++ b/src/Events/UpdateEvent.php @@ -10,8 +10,11 @@ class UpdateEvent extends AbstractEvent { public const NAME = 'update'; - public Api $telegram; - public Update $update; + /** @var \Telegram\Bot\Api */ + public $telegram; + + /** @var \Telegram\Bot\Objects\Update */ + public $update; public function __construct(Api $telegram, Update $update) { From 0056d7d90657ba2d5d684b163093fccd5526e89f Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Wed, 10 Aug 2022 02:07:09 +0300 Subject: [PATCH 3/7] Simplify implementation: do not use anonymous class --- src/Events/UpdateEvent.php | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Events/UpdateEvent.php b/src/Events/UpdateEvent.php index f2bf968d..ff0889cf 100644 --- a/src/Events/UpdateEvent.php +++ b/src/Events/UpdateEvent.php @@ -16,33 +16,33 @@ class UpdateEvent extends AbstractEvent /** @var \Telegram\Bot\Objects\Update */ public $update; + /** + * @deprecated Will be removed in SDK v4 + * @var string|null + */ + private $eventName = null; + public function __construct(Api $telegram, Update $update) { $this->telegram = $telegram; $this->update = $update; } + /** @inheritDoc */ + public function getName() + { + return $this->eventName ?: get_class($this); + } + /** * @internal * @deprecated Will be removed in SDK v4 */ - public function cloneWithCustomName(string $eventName): self + public function cloneWithCustomName(string $eventName): UpdateEvent { - return new class ($this->telegram, $this->update, $eventName) extends UpdateEvent { - /** @var string */ - private $eventName; - - public function __construct(Api $telegram, Update $update, string $eventName) - { - $this->eventName = $eventName; - parent::__construct($telegram, $update); - } - - /** @inheritDoc */ - public function getName(): string - { - return $this->eventName; - } - }; + $event = new self($this->telegram, $this->update); + $event->eventName = $eventName; + + return $event; } } From b380542d00f6c1c2df26c5f9d4de6edba003ef6f Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Wed, 10 Aug 2022 02:08:07 +0300 Subject: [PATCH 4/7] Make UpdateEvent final (there is no reason to extend it) --- src/Events/UpdateEvent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Events/UpdateEvent.php b/src/Events/UpdateEvent.php index ff0889cf..64a77ee1 100644 --- a/src/Events/UpdateEvent.php +++ b/src/Events/UpdateEvent.php @@ -6,7 +6,7 @@ use Telegram\Bot\Api; use Telegram\Bot\Objects\Update; -class UpdateEvent extends AbstractEvent +final class UpdateEvent extends AbstractEvent { public const NAME = 'update'; From 5873cac4da37909326609b3906bcaeb3c0550bcc Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sat, 20 Aug 2022 15:46:52 +0300 Subject: [PATCH 5/7] Use exactly the same event names as used on SDKv4 --- src/Events/UpdateEvent.php | 12 ---------- src/Methods/Update.php | 6 ++--- tests/Integration/TelegramApiTest.php | 32 ++++++++++++++++++++++----- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Events/UpdateEvent.php b/src/Events/UpdateEvent.php index 64a77ee1..cfb38b68 100644 --- a/src/Events/UpdateEvent.php +++ b/src/Events/UpdateEvent.php @@ -33,16 +33,4 @@ public function getName() { return $this->eventName ?: get_class($this); } - - /** - * @internal - * @deprecated Will be removed in SDK v4 - */ - public function cloneWithCustomName(string $eventName): UpdateEvent - { - $event = new self($this->telegram, $this->update); - $event->eventName = $eventName; - - return $event; - } } diff --git a/src/Methods/Update.php b/src/Methods/Update.php index aa26b644..fe431f90 100644 --- a/src/Methods/Update.php +++ b/src/Methods/Update.php @@ -236,13 +236,13 @@ private function dispatchUpdateEvent(UpdateObject $update): void $event = new UpdateEvent($this, $update); - $eventEmitter->emit($event->cloneWithCustomName(UpdateEvent::NAME)); + $eventEmitter->emit(UpdateEvent::NAME, $event); $updateType = $update->objectType(); if (is_string($updateType)) { - $eventEmitter->emit($event->cloneWithCustomName($updateType)); + $eventEmitter->emit($update->objectType(), $event); if (null !== $update->getMessage()->objectType()) { - $eventEmitter->emit($event->cloneWithCustomName($updateType . '.' . $update->getMessage()->objectType())); + $eventEmitter->emit($update->objectType() . '.' . $update->getMessage()->objectType(), $event); } } } diff --git a/tests/Integration/TelegramApiTest.php b/tests/Integration/TelegramApiTest.php index b923c0fa..ee31fcab 100644 --- a/tests/Integration/TelegramApiTest.php +++ b/tests/Integration/TelegramApiTest.php @@ -4,7 +4,9 @@ use GuzzleHttp\Psr7\Request; use GuzzleHttp\Psr7\Stream; +use League\Event\AbstractListener; use League\Event\Emitter; +use League\Event\EventInterface; use PHPUnit\Framework\TestCase; use Prophecy\Argument; use Telegram\Bot\Api; @@ -556,10 +558,12 @@ public function check_the_webhook_works_and_can_emmit_an_event() /** @test */ public function it_emits_3_events_of_update_event_type() { - $emitter = $this->prophesize(Emitter::class); + $emitter = new Emitter(); + $listener = $this->createSpyListener(); + $emitter->addListener('*', $listener); $api = $this->getApi(); - $api->setEventEmitter($emitter->reveal()); + $api->setEventEmitter($emitter); $incomeWebhookRequest = $this->createIncomeWebhookRequestInstance([ 'message' => [ // to help SDK to detect Update of "message" type and send 2nd event (with name "message") @@ -567,10 +571,15 @@ public function it_emits_3_events_of_update_event_type() ], ]); - $update = $api->getWebhookUpdate(true, $incomeWebhookRequest); + $api->getWebhookUpdate(true, $incomeWebhookRequest); + $allEvents = $listener->events; - $this->assertInstanceOf(Update::class, $update); - $emitter->emit(Argument::type(UpdateEvent::class))->shouldHaveBeenCalledTimes(3); + $this->assertArrayHasKey(UpdateEvent::NAME, $allEvents); + $this->assertArrayHasKey('message', $allEvents); + $this->assertArrayHasKey('message.text', $allEvents); + $this->assertCount(1, $allEvents[UpdateEvent::NAME]); + $this->assertCount(1, $allEvents['message']); + $this->assertCount(1, $allEvents['message.text']); } /** @test */ @@ -648,4 +657,17 @@ private function streamFor($resource) throw new \RuntimeException('Not found "streamFor" implementation'); } + + private function createSpyListener(): \League\Event\ListenerInterface + { + return new class extends AbstractListener { + /** @var array> */ + public $events = []; + + public function handle(EventInterface $event) + { + $this->events[$event->getName()][] = func_get_args(); + } + }; + } } From da918496be87d9aacc742917f824aaa22ff8931e Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sat, 20 Aug 2022 20:23:38 +0300 Subject: [PATCH 6/7] Cleanup a way how emit class based events with custom names --- src/Events/UpdateEvent.php | 19 ++++++++++--------- src/Methods/Update.php | 11 +++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Events/UpdateEvent.php b/src/Events/UpdateEvent.php index cfb38b68..4c4046cb 100644 --- a/src/Events/UpdateEvent.php +++ b/src/Events/UpdateEvent.php @@ -10,27 +10,28 @@ final class UpdateEvent extends AbstractEvent { public const NAME = 'update'; + /** + * @deprecated Will be removed in SDK v4 + * @var string + */ + private $name; + /** @var \Telegram\Bot\Api */ public $telegram; /** @var \Telegram\Bot\Objects\Update */ public $update; - /** - * @deprecated Will be removed in SDK v4 - * @var string|null - */ - private $eventName = null; - - public function __construct(Api $telegram, Update $update) + public function __construct(Api $telegram, Update $update, string $name = self::NAME) { $this->telegram = $telegram; $this->update = $update; + $this->name = $name; } /** @inheritDoc */ - public function getName() + public function getName(): string { - return $this->eventName ?: get_class($this); + return $this->name; } } diff --git a/src/Methods/Update.php b/src/Methods/Update.php index fe431f90..e2f18b47 100644 --- a/src/Methods/Update.php +++ b/src/Methods/Update.php @@ -234,15 +234,14 @@ private function dispatchUpdateEvent(UpdateObject $update): void $eventEmitter = $this->eventEmitter; - $event = new UpdateEvent($this, $update); - - $eventEmitter->emit(UpdateEvent::NAME, $event); + $eventEmitter->emit(new UpdateEvent($this, $update)); $updateType = $update->objectType(); if (is_string($updateType)) { - $eventEmitter->emit($update->objectType(), $event); + $eventEmitter->emit(new UpdateEvent($this, $update, $updateType)); - if (null !== $update->getMessage()->objectType()) { - $eventEmitter->emit($update->objectType() . '.' . $update->getMessage()->objectType(), $event); + $messageType = $update->getMessage()->objectType(); + if (null !== $messageType) { + $eventEmitter->emit(new UpdateEvent($this, $update, "$updateType.$messageType")); } } } From aee849ce6cf12afa0a0a8594dbd2875625150d08 Mon Sep 17 00:00:00 2001 From: Alies Lapatsin Date: Sat, 20 Aug 2022 20:28:51 +0300 Subject: [PATCH 7/7] Simplify test --- tests/Integration/TelegramApiTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Integration/TelegramApiTest.php b/tests/Integration/TelegramApiTest.php index ee31fcab..c5144554 100644 --- a/tests/Integration/TelegramApiTest.php +++ b/tests/Integration/TelegramApiTest.php @@ -661,12 +661,12 @@ private function streamFor($resource) private function createSpyListener(): \League\Event\ListenerInterface { return new class extends AbstractListener { - /** @var array> */ + /** @var array> */ public $events = []; public function handle(EventInterface $event) { - $this->events[$event->getName()][] = func_get_args(); + $this->events[$event->getName()][] = $event; } }; }