Skip to content

Commit d543813

Browse files
Alex6092Namoshek
andauthored
Refactor MQTT client loop implementation to allow easier integration with third party event loops (#115)
* Refactor MQTT client loop implementation This refactoring add a loopOnce method to MqttClient class. This method allows the MQTT client implementation to be integrated to an event loop (like ReactPHP - Ratchet). * Update loopOnce documentation * Add method to interface, refactor and reformat code Co-authored-by: Marvin Mall <[email protected]>
1 parent 77f6854 commit d543813

File tree

2 files changed

+53
-23
lines changed

2 files changed

+53
-23
lines changed

src/Contracts/MqttClient.php

+22
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PhpMqtt\Client\Exceptions\ConfigurationInvalidException;
99
use PhpMqtt\Client\Exceptions\ConnectingToBrokerFailedException;
1010
use PhpMqtt\Client\Exceptions\DataTransferException;
11+
use PhpMqtt\Client\Exceptions\InvalidMessageException;
1112
use PhpMqtt\Client\Exceptions\MqttClientException;
1213
use PhpMqtt\Client\Exceptions\ProtocolViolationException;
1314
use PhpMqtt\Client\Exceptions\RepositoryException;
@@ -143,11 +144,32 @@ public function interrupt(): void;
143144
* @param int|null $queueWaitLimit
144145
* @return void
145146
* @throws DataTransferException
147+
* @throws InvalidMessageException
146148
* @throws MqttClientException
147149
* @throws ProtocolViolationException
148150
*/
149151
public function loop(bool $allowSleep = true, bool $exitWhenQueuesEmpty = false, int $queueWaitLimit = null): void;
150152

153+
/**
154+
* Runs an event loop iteration that handles messages from the server and calls the registered
155+
* callbacks for published messages. Also resends pending messages and calls loop event handlers.
156+
*
157+
* This method can be used to integrate the MQTT client in another event loop (like ReactPHP or Ratchet).
158+
*
159+
* Note: To ensure the event handlers called by this method will receive the correct elapsed time,
160+
* the caller is responsible to provide the correct starting time of the loop as returned by `microtime(true)`.
161+
*
162+
* @param float $loopStartedAt
163+
* @param bool $allowSleep
164+
* @param int $sleepMicroseconds
165+
* @return void
166+
* @throws DataTransferException
167+
* @throws InvalidMessageException
168+
* @throws MqttClientException
169+
* @throws ProtocolViolationException
170+
*/
171+
public function loopOnce(float $loopStartedAt, bool $allowSleep = false, int $sleepMicroseconds = 100000): void;
172+
151173
/**
152174
* Returns the host used by the client to connect to.
153175
*

src/MqttClient.php

+31-23
Original file line numberDiff line numberDiff line change
@@ -641,29 +641,7 @@ public function loop(bool $allowSleep = true, bool $exitWhenQueuesEmpty = false,
641641
break;
642642
}
643643

644-
$elapsedTime = microtime(true) - $loopStartedAt;
645-
$this->runLoopEventHandlers($elapsedTime);
646-
647-
// Read data from the socket - as much as available.
648-
$this->buffer .= $this->readAllAvailableDataFromSocket();
649-
650-
// Try to parse a message from the buffer and handle it, as long as messages can be parsed.
651-
if (strlen($this->buffer) > 0) {
652-
$this->processMessageBuffer();
653-
} elseif ($allowSleep) {
654-
usleep(100000); // 100ms
655-
}
656-
657-
// Republish messages expired without confirmation.
658-
// This includes published messages, subscribe and unsubscribe requests.
659-
$this->resendPendingMessages();
660-
661-
// If the last message of the broker has been received more seconds ago
662-
// than specified by the keep alive time, we will send a ping to ensure
663-
// the connection is kept alive.
664-
if ($this->nextPingAt() <= microtime(true)) {
665-
$this->ping();
666-
}
644+
$this->loopOnce($loopStartedAt, $allowSleep);
667645

668646
// If configured, the loop is exited if all queues are empty or a certain time limit is reached (i.e. retry is aborted).
669647
// In any case, there may not be any active subscriptions though.
@@ -681,6 +659,36 @@ public function loop(bool $allowSleep = true, bool $exitWhenQueuesEmpty = false,
681659
}
682660
}
683661

662+
/**
663+
* {@inheritDoc}
664+
*/
665+
public function loopOnce(float $loopStartedAt, bool $allowSleep = false, int $sleepMicroseconds = 100000): void
666+
{
667+
$elapsedTime = microtime(true) - $loopStartedAt;
668+
$this->runLoopEventHandlers($elapsedTime);
669+
670+
// Read data from the socket - as much as available.
671+
$this->buffer .= $this->readAllAvailableDataFromSocket();
672+
673+
// Try to parse a message from the buffer and handle it, as long as messages can be parsed.
674+
if (strlen($this->buffer) > 0) {
675+
$this->processMessageBuffer();
676+
} elseif ($allowSleep) {
677+
usleep($sleepMicroseconds);
678+
}
679+
680+
// Republish messages expired without confirmation.
681+
// This includes published messages, subscribe and unsubscribe requests.
682+
$this->resendPendingMessages();
683+
684+
// If the last message of the broker has been received more seconds ago
685+
// than specified by the keep alive time, we will send a ping to ensure
686+
// the connection is kept alive.
687+
if ($this->nextPingAt() <= microtime(true)) {
688+
$this->ping();
689+
}
690+
}
691+
684692
/**
685693
* Processes the incoming message buffer by parsing and handling the messages, until the buffer is empty.
686694
*

0 commit comments

Comments
 (0)