Skip to content

Commit 637bd31

Browse files
joedixontaylorotwellStyleCIBot
authored
[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]>
1 parent eb930f5 commit 637bd31

File tree

3 files changed

+326
-0
lines changed

3 files changed

+326
-0
lines changed
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?php
2+
3+
namespace Illuminate\Broadcasting;
4+
5+
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
6+
use Illuminate\Contracts\Support\Arrayable;
7+
use Illuminate\Foundation\Events\Dispatchable;
8+
use Illuminate\Support\Arr;
9+
10+
class AnonymousEvent implements ShouldBroadcast
11+
{
12+
use Dispatchable, InteractsWithBroadcasting, InteractsWithSockets;
13+
14+
/**
15+
* The connection the event should be broadcast on.
16+
*/
17+
protected ?string $connection = null;
18+
19+
/**
20+
* The name the event should be broadcast as.
21+
*/
22+
protected ?string $name = null;
23+
24+
/**
25+
* The payload the event should be broadcast with.
26+
*/
27+
protected array $payload = [];
28+
29+
/**
30+
* Should the broadcast include the current user.
31+
*/
32+
protected bool $includeCurrentUser = true;
33+
34+
/**
35+
* Indicates if the event should be broadcast synchronously.
36+
*/
37+
protected bool $shouldBroadcastNow = false;
38+
39+
/**
40+
* Create a new anonymous broadcastable event instance.
41+
*
42+
* @return void
43+
*/
44+
public function __construct(protected Channel|array|string $channels)
45+
{
46+
$this->channels = Arr::wrap($channels);
47+
}
48+
49+
/**
50+
* Set the connection the event should be broadcast on.
51+
*/
52+
public function via(string $connection): static
53+
{
54+
$this->connection = $connection;
55+
56+
return $this;
57+
}
58+
59+
/**
60+
* Set the name the event should be broadcast as.
61+
*/
62+
public function as(string $name): static
63+
{
64+
$this->name = $name;
65+
66+
return $this;
67+
}
68+
69+
/**
70+
* Set the payload the event should be broadcast with.
71+
*/
72+
public function with(Arrayable|array $payload): static
73+
{
74+
$this->payload = $payload instanceof Arrayable
75+
? $payload->toArray()
76+
: collect($payload)->map(
77+
fn ($p) => $p instanceof Arrayable ? $p->toArray() : $p
78+
)->all();
79+
80+
return $this;
81+
}
82+
83+
/**
84+
* Broadcast the event to everyone except the current user.
85+
*/
86+
public function toOthers(): static
87+
{
88+
$this->includeCurrentUser = false;
89+
90+
return $this;
91+
}
92+
93+
/**
94+
* Broadcast the event.
95+
*/
96+
public function sendNow(): void
97+
{
98+
$this->shouldBroadcastNow = true;
99+
100+
$this->send();
101+
}
102+
103+
/**
104+
* Broadcast the event.
105+
*/
106+
public function send(): void
107+
{
108+
$broadcast = broadcast($this)->via($this->connection);
109+
110+
if (! $this->includeCurrentUser) {
111+
$broadcast->toOthers();
112+
}
113+
}
114+
115+
/**
116+
* Get the name the event should broadcast as.
117+
*/
118+
public function broadcastAs(): string
119+
{
120+
return $this->name ?: class_basename($this);
121+
122+
return $this;
123+
}
124+
125+
/**
126+
* Get the payload the event should broadcast with.
127+
*
128+
* @return array<string, mixed>
129+
*/
130+
public function broadcastWith(): array
131+
{
132+
return $this->payload;
133+
}
134+
135+
/**
136+
* Get the channels the event should broadcast on.
137+
*
138+
* @return \Illuminate\Broadcasting\Channel|\Illuminate\Broadcasting\Channel[]|string[]|string
139+
*/
140+
public function broadcastOn(): Channel|array
141+
{
142+
return $this->channels;
143+
}
144+
145+
/**
146+
* Determine if the event should be broadcast synchronously.
147+
*/
148+
public function shouldBroadcastNow(): bool
149+
{
150+
return $this->shouldBroadcastNow;
151+
}
152+
}

src/Illuminate/Broadcasting/BroadcastManager.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,30 @@ public function socket($request = null)
132132
return $request->header('X-Socket-ID');
133133
}
134134

