Being able to broadcast data in real-time from servers to clients is a requirement for many modern web and mobile applications. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. WebSockets provide a more efficient alternative to continually polling your application's server for data changes that should be reflected in your UI.
The component available by default in the application bundle.
To enable the component, you just need to add Spiral\Broadcasting\Bootloader\BroadcastingBootloader
and
Spiral\Broadcasting\Bootloader\WebsocketsBootloader
classes to the bootloaders list, which is located in the
class of your application.
namespace App;
use Spiral\Broadcasting\Bootloader\BroadcastingBootloader;
use Spiral\Broadcasting\Bootloader\WebsocketsBootloader;
class App extends Kernel
{
protected const LOAD = [
// ...
BroadcastingBootloader::class,
WebsocketsBootloader::class,
// ...
];
}
The BroadcastingBootloader
bootloader registers required services for event broadcasting. The WebsocketsBootloader
registers Spiral\Broadcasting\Middleware\AuthorizationMiddleware
.
The Broadcasting component requires a server that will receive events from the application. By default, Broadcasting
component supports RoadRunner WebSockets
. The process of configuring the Broadcasting driver is described on the
RoadRunner Bridge documentation page.
You can create config file app/config/broadcasting.php
if you want to configure Broadcasting drivers.
use Psr\Log\LogLevel;
use Psr\Http\Message\ServerRequestInterface;
use Spiral\Broadcasting\Driver\LogBroadcast;
use Spiral\Broadcasting\Driver\NullBroadcast;
return [
/**
* -------------------------------------------------------------------------
* Default connection
* -------------------------------------------------------------------------
*
* Events will be sent by default to this connection.
*/
'default' => env('BROADCAST_CONNECTION', 'null'),
/**
* -------------------------------------------------------------------------
* Authorize settings
* -------------------------------------------------------------------------
*/
'authorize' => [
'path' => env('BROADCAST_AUTHORIZE_PATH'),
'topics' => [
'topic' => static fn (ServerRequestInterface $request): bool => $request->getHeader('SECRET')[0] == 'secret',
'user.{id}' => static fn ($id, Actor $actor): bool => $actor->getId() === $id
],
],
/**
* -------------------------------------------------------------------------
* Connections
* -------------------------------------------------------------------------
*
* List of available connections to send events.
*/
'connections' => [
'null' => [
'driver' => 'null',
],
'log' => [
'driver' => 'log',
'level' => LogLevel::INFO,
],
],
/**
* -------------------------------------------------------------------------
* Aliases
* -------------------------------------------------------------------------
*/
'aliases' => [
'log' => 'alias'
],
/**
* -------------------------------------------------------------------------
* Driver aliases
* -------------------------------------------------------------------------
*/
'driverAliases' => [
'null' => NullBroadcast::class,
'log' => LogBroadcast::class,
],
];
Using Spiral\Broadcasting\TopicRegistryInterface
, you can add a new topic
. To do this, you need to use the register
method and pass the $topic
and the $callback
function in the parameters.
namespace App\Bootloader;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Broadcasting\TopicRegistryInterface;
class AppBootloader extends Bootloader
{
public function boot(TopicRegistryInterface $registry): void
{
$registry->register('baz-topic', fn(): string => 'baz');
$registry->register('baz.{uuid}', fn(string $uuid): string => $uuid);
}
}
Topics from the configuration file are added to the TopicRegistry
automatically.
You can use Spiral\Broadcasting\BroadcastInterface
to send an event to the default connection.
namespace App;
use Spiral\Broadcasting\BroadcastInterface;
use Spiral\Serializer\SerializerInterface;
class SomeClass
{
public function __construct(
private readonly BroadcastInterface $broadcast,
private readonly SerializerInterface $serializer
) {
}
public function send(): void
{
$this->broadcast->publish(
'baz-topic',
$this->serializer->serialize(['foo' => 'bar'])
);
}
}
Using the Spiral\Broadcasting\BroadcastManagerInterface
, you can send an event to a selected connection.
namespace App;
use Spiral\Broadcasting\BroadcastManagerInterface;
use Spiral\Serializer\SerializerInterface;
class SomeClass
{
public function __construct(
private readonly BroadcastManagerInterface $broadcast,
private readonly SerializerInterface $serializer
) {
}
public function send(): void
{
$this->broadcast
->connection('log')
->publish(
'baz-topic',
$this->serializer->serialize(['foo' => 'bar'])
);
}
}
A driver that implements Spiral\Broadcasting\BroadcastInterface
can implement the Spiral\Broadcasting\GuardInterface
interface and implement the authorize
method. This method will be called by the Spiral\Broadcasting\Middleware\AuthorizationMiddleware
and used to authorize the connection.
namespace App;
use Psr\Http\Message\ServerRequestInterface;
use Spiral\Broadcasting\AuthorizationStatus;
use Spiral\Broadcasting\Driver\AbstractBroadcast;
use Spiral\Broadcasting\GuardInterface;
final class Broadcast extends AbstractBroadcast implements GuardInterface
{
public function publish(iterable|string|\Stringable $topics, iterable|string $messages): void
{
// ...
}
public function join(iterable|string|\Stringable $topics): TopicInterface
{
// ...
}
public function authorize(ServerRequestInterface $request): AuthorizationStatus
{
// ...
}
}
Note
This functionality is already implemented inRoadRunner Bridge
.