-
Notifications
You must be signed in to change notification settings - Fork 11.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[11.x] Adds anonymous broadcasting (#51082)
* support anonymous broadcasts * test anonymous broadcastables * allow broadcasting to others * formatting * convenience methods * formatting * Apply fixes from StyleCI * remove improt * broadcast now support --------- Co-authored-by: Taylor Otwell <[email protected]> Co-authored-by: StyleCI Bot <[email protected]>
- Loading branch information
1 parent
eb930f5
commit 637bd31
Showing
3 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
<?php | ||
|
||
namespace Illuminate\Broadcasting; | ||
|
||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; | ||
use Illuminate\Contracts\Support\Arrayable; | ||
use Illuminate\Foundation\Events\Dispatchable; | ||
use Illuminate\Support\Arr; | ||
|
||
class AnonymousEvent implements ShouldBroadcast | ||
{ | ||
use Dispatchable, InteractsWithBroadcasting, InteractsWithSockets; | ||
|
||
/** | ||
* The connection the event should be broadcast on. | ||
*/ | ||
protected ?string $connection = null; | ||
|
||
/** | ||
* The name the event should be broadcast as. | ||
*/ | ||
protected ?string $name = null; | ||
|
||
/** | ||
* The payload the event should be broadcast with. | ||
*/ | ||
protected array $payload = []; | ||
|
||
/** | ||
* Should the broadcast include the current user. | ||
*/ | ||
protected bool $includeCurrentUser = true; | ||
|
||
/** | ||
* Indicates if the event should be broadcast synchronously. | ||
*/ | ||
protected bool $shouldBroadcastNow = false; | ||
|
||
/** | ||
* Create a new anonymous broadcastable event instance. | ||
* | ||
* @return void | ||
*/ | ||
public function __construct(protected Channel|array|string $channels) | ||
{ | ||
$this->channels = Arr::wrap($channels); | ||
} | ||
|
||
/** | ||
* Set the connection the event should be broadcast on. | ||
*/ | ||
public function via(string $connection): static | ||
{ | ||
$this->connection = $connection; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Set the name the event should be broadcast as. | ||
*/ | ||
public function as(string $name): static | ||
{ | ||
$this->name = $name; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Set the payload the event should be broadcast with. | ||
*/ | ||
public function with(Arrayable|array $payload): static | ||
{ | ||
$this->payload = $payload instanceof Arrayable | ||
? $payload->toArray() | ||
: collect($payload)->map( | ||
fn ($p) => $p instanceof Arrayable ? $p->toArray() : $p | ||
)->all(); | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Broadcast the event to everyone except the current user. | ||
*/ | ||
public function toOthers(): static | ||
{ | ||
$this->includeCurrentUser = false; | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Broadcast the event. | ||
*/ | ||
public function sendNow(): void | ||
{ | ||
$this->shouldBroadcastNow = true; | ||
|
||
$this->send(); | ||
} | ||
|
||
/** | ||
* Broadcast the event. | ||
*/ | ||
public function send(): void | ||
{ | ||
$broadcast = broadcast($this)->via($this->connection); | ||
|
||
if (! $this->includeCurrentUser) { | ||
$broadcast->toOthers(); | ||
} | ||
} | ||
|
||
/** | ||
* Get the name the event should broadcast as. | ||
*/ | ||
public function broadcastAs(): string | ||
{ | ||
return $this->name ?: class_basename($this); | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Get the payload the event should broadcast with. | ||
* | ||
* @return array<string, mixed> | ||
*/ | ||
public function broadcastWith(): array | ||
{ | ||
return $this->payload; | ||
} | ||
|
||
/** | ||
* Get the channels the event should broadcast on. | ||
* | ||
* @return \Illuminate\Broadcasting\Channel|\Illuminate\Broadcasting\Channel[]|string[]|string | ||
*/ | ||
public function broadcastOn(): Channel|array | ||
{ | ||
return $this->channels; | ||
} | ||
|
||
/** | ||
* Determine if the event should be broadcast synchronously. | ||
*/ | ||
public function shouldBroadcastNow(): bool | ||
{ | ||
return $this->shouldBroadcastNow; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 150 additions & 0 deletions
150
tests/Integration/Broadcasting/SendingBroadcastsViaAnonymousEventTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
<?php | ||
|
||
namespace Illuminate\Tests\Integration\Broadcasting; | ||
|
||
use Illuminate\Broadcasting\AnonymousEvent; | ||
use Illuminate\Broadcasting\PresenceChannel; | ||
use Illuminate\Broadcasting\PrivateChannel; | ||
use Illuminate\Support\Facades\Broadcast as BroadcastFacade; | ||
use Illuminate\Support\Facades\Event as EventFacade; | ||
use Orchestra\Testbench\TestCase; | ||
use ReflectionClass; | ||
|
||
class SendingBroadcastsViaAnonymousEventTest extends TestCase | ||
{ | ||
public function testBroadcastIsSent() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::on('test-channel') | ||
->with(['some' => 'data']) | ||
->as('test-event') | ||
->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return (new ReflectionClass($event))->getProperty('connection')->getValue($event) === null && | ||
$event->broadcastOn() === ['test-channel'] && | ||
$event->broadcastAs() === 'test-event' && | ||
$event->broadcastWith() === ['some' => 'data']; | ||
}); | ||
} | ||
|
||
public function testBroadcastIsSentNow() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::on('test-channel') | ||
->with(['some' => 'data']) | ||
->as('test-event') | ||
->sendNow(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return (new ReflectionClass($event))->getProperty('connection')->getValue($event) === null && | ||
$event->shouldBroadcastNow(); | ||
}); | ||
} | ||
|
||
public function testDefaultNameIsSet() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::on('test-channel') | ||
->with(['some' => 'data']) | ||
->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return $event->broadcastAs() === 'AnonymousEvent'; | ||
}); | ||
} | ||
|
||
public function testDefaultPayloadIsSet() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::on('test-channel')->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return $event->broadcastWith() === []; | ||
}); | ||
} | ||
|
||
public function testSendToMultipleChannels() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::on([ | ||
'test-channel', | ||
new PrivateChannel('test-channel'), | ||
'presence-test-channel', | ||
])->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
[$one, $two, $three] = $event->broadcastOn(); | ||
|
||
return $one === 'test-channel' && | ||
$two instanceof PrivateChannel && | ||
$two->name === 'private-test-channel' && | ||
$three === 'presence-test-channel'; | ||
}); | ||
} | ||
|
||
public function testSendViaANonDefaultConnection() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::on('test-channel') | ||
->via('pusher') | ||
->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return (new ReflectionClass($event))->getProperty('connection')->getValue($event) === 'pusher'; | ||
}); | ||
} | ||
|
||
public function testSendToOthersOnly() | ||
{ | ||
EventFacade::fake(); | ||
|
||
$this->app['request']->headers->add(['X-Socket-ID' => '12345']); | ||
|
||
BroadcastFacade::on('test-channel')->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return $event->socket === null; | ||
}); | ||
|
||
BroadcastFacade::on('test-channel') | ||
->toOthers() | ||
->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
return $event->socket = '12345'; | ||
}); | ||
} | ||
|
||
public function testSendToPrivateChannel() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::private('test-channel')->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
$channel = $event->broadcastOn()[0]; | ||
|
||
return $channel instanceof PrivateChannel && $channel->name === 'private-test-channel'; | ||
}); | ||
} | ||
|
||
public function testSendToPresenceChannel() | ||
{ | ||
EventFacade::fake(); | ||
|
||
BroadcastFacade::presence('test-channel')->send(); | ||
|
||
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) { | ||
$channel = $event->broadcastOn()[0]; | ||
|
||
return $channel instanceof PresenceChannel && $channel->name === 'presence-test-channel'; | ||
}); | ||
} | ||
} |