135+
/**
136+
* Begin sending an anonymous broadcast to the given channels.
137+
*/
138+
public function on(Channel|string|array $channels): AnonymousEvent
139+
{
140+
return new AnonymousEvent($channels);
141+
}
142+
143+
/**
144+
* Begin sending an anonymous broadcast to the given private channels.
145+
*/
146+
public function private(string $channel): AnonymousEvent
147+
{
148+
return $this->on(new PrivateChannel($channel));
149+
}
150+
151+
/**
152+
* Begin sending an anonymous broadcast to the given presence channels.
153+
*/
154+
public function presence(string $channel): AnonymousEvent
155+
{
156+
return $this->on(new PresenceChannel($channel));
157+
}
158+
135159
/**
136160
* Begin broadcasting an event.
137161
*
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Integration\Broadcasting;
4+
5+
use Illuminate\Broadcasting\AnonymousEvent;
6+
use Illuminate\Broadcasting\PresenceChannel;
7+
use Illuminate\Broadcasting\PrivateChannel;
8+
use Illuminate\Support\Facades\Broadcast as BroadcastFacade;
9+
use Illuminate\Support\Facades\Event as EventFacade;
10+
use Orchestra\Testbench\TestCase;
11+
use ReflectionClass;
12+
13+
class SendingBroadcastsViaAnonymousEventTest extends TestCase
14+
{
15+
public function testBroadcastIsSent()
16+
{
17+
EventFacade::fake();
18+
19+
BroadcastFacade::on('test-channel')
20+
->with(['some' => 'data'])
21+
->as('test-event')
22+
->send();
23+
24+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
25+
return (new ReflectionClass($event))->getProperty('connection')->getValue($event) === null &&
26+
$event->broadcastOn() === ['test-channel'] &&
27+
$event->broadcastAs() === 'test-event' &&
28+
$event->broadcastWith() === ['some' => 'data'];
29+
});
30+
}
31+
32+
public function testBroadcastIsSentNow()
33+
{
34+
EventFacade::fake();
35+
36+
BroadcastFacade::on('test-channel')
37+
->with(['some' => 'data'])
38+
->as('test-event')
39+
->sendNow();
40+
41+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
42+
return (new ReflectionClass($event))->getProperty('connection')->getValue($event) === null &&
43+
$event->shouldBroadcastNow();
44+
});
45+
}
46+
47+
public function testDefaultNameIsSet()
48+
{
49+
EventFacade::fake();
50+
51+
BroadcastFacade::on('test-channel')
52+
->with(['some' => 'data'])
53+
->send();
54+
55+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
56+
return $event->broadcastAs() === 'AnonymousEvent';
57+
});
58+
}
59+
60+
public function testDefaultPayloadIsSet()
61+
{
62+
EventFacade::fake();
63+
64+
BroadcastFacade::on('test-channel')->send();
65+
66+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
67+
return $event->broadcastWith() === [];
68+
});
69+
}
70+
71+
public function testSendToMultipleChannels()
72+
{
73+
EventFacade::fake();
74+
75+
BroadcastFacade::on([
76+
'test-channel',
77+
new PrivateChannel('test-channel'),
78+
'presence-test-channel',
79+
])->send();
80+
81+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
82+
[$one, $two, $three] = $event->broadcastOn();
83+
84+
return $one === 'test-channel' &&
85+
$two instanceof PrivateChannel &&
86+
$two->name === 'private-test-channel' &&
87+
$three === 'presence-test-channel';
88+
});
89+
}
90+
91+
public function testSendViaANonDefaultConnection()
92+
{
93+
EventFacade::fake();
94+
95+
BroadcastFacade::on('test-channel')
96+
->via('pusher')
97+
->send();
98+
99+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
100+
return (new ReflectionClass($event))->getProperty('connection')->getValue($event) === 'pusher';
101+
});
102+
}
103+
104+
public function testSendToOthersOnly()
105+
{
106+
EventFacade::fake();
107+
108+
$this->app['request']->headers->add(['X-Socket-ID' => '12345']);
109+
110+
BroadcastFacade::on('test-channel')->send();
111+
112+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
113+
return $event->socket === null;
114+
});
115+
116+
BroadcastFacade::on('test-channel')
117+
->toOthers()
118+
->send();
119+
120+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
121+
return $event->socket = '12345';
122+
});
123+
}
124+
125+
public function testSendToPrivateChannel()
126+
{
127+
EventFacade::fake();
128+
129+
BroadcastFacade::private('test-channel')->send();
130+
131+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
132+
$channel = $event->broadcastOn()[0];
133+
134+
return $channel instanceof PrivateChannel && $channel->name === 'private-test-channel';
135+
});
136+
}
137+
138+
public function testSendToPresenceChannel()
139+
{
140+
EventFacade::fake();
141+
142+
BroadcastFacade::presence('test-channel')->send();
143+
144+
EventFacade::assertDispatched(AnonymousEvent::class, function ($event) {
145+
$channel = $event->broadcastOn()[0];
146+
147+
return $channel instanceof PresenceChannel && $channel->name === 'presence-test-channel';
148+
});
149+
}
150+
}

0 commit comments

Comments
 (0)