@@ -23,6 +23,9 @@ trait OffersHooks
2323 /** @var \SplObjectStorage|array<\Closure> */
2424 private $ messageReceivedEventHandlers ;
2525
26+ /** @var \SplObjectStorage|array<\Closure> */
27+ private $ connectedEventHandlers ;
28+
2629 /**
2730 * Needs to be called in order to initialize the trait.
2831 *
@@ -33,6 +36,7 @@ protected function initializeEventHandlers(): void
3336 $ this ->loopEventHandlers = new \SplObjectStorage ();
3437 $ this ->publishEventHandlers = new \SplObjectStorage ();
3538 $ this ->messageReceivedEventHandlers = new \SplObjectStorage ();
39+ $ this ->connectedEventHandlers = new \SplObjectStorage ();
3640 }
3741
3842 /**
@@ -266,4 +270,77 @@ private function runMessageReceivedEventHandlers(string $topic, string $message,
266270 }
267271 }
268272 }
273+
274+ /**
275+ * Registers an event handler which is called when the client established a connection to the broker.
276+ * This also includes manual reconnects as well as auto-reconnects by the client itself.
277+ *
278+ * The event handler is passed the MQTT client as first argument,
279+ * followed by a flag which indicates whether an auto-reconnect occurred as second argument.
280+ *
281+ * Example:
282+ * ```php
283+ * $mqtt->registerConnectedEventHandler(function (
284+ * MqttClient $mqtt,
285+ * bool $isAutoReconnect
286+ * ) use ($logger) {
287+ * if ($isAutoReconnect) {
288+ * $logger->info("Client successfully auto-reconnected to the broker.);
289+ * } else {
290+ * $logger->info("Client successfully connected to the broker.");
291+ * }
292+ * });
293+ * ```
294+ *
295+ * Multiple event handlers can be registered at the same time.
296+ *
297+ * @param \Closure $callback
298+ * @return MqttClient
299+ */
300+ public function registerConnectedEventHandler (\Closure $ callback ): MqttClient
301+ {
302+ $ this ->connectedEventHandlers ->attach ($ callback );
303+
304+ /** @var MqttClient $this */
305+ return $ this ;
306+ }
307+
308+ /**
309+ * Unregisters a connected event handler which prevents it from being called in the future.
310+ *
311+ * This does not affect other registered event handlers. It is possible
312+ * to unregister all registered event handlers by passing null as callback.
313+ *
314+ * @param \Closure|null $callback
315+ * @return MqttClient
316+ */
317+ public function unregisterConnectedEventHandler (\Closure $ callback = null ): MqttClient
318+ {
319+ if ($ callback === null ) {
320+ $ this ->connectedEventHandlers ->removeAll ($ this ->connectedEventHandlers );
321+ } else {
322+ $ this ->connectedEventHandlers ->detach ($ callback );
323+ }
324+
325+ /** @var MqttClient $this */
326+ return $ this ;
327+ }
328+
329+ /**
330+ * Runs all the registered connected event handlers.
331+ * Each event handler is executed in a try-catch block to avoid spilling exceptions.
332+ *
333+ * @param bool $isAutoReconnect
334+ * @return void
335+ */
336+ private function runConnectedEventHandlers (bool $ isAutoReconnect ): void
337+ {
338+ foreach ($ this ->connectedEventHandlers as $ handler ) {
339+ try {
340+ call_user_func ($ handler , $ this , $ isAutoReconnect );
341+ } catch (\Throwable $ e ) {
342+ $ this ->logger ->error ('Connected hook callback threw exception. ' , ['exception ' => $ e ]);
343+ }
344+ }
345+ }
269346}
0 commit comments