1
0
Fork 0
mirror of https://github.com/archtechx/tenancy.git synced 2025-12-12 15:54:03 +00:00

Add broadcasting channel prefixing bootstrapper (#12)

* Rename old broadcast bootstrapper, add new one

* Add broadcast tenancy bootstrapper + tests

* Fix code style (php-cs-fixer)

* Fix prefixing

* Work on th bootstrapper's tests (wip – problem with events)

* Fix bootstrapper

* Test that auth closures of channels work correctly

* Fix bootstrapper

* Fix code style (php-cs-fixer)

* Delete channel cloning bootstrapper

* Add bootstrapper that prefixes broadcastOn channels under the hood

* Add broadcast channel registering helpers

* Update prefixing tests (WIP)

* Fix code style (php-cs-fixer)

* Improve comment

* Fix code style (php-cs-fixer)

* Allow customization of Pusher/Ably broadcaster extension

* Fix code style (php-cs-fixer)

* Implement prefix bootstrapper logic, test channel prefixing using a closure

* Work on the prefixing bootstrapper and tests

* Fix code style (php-cs-fixer)

* Add optional $options param to broadcasting helpers

* Test broadcasting helpers

* Fix code style (php-cs-fixer)

* Broadcasting channel prefixing + testing progress

* Improve helper methods

* Fix and improve test

* Fix extending in bootstrap()

* Fix code style (php-cs-fixer)

* Add docblocks, name things more accurately

* Fix code style (php-cs-fixer)

* Delete redundant method from testing broadcaster

* Test Pusher channel prefixing (probabaly redundant?)

* Test if channels get prefixed correctly when switching tenants

* Work with the current broadcast manager instead of overriding it

* Give the original channels to the overriden broadcasters

* Fix code style (php-cs-fixer)

* Simplify channel prefix bootstrapper

* Fix code style (php-cs-fixer)

* Fix comment

* Fix test

* Delete annotation

* Delete unused classes from test

* Delete outdated test

* Move broadcasting bootstrapper test to BootstrapperTest

* Improve bootstrapper test, delete unused event

* Add annotations to the bootstrapper

* Fix code style (php-cs-fixer)

* Improve wording

* Improve comment

* Update src/Bootstrappers/BroadcastChannelPrefixBootstrapper.php

* Apply suggestions from code review

* Optionally skip prefixing of specific channels

* Add and test central channel helper, update formatChannels overrides and tests

* Fix code style (php-cs-fixer)

* minor fixes

* Improve annotation

* Use "global__" prefix instead of "central__", add comments

* Correct tests

---------

Co-authored-by: PHP CS Fixer <phpcsfixer@example.com>
Co-authored-by: Samuel Štancl <samuel.stancl@gmail.com>
This commit is contained in:
lukinovec 2023-11-06 22:09:01 +01:00 committed by GitHub
parent b503dbf33d
commit c34952f328
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 450 additions and 24 deletions

View file

@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Bootstrappers;
use Closure;
use Illuminate\Broadcasting\Broadcasters\AblyBroadcaster;
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
use Illuminate\Broadcasting\BroadcastManager;
use Illuminate\Contracts\Foundation\Application;
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
use Stancl\Tenancy\Contracts\Tenant;
/**
* Overrides broadcasters (by default, using $broadcasterManager->extend())
* so that the channel names they actually use to broadcast events get prefixed.
*
* Channels you return in the broadcastOn() methods of the events are passed to the formatChannels() method.
* Broadcasters use that method to format the names of the channels on which the event will broadcast,
* so we override it to prefix the final channel names the broadcasters use for event broadcasting.
*/
class BroadcastChannelPrefixBootstrapper implements TenancyBootstrapper
{
/**
* Closures overriding broadcasters with custom broadcasters that prefix the channel names with the tenant keys.
*
* The key is the broadcaster's name, and the value is a closure that should prefix the broadcaster's channels.
* $broadcasterOverrides['custom'] = fn () => ...; // Custom override closure
*
* For more info, see the default override methods in this class (pusher() and ably()).
*/
public static array $broadcasterOverrides = [];
protected array $originalBroadcasters = [];
public function __construct(
protected Application $app,
protected BroadcastManager $broadcastManager,
) {
}
public function bootstrap(Tenant $tenant): void
{
foreach (static::$broadcasterOverrides as $broadcaster => $broadcasterOverride) {
// Save the original broadcaster, so that we can revert to it later
$this->originalBroadcasters[$broadcaster] = $this->broadcastManager->driver($broadcaster);
// Delete the cached broadcaster, so that the manager uses the new one
$this->broadcastManager->purge($broadcaster);
$broadcasterOverride();
// Get the overriden broadcaster
$newBroadcaster = $this->broadcastManager->driver($broadcaster);
// Register the original broadcaster's channels in the new broadcaster
foreach ($this->originalBroadcasters[$broadcaster]->getChannels() as $channel => $callback) {
$newBroadcaster->channel($channel, $callback);
}
}
}
public function revert(): void
{
// Revert to the original broadcasters
foreach ($this->originalBroadcasters as $name => $broadcaster) {
// Delete the cached (overriden) broadcaster
$this->broadcastManager->purge($name);
// Make manager return the original broadcaster instance
// Whenever the broadcaster is requested
$this->broadcastManager->extend($name, fn ($app, $config) => $broadcaster);
}
}
/**
* Set the closure that overrides the 'pusher' broadcaster.
*
* By default, override the 'pusher' broadcaster with a broadcaster that
* extends PusherBroadcaster, and overrides the formatChannels() method,
* such that e.g. 'private-channel' becomes 'private-tenantKey.channel'.
*/
public static function pusher(Closure|null $override = null): void
{
static::$broadcasterOverrides['pusher'] = $override ?? function () {
$broadcastManager = app(BroadcastManager::class);
return $broadcastManager->extend('pusher', function ($app, $config) use ($broadcastManager) {
return new class($broadcastManager->pusher($config)) extends PusherBroadcaster {
protected function formatChannels(array $channels)
{
$formatChannel = function (string $channel) {
$prefixes = ['private-', 'presence-', 'private-encrypted-'];
$defaultPrefix = '';
foreach ($prefixes as $prefix) {
if (str($channel)->startsWith($prefix)) {
$defaultPrefix = $prefix;
break;
}
}
// Give the tenant prefix to channels that aren't flagged as central
if (! str($channel)->startsWith('global__')) {
$channel = str($channel)->after($defaultPrefix)->prepend($defaultPrefix . tenant()->getTenantKey() . '.');
}
return (string) $channel;
};
return array_map($formatChannel, $channels);
}
};
});
};
}
/**
* Set the closure that overrides the 'ably' broadcaster.
*
* By default, override the 'ably' broadcaster with a broadcaster that
* Extends AblyBroadcaster, and overrides the formatChannels() method
* such that e.g. 'private-channel' becomes 'private:tenantKey.channel'.
*/
public static function ably(Closure|null $override = null): void
{
static::$broadcasterOverrides['ably'] = $override ?? function () {
$broadcastManager = app(BroadcastManager::class);
return $broadcastManager->extend('ably', function ($app, $config) use ($broadcastManager) {
return new class($broadcastManager->ably($config)) extends AblyBroadcaster {
protected function formatChannels(array $channels)
{
$formatChannel = function (string $channel) {
$prefixes = ['private:', 'presence:'];
$defaultPrefix = '';
foreach ($prefixes as $prefix) {
if (str($channel)->startsWith($prefix)) {
$defaultPrefix = $prefix;
break;
}
}
// Give the tenant prefix to channels that aren't flagged as central
if (! str($channel)->startsWith('global__')) {
$channel = str($channel)->after($defaultPrefix)->prepend($defaultPrefix . tenant()->getTenantKey() . '.');
}
return (string) $channel;
};
return array_map($formatChannel, parent::formatChannels($channels));
}
};
});
};
}
}