1<?php 2 3namespace Illuminate\Broadcasting; 4 5use Ably\AblyRest; 6use Closure; 7use Illuminate\Broadcasting\Broadcasters\AblyBroadcaster; 8use Illuminate\Broadcasting\Broadcasters\LogBroadcaster; 9use Illuminate\Broadcasting\Broadcasters\NullBroadcaster; 10use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster; 11use Illuminate\Broadcasting\Broadcasters\RedisBroadcaster; 12use Illuminate\Contracts\Broadcasting\Factory as FactoryContract; 13use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; 14use Illuminate\Contracts\Bus\Dispatcher as BusDispatcherContract; 15use Illuminate\Contracts\Foundation\CachesRoutes; 16use InvalidArgumentException; 17use Psr\Log\LoggerInterface; 18use Pusher\Pusher; 19 20/** 21 * @mixin \Illuminate\Contracts\Broadcasting\Broadcaster 22 */ 23class BroadcastManager implements FactoryContract 24{ 25 /** 26 * The application instance. 27 * 28 * @var \Illuminate\Contracts\Container\Container 29 */ 30 protected $app; 31 32 /** 33 * The array of resolved broadcast drivers. 34 * 35 * @var array 36 */ 37 protected $drivers = []; 38 39 /** 40 * The registered custom driver creators. 41 * 42 * @var array 43 */ 44 protected $customCreators = []; 45 46 /** 47 * Create a new manager instance. 48 * 49 * @param \Illuminate\Contracts\Container\Container $app 50 * @return void 51 */ 52 public function __construct($app) 53 { 54 $this->app = $app; 55 } 56 57 /** 58 * Register the routes for handling broadcast authentication and sockets. 59 * 60 * @param array|null $attributes 61 * @return void 62 */ 63 public function routes(array $attributes = null) 64 { 65 if ($this->app instanceof CachesRoutes && $this->app->routesAreCached()) { 66 return; 67 } 68 69 $attributes = $attributes ?: ['middleware' => ['web']]; 70 71 $this->app['router']->group($attributes, function ($router) { 72 $router->match( 73 ['get', 'post'], '/broadcasting/auth', 74 '\\'.BroadcastController::class.'@authenticate' 75 )->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]); 76 }); 77 } 78 79 /** 80 * Get the socket ID for the given request. 81 * 82 * @param \Illuminate\Http\Request|null $request 83 * @return string|null 84 */ 85 public function socket($request = null) 86 { 87 if (! $request && ! $this->app->bound('request')) { 88 return; 89 } 90 91 $request = $request ?: $this->app['request']; 92 93 return $request->header('X-Socket-ID'); 94 } 95 96 /** 97 * Begin broadcasting an event. 98 * 99 * @param mixed|null $event 100 * @return \Illuminate\Broadcasting\PendingBroadcast 101 */ 102 public function event($event = null) 103 { 104 return new PendingBroadcast($this->app->make('events'), $event); 105 } 106 107 /** 108 * Queue the given event for broadcast. 109 * 110 * @param mixed $event 111 * @return void 112 */ 113 public function queue($event) 114 { 115 if ($event instanceof ShouldBroadcastNow) { 116 return $this->app->make(BusDispatcherContract::class)->dispatchNow(new BroadcastEvent(clone $event)); 117 } 118 119 $queue = null; 120 121 if (method_exists($event, 'broadcastQueue')) { 122 $queue = $event->broadcastQueue(); 123 } elseif (isset($event->broadcastQueue)) { 124 $queue = $event->broadcastQueue; 125 } elseif (isset($event->queue)) { 126 $queue = $event->queue; 127 } 128 129 $this->app->make('queue')->connection($event->connection ?? null)->pushOn( 130 $queue, new BroadcastEvent(clone $event) 131 ); 132 } 133 134 /** 135 * Get a driver instance. 136 * 137 * @param string|null $driver 138 * @return mixed 139 */ 140 public function connection($driver = null) 141 { 142 return $this->driver($driver); 143 } 144 145 /** 146 * Get a driver instance. 147 * 148 * @param string|null $name 149 * @return mixed 150 */ 151 public function driver($name = null) 152 { 153 $name = $name ?: $this->getDefaultDriver(); 154 155 return $this->drivers[$name] = $this->get($name); 156 } 157 158 /** 159 * Attempt to get the connection from the local cache. 160 * 161 * @param string $name 162 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 163 */ 164 protected function get($name) 165 { 166 return $this->drivers[$name] ?? $this->resolve($name); 167 } 168 169 /** 170 * Resolve the given broadcaster. 171 * 172 * @param string $name 173 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 174 * 175 * @throws \InvalidArgumentException 176 */ 177 protected function resolve($name) 178 { 179 $config = $this->getConfig($name); 180 181 if (isset($this->customCreators[$config['driver']])) { 182 return $this->callCustomCreator($config); 183 } 184 185 $driverMethod = 'create'.ucfirst($config['driver']).'Driver'; 186 187 if (! method_exists($this, $driverMethod)) { 188 throw new InvalidArgumentException("Driver [{$config['driver']}] is not supported."); 189 } 190 191 return $this->{$driverMethod}($config); 192 } 193 194 /** 195 * Call a custom driver creator. 196 * 197 * @param array $config 198 * @return mixed 199 */ 200 protected function callCustomCreator(array $config) 201 { 202 return $this->customCreators[$config['driver']]($this->app, $config); 203 } 204 205 /** 206 * Create an instance of the driver. 207 * 208 * @param array $config 209 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 210 */ 211 protected function createPusherDriver(array $config) 212 { 213 $pusher = new Pusher( 214 $config['key'], $config['secret'], 215 $config['app_id'], $config['options'] ?? [] 216 ); 217 218 if ($config['log'] ?? false) { 219 $pusher->setLogger($this->app->make(LoggerInterface::class)); 220 } 221 222 return new PusherBroadcaster($pusher); 223 } 224 225 /** 226 * Create an instance of the driver. 227 * 228 * @param array $config 229 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 230 */ 231 protected function createAblyDriver(array $config) 232 { 233 return new AblyBroadcaster(new AblyRest($config)); 234 } 235 236 /** 237 * Create an instance of the driver. 238 * 239 * @param array $config 240 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 241 */ 242 protected function createRedisDriver(array $config) 243 { 244 return new RedisBroadcaster( 245 $this->app->make('redis'), $config['connection'] ?? null, 246 $this->app['config']->get('database.redis.options.prefix', '') 247 ); 248 } 249 250 /** 251 * Create an instance of the driver. 252 * 253 * @param array $config 254 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 255 */ 256 protected function createLogDriver(array $config) 257 { 258 return new LogBroadcaster( 259 $this->app->make(LoggerInterface::class) 260 ); 261 } 262 263 /** 264 * Create an instance of the driver. 265 * 266 * @param array $config 267 * @return \Illuminate\Contracts\Broadcasting\Broadcaster 268 */ 269 protected function createNullDriver(array $config) 270 { 271 return new NullBroadcaster; 272 } 273 274 /** 275 * Get the connection configuration. 276 * 277 * @param string $name 278 * @return array 279 */ 280 protected function getConfig($name) 281 { 282 if (! is_null($name) && $name !== 'null') { 283 return $this->app['config']["broadcasting.connections.{$name}"]; 284 } 285 286 return ['driver' => 'null']; 287 } 288 289 /** 290 * Get the default driver name. 291 * 292 * @return string 293 */ 294 public function getDefaultDriver() 295 { 296 return $this->app['config']['broadcasting.default']; 297 } 298 299 /** 300 * Set the default driver name. 301 * 302 * @param string $name 303 * @return void 304 */ 305 public function setDefaultDriver($name) 306 { 307 $this->app['config']['broadcasting.default'] = $name; 308 } 309 310 /** 311 * Disconnect the given disk and remove from local cache. 312 * 313 * @param string|null $name 314 * @return void 315 */ 316 public function purge($name = null) 317 { 318 $name = $name ?? $this->getDefaultDriver(); 319 320 unset($this->drivers[$name]); 321 } 322 323 /** 324 * Register a custom driver creator Closure. 325 * 326 * @param string $driver 327 * @param \Closure $callback 328 * @return $this 329 */ 330 public function extend($driver, Closure $callback) 331 { 332 $this->customCreators[$driver] = $callback; 333 334 return $this; 335 } 336 337 /** 338 * Get the application instance used by the manager. 339 * 340 * @return \Illuminate\Contracts\Foundation\Application 341 */ 342 public function getApplication() 343 { 344 return $this->app; 345 } 346 347 /** 348 * Set the application instance used by the manager. 349 * 350 * @param \Illuminate\Contracts\Foundation\Application $app 351 * @return $this 352 */ 353 public function setApplication($app) 354 { 355 $this->app = $app; 356 357 return $this; 358 } 359 360 /** 361 * Forget all of the resolved driver instances. 362 * 363 * @return $this 364 */ 365 public function forgetDrivers() 366 { 367 $this->drivers = []; 368 369 return $this; 370 } 371 372 /** 373 * Dynamically call the default driver instance. 374 * 375 * @param string $method 376 * @param array $parameters 377 * @return mixed 378 */ 379 public function __call($method, $parameters) 380 { 381 return $this->driver()->$method(...$parameters); 382 } 383} 384