Skip to content

Commit

Permalink
[11.x] Adds anonymous broadcasting (#51082)
Browse files Browse the repository at this point in the history
* 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
3 people authored Apr 16, 2024
1 parent eb930f5 commit 637bd31
Show file tree
Hide file tree
Showing 3 changed files with 326 additions and 0 deletions.
152 changes: 152 additions & 0 deletions src/Illuminate/Broadcasting/AnonymousEvent.php
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;
}
}
24 changes: 24 additions & 0 deletions src/Illuminate/Broadcasting/BroadcastManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,30 @@ public function socket($request = null)
return $request->header('X-Socket-ID');
}

/**
* Begin sending an anonymous broadcast to the given channels.
*/
public function on(Channel|string|array $channels): AnonymousEvent
{
return new AnonymousEvent($channels);
}

/**
* Begin sending an anonymous broadcast to the given private channels.
*/
public function private(string $channel): AnonymousEvent
{
return $this->on(new PrivateChannel($channel));
}

/**
* Begin sending an anonymous broadcast to the given presence channels.
*/
public function presence(string $channel): AnonymousEvent
{
return $this->on(new PresenceChannel($channel));
}

/**
* Begin broadcasting an event.
*
Expand Down
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';
});
}
}

0 comments on commit 637bd31

Please sign in to comment.