CMS 2.0
This commit is contained in:
21
vendor/php-mqtt/client/src/Concerns/GeneratesRandomClientIds.php
vendored
Normal file
21
vendor/php-mqtt/client/src/Concerns/GeneratesRandomClientIds.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Concerns;
|
||||
|
||||
/**
|
||||
* Provides common methods used to generate random client ids.
|
||||
*
|
||||
* @package PhpMqtt\Client\Concerns
|
||||
*/
|
||||
trait GeneratesRandomClientIds
|
||||
{
|
||||
/**
|
||||
* Generates a random client id in the form of an md5 hash.
|
||||
*/
|
||||
protected function generateRandomClientId(): string
|
||||
{
|
||||
return substr(md5(uniqid((string) random_int(0, PHP_INT_MAX), true)), 0, 20);
|
||||
}
|
||||
}
|
||||
301
vendor/php-mqtt/client/src/Concerns/OffersHooks.php
vendored
Normal file
301
vendor/php-mqtt/client/src/Concerns/OffersHooks.php
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Concerns;
|
||||
|
||||
use PhpMqtt\Client\Contracts\MqttClient;
|
||||
|
||||
/**
|
||||
* Contains common methods and properties necessary to offer hooks.
|
||||
*
|
||||
* @mixin MqttClient
|
||||
* @package PhpMqtt\Client\Concerns
|
||||
*/
|
||||
trait OffersHooks
|
||||
{
|
||||
/** @var \SplObjectStorage|array<\Closure> */
|
||||
private $loopEventHandlers;
|
||||
|
||||
/** @var \SplObjectStorage|array<\Closure> */
|
||||
private $publishEventHandlers;
|
||||
|
||||
/** @var \SplObjectStorage|array<\Closure> */
|
||||
private $messageReceivedEventHandlers;
|
||||
|
||||
/** @var \SplObjectStorage|array<\Closure> */
|
||||
private $connectedEventHandlers;
|
||||
|
||||
/**
|
||||
* Needs to be called in order to initialize the trait.
|
||||
*/
|
||||
protected function initializeEventHandlers(): void
|
||||
{
|
||||
$this->loopEventHandlers = new \SplObjectStorage();
|
||||
$this->publishEventHandlers = new \SplObjectStorage();
|
||||
$this->messageReceivedEventHandlers = new \SplObjectStorage();
|
||||
$this->connectedEventHandlers = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a loop event handler which is called each iteration of the loop.
|
||||
* This event handler can be used for example to interrupt the loop under
|
||||
* certain conditions.
|
||||
*
|
||||
* The loop event handler is passed the MQTT client instance as first and
|
||||
* the elapsed time which the loop is already running for as second
|
||||
* parameter. The elapsed time is a float containing seconds.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerLoopEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* float $elapsedTime
|
||||
* ) use ($logger) {
|
||||
* $logger->info("Running for [{$elapsedTime}] seconds already.");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerLoopEventHandler(\Closure $callback): MqttClient
|
||||
{
|
||||
$this->loopEventHandlers->attach($callback);
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a loop event handler which prevents it from being called
|
||||
* in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterLoopEventHandler(?\Closure $callback = null): MqttClient
|
||||
{
|
||||
if ($callback === null) {
|
||||
$this->loopEventHandlers->removeAll($this->loopEventHandlers);
|
||||
} else {
|
||||
$this->loopEventHandlers->detach($callback);
|
||||
}
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all registered loop event handlers with the given parameters.
|
||||
* Each event handler is executed in a try-catch block to avoid spilling exceptions.
|
||||
*/
|
||||
private function runLoopEventHandlers(float $elapsedTime): void
|
||||
{
|
||||
foreach ($this->loopEventHandlers as $handler) {
|
||||
try {
|
||||
call_user_func($handler, $this, $elapsedTime);
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Loop hook callback threw exception.', ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a loop event handler which is called when a message is published.
|
||||
*
|
||||
* The loop event handler is passed the MQTT client as first, the topic as
|
||||
* second and the message as third parameter. As fourth parameter, the message identifier
|
||||
* will be passed, which can be null in case of QoS 0. The QoS level as well as the retained
|
||||
* flag will also be passed as fifth and sixth parameters.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerPublishEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* string $topic,
|
||||
* string $message,
|
||||
* ?int $messageId,
|
||||
* int $qualityOfService,
|
||||
* bool $retain
|
||||
* ) use ($logger) {
|
||||
* $logger->info("Sending message on topic [{$topic}]: {$message}");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerPublishEventHandler(\Closure $callback): MqttClient
|
||||
{
|
||||
$this->publishEventHandlers->attach($callback);
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a publish event handler which prevents it from being called
|
||||
* in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterPublishEventHandler(?\Closure $callback = null): MqttClient
|
||||
{
|
||||
if ($callback === null) {
|
||||
$this->publishEventHandlers->removeAll($this->publishEventHandlers);
|
||||
} else {
|
||||
$this->publishEventHandlers->detach($callback);
|
||||
}
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all the registered publish event handlers with the given parameters.
|
||||
* Each event handler is executed in a try-catch block to avoid spilling exceptions.
|
||||
*/
|
||||
private function runPublishEventHandlers(string $topic, string $message, ?int $messageId, int $qualityOfService, bool $retain): void
|
||||
{
|
||||
foreach ($this->publishEventHandlers as $handler) {
|
||||
try {
|
||||
call_user_func($handler, $this, $topic, $message, $messageId, $qualityOfService, $retain);
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Publish hook callback threw exception for published message on topic [{topic}].', [
|
||||
'topic' => $topic,
|
||||
'exception' => $e,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an event handler which is called when a message is received from the broker.
|
||||
*
|
||||
* The message received event handler is passed the MQTT client as first, the topic as
|
||||
* second and the message as third parameter. As fourth parameter, the QoS level will be
|
||||
* passed and the retained flag as fifth.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerReceivedMessageEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* string $topic,
|
||||
* string $message,
|
||||
* int $qualityOfService,
|
||||
* bool $retained
|
||||
* ) use ($logger) {
|
||||
* $logger->info("Received message on topic [{$topic}]: {$message}");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerMessageReceivedEventHandler(\Closure $callback): MqttClient
|
||||
{
|
||||
$this->messageReceivedEventHandlers->attach($callback);
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a message received event handler which prevents it from being called in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterMessageReceivedEventHandler(?\Closure $callback = null): MqttClient
|
||||
{
|
||||
if ($callback === null) {
|
||||
$this->messageReceivedEventHandlers->removeAll($this->messageReceivedEventHandlers);
|
||||
} else {
|
||||
$this->messageReceivedEventHandlers->detach($callback);
|
||||
}
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all the registered message received event handlers with the given parameters.
|
||||
* Each event handler is executed in a try-catch block to avoid spilling exceptions.
|
||||
*/
|
||||
private function runMessageReceivedEventHandlers(string $topic, string $message, int $qualityOfService, bool $retained): void
|
||||
{
|
||||
foreach ($this->messageReceivedEventHandlers as $handler) {
|
||||
try {
|
||||
call_user_func($handler, $this, $topic, $message, $qualityOfService, $retained);
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Received message hook callback threw exception for received message on topic [{topic}].', [
|
||||
'topic' => $topic,
|
||||
'exception' => $e,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an event handler which is called when the client established a connection to the broker.
|
||||
* This also includes manual reconnects as well as auto-reconnects by the client itself.
|
||||
*
|
||||
* The event handler is passed the MQTT client as first argument,
|
||||
* followed by a flag which indicates whether an auto-reconnect occurred as second argument.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerConnectedEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* bool $isAutoReconnect
|
||||
* ) use ($logger) {
|
||||
* if ($isAutoReconnect) {
|
||||
* $logger->info("Client successfully auto-reconnected to the broker.);
|
||||
* } else {
|
||||
* $logger->info("Client successfully connected to the broker.");
|
||||
* }
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerConnectedEventHandler(\Closure $callback): MqttClient
|
||||
{
|
||||
$this->connectedEventHandlers->attach($callback);
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a connected event handler which prevents it from being called in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterConnectedEventHandler(?\Closure $callback = null): MqttClient
|
||||
{
|
||||
if ($callback === null) {
|
||||
$this->connectedEventHandlers->removeAll($this->connectedEventHandlers);
|
||||
} else {
|
||||
$this->connectedEventHandlers->detach($callback);
|
||||
}
|
||||
|
||||
/** @var MqttClient $this */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs all the registered connected event handlers.
|
||||
* Each event handler is executed in a try-catch block to avoid spilling exceptions.
|
||||
*/
|
||||
private function runConnectedEventHandlers(bool $isAutoReconnect): void
|
||||
{
|
||||
foreach ($this->connectedEventHandlers as $handler) {
|
||||
try {
|
||||
call_user_func($handler, $this, $isAutoReconnect);
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->error('Connected hook callback threw exception.', ['exception' => $e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
vendor/php-mqtt/client/src/Concerns/TranscodesData.php
vendored
Normal file
78
vendor/php-mqtt/client/src/Concerns/TranscodesData.php
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Concerns;
|
||||
|
||||
/**
|
||||
* Provides common methods to encode data before sending it to a broker
|
||||
* and to decode data received from a broker.
|
||||
*
|
||||
* @package PhpMqtt\Client\Concerns
|
||||
*/
|
||||
trait TranscodesData
|
||||
{
|
||||
/**
|
||||
* Creates a string which is prefixed with its own length as bytes.
|
||||
* This means a string like 'hello world' will become
|
||||
*
|
||||
* \x00\x0bhello world
|
||||
*
|
||||
* where \x00\0x0b is the hex representation of 00000000 00001011 = 11
|
||||
*/
|
||||
protected function buildLengthPrefixedString(string $data): string
|
||||
{
|
||||
$length = strlen($data);
|
||||
$msb = $length >> 8;
|
||||
$lsb = $length % 256;
|
||||
|
||||
return chr($msb) . chr($lsb) . $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given string to a number, assuming it is an MSB encoded message id.
|
||||
* MSB means preceding characters have higher value.
|
||||
*/
|
||||
protected function decodeMessageId(string $encodedMessageId): int
|
||||
{
|
||||
$length = strlen($encodedMessageId);
|
||||
$result = 0;
|
||||
|
||||
foreach (str_split($encodedMessageId) as $index => $char) {
|
||||
$result += ord($char) << (($length - 1) * 8 - ($index * 8));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given message identifier as string.
|
||||
*/
|
||||
protected function encodeMessageId(int $messageId): string
|
||||
{
|
||||
return chr($messageId >> 8) . chr($messageId % 256);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the length of a message as string, so it can be transmitted
|
||||
* over the wire.
|
||||
*/
|
||||
protected function encodeMessageLength(int $length): string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
do {
|
||||
$digit = $length % 128;
|
||||
$length = $length >> 7;
|
||||
|
||||
// if there are more digits to encode, set the top bit of this digit
|
||||
if ($length > 0) {
|
||||
$digit = ($digit | 0x80);
|
||||
}
|
||||
|
||||
$result .= chr($digit);
|
||||
} while ($length > 0);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
89
vendor/php-mqtt/client/src/Concerns/ValidatesConfiguration.php
vendored
Normal file
89
vendor/php-mqtt/client/src/Concerns/ValidatesConfiguration.php
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Concerns;
|
||||
|
||||
use PhpMqtt\Client\ConnectionSettings;
|
||||
use PhpMqtt\Client\Exceptions\ConfigurationInvalidException;
|
||||
use PhpMqtt\Client\MqttClient;
|
||||
|
||||
/**
|
||||
* Provides methods to validate the configuration of an {@see MqttClient} and
|
||||
* the {@see ConnectionSettings} being used to connect to a broker.
|
||||
*
|
||||
* @package PhpMqtt\Client\Concerns
|
||||
*/
|
||||
trait ValidatesConfiguration
|
||||
{
|
||||
/**
|
||||
* Ensures the given connection settings are valid. If they are not valid,
|
||||
* which means they are misconfigured, an exception containing information about
|
||||
* the configuration error is thrown.
|
||||
*
|
||||
* @throws ConfigurationInvalidException
|
||||
*/
|
||||
protected function ensureConnectionSettingsAreValid(ConnectionSettings $settings): void
|
||||
{
|
||||
if ($settings->getConnectTimeout() < 1) {
|
||||
throw new ConfigurationInvalidException('The connect timeout cannot be less than 1 second.');
|
||||
}
|
||||
|
||||
if ($settings->getSocketTimeout() < 1) {
|
||||
throw new ConfigurationInvalidException('The socket timeout cannot be less than 1 second.');
|
||||
}
|
||||
|
||||
if ($settings->getResendTimeout() < 1) {
|
||||
throw new ConfigurationInvalidException('The resend timeout cannot be less than 1 second.');
|
||||
}
|
||||
|
||||
if ($settings->getKeepAliveInterval() < 1 || $settings->getKeepAliveInterval() > 65535) {
|
||||
throw new ConfigurationInvalidException('The keep alive interval must be a value in the range of 1 to 65535 seconds.');
|
||||
}
|
||||
|
||||
if ($settings->getMaxReconnectAttempts() < 1) {
|
||||
throw new ConfigurationInvalidException('The maximum reconnect attempts cannot be fewer than 1.');
|
||||
}
|
||||
|
||||
if ($settings->getDelayBetweenReconnectAttempts() < 0) {
|
||||
throw new ConfigurationInvalidException('The delay between reconnect attempts cannot be lower than 0.');
|
||||
}
|
||||
|
||||
if ($settings->getUsername() !== null && trim($settings->getUsername()) === '') {
|
||||
throw new ConfigurationInvalidException('The username may not consist of white space only.');
|
||||
}
|
||||
|
||||
if ($settings->getLastWillTopic() !== null && trim($settings->getLastWillTopic()) === '') {
|
||||
throw new ConfigurationInvalidException('The last will topic may not consist of white space only.');
|
||||
}
|
||||
|
||||
if ($settings->getLastWillQualityOfService() < MqttClient::QOS_AT_MOST_ONCE
|
||||
|| $settings->getLastWillQualityOfService() > MqttClient::QOS_EXACTLY_ONCE) {
|
||||
throw new ConfigurationInvalidException('The QoS for the last will must be a value in the range of 0 to 2.');
|
||||
}
|
||||
|
||||
if ($settings->getTlsCertificateAuthorityFile() !== null && !is_file($settings->getTlsCertificateAuthorityFile())) {
|
||||
throw new ConfigurationInvalidException('The Certificate Authority file setting must contain the path to a regular file.');
|
||||
}
|
||||
|
||||
if ($settings->getTlsCertificateAuthorityPath() !== null && !is_dir($settings->getTlsCertificateAuthorityPath())) {
|
||||
throw new ConfigurationInvalidException('The Certificate Authority path setting must contain the path to a directory.');
|
||||
}
|
||||
|
||||
if ($settings->getTlsClientCertificateFile() !== null && !is_file($settings->getTlsClientCertificateFile())) {
|
||||
throw new ConfigurationInvalidException('The client certificate file setting must contain the path to a regular file.');
|
||||
}
|
||||
|
||||
if ($settings->getTlsClientCertificateKeyFile() !== null && !is_file($settings->getTlsClientCertificateKeyFile())) {
|
||||
throw new ConfigurationInvalidException('The client certificate key file setting must contain the path to a regular file.');
|
||||
}
|
||||
|
||||
if ($settings->getTlsClientCertificateKeyFile() !== null && $settings->getTlsClientCertificateFile() === null) {
|
||||
throw new ConfigurationInvalidException('Using a client certificate key file without certificate does not work.');
|
||||
}
|
||||
|
||||
if ($settings->getTlsClientCertificateKeyPassphrase() !== null && $settings->getTlsClientCertificateKeyFile() === null) {
|
||||
throw new ConfigurationInvalidException('Using a client certificate key passphrase without key file does not work.');
|
||||
}
|
||||
}
|
||||
}
|
||||
26
vendor/php-mqtt/client/src/Concerns/WorksWithBuffers.php
vendored
Normal file
26
vendor/php-mqtt/client/src/Concerns/WorksWithBuffers.php
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Concerns;
|
||||
|
||||
/**
|
||||
* Provides common methods to work with buffers.
|
||||
*
|
||||
* @package PhpMqtt\Client\Concerns
|
||||
*/
|
||||
trait WorksWithBuffers
|
||||
{
|
||||
/**
|
||||
* Pops the first $limit bytes from the given buffer and returns them.
|
||||
*/
|
||||
protected function pop(string &$buffer, int $limit): string
|
||||
{
|
||||
$limit = min(strlen($buffer), $limit);
|
||||
|
||||
$result = substr($buffer, 0, $limit);
|
||||
$buffer = substr($buffer, $limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
555
vendor/php-mqtt/client/src/ConnectionSettings.php
vendored
Normal file
555
vendor/php-mqtt/client/src/ConnectionSettings.php
vendored
Normal file
@@ -0,0 +1,555 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
/**
|
||||
* The settings used during connection to a broker.
|
||||
*
|
||||
* This class is immutable and all setters return a clone of the original class because
|
||||
* connection settings must not change once passed to MqttClient.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class ConnectionSettings
|
||||
{
|
||||
private ?string $username = null;
|
||||
private ?string $password = null;
|
||||
private bool $useBlockingSocket = false;
|
||||
private int $connectTimeout = 60;
|
||||
private int $socketTimeout = 5;
|
||||
private int $resendTimeout = 10;
|
||||
private int $keepAliveInterval = 10;
|
||||
private bool $reconnectAutomatically = false;
|
||||
private int $maxReconnectAttempts = 3;
|
||||
private int $delayBetweenReconnectAttempts = 0;
|
||||
private ?string $lastWillTopic = null;
|
||||
private ?string $lastWillMessage = null;
|
||||
private int $lastWillQualityOfService = 0;
|
||||
private bool $lastWillRetain = false;
|
||||
private bool $useTls = false;
|
||||
private bool $tlsVerifyPeer = true;
|
||||
private bool $tlsVerifyPeerName = true;
|
||||
private bool $tlsSelfSignedAllowed = false;
|
||||
private ?string $tlsCertificateAuthorityFile = null;
|
||||
private ?string $tlsCertificateAuthorityPath = null;
|
||||
private ?string $tlsClientCertificateFile = null;
|
||||
private ?string $tlsClientCertificateKeyFile = null;
|
||||
private ?string $tlsClientCertificateKeyPassphrase = null;
|
||||
private ?string $tlsAlpn = null;
|
||||
|
||||
/**
|
||||
* The username used for authentication when connecting to the broker.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setUsername(?string $username): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->username = $username;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* The password used for authentication when connecting to the broker.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setPassword(?string $password): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->password = $password;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to use a blocking socket when publishing messages or not.
|
||||
* Normally, this setting can be ignored. When publishing large messages with multiple kilobytes in size,
|
||||
* a blocking socket may be required if the receipt buffer of the broker is not large enough.
|
||||
*
|
||||
* Note: This setting has no effect on subscriptions, only on the publishing of messages.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function useBlockingSocket(bool $useBlockingSocket): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->useBlockingSocket = $useBlockingSocket;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function shouldUseBlockingSocket(): bool
|
||||
{
|
||||
return $this->useBlockingSocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* The connect timeout is the maximum amount of seconds the client will try to establish
|
||||
* a socket connection with the broker. The value cannot be less than 1 second.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setConnectTimeout(int $connectTimeout): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->connectTimeout = $connectTimeout;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getConnectTimeout(): int
|
||||
{
|
||||
return $this->connectTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The socket timeout is the maximum amount of idle time in seconds for the socket connection.
|
||||
* If no data is read or sent for the given amount of seconds, the socket will be closed.
|
||||
* The value cannot be less than 1 second.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setSocketTimeout(int $socketTimeout): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->socketTimeout = $socketTimeout;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getSocketTimeout(): int
|
||||
{
|
||||
return $this->socketTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The resend timeout is the number of seconds the client will wait before sending a duplicate
|
||||
* of pending messages without acknowledgement. The value cannot be less than 1 second.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setResendTimeout(int $resendTimeout): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->resendTimeout = $resendTimeout;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getResendTimeout(): int
|
||||
{
|
||||
return $this->resendTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The keep alive interval is the number of seconds the client will wait without sending a message
|
||||
* until it sends a keep alive signal (ping) to the broker. The value cannot be less than 1 second
|
||||
* and may not be higher than 65535 seconds. A reasonable value is 10 seconds (the default).
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setKeepAliveInterval(int $keepAliveInterval): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->keepAliveInterval = $keepAliveInterval;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getKeepAliveInterval(): int
|
||||
{
|
||||
return $this->keepAliveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag determines whether the client will try to reconnect automatically,
|
||||
* if it notices a disconnect while sending data.
|
||||
* The setting cannot be used together with the clean session flag.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setReconnectAutomatically(bool $reconnectAutomatically): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->reconnectAutomatically = $reconnectAutomatically;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function shouldReconnectAutomatically(): bool
|
||||
{
|
||||
return $this->reconnectAutomatically;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the maximum number of reconnect attempts until the client gives up. This setting
|
||||
* is only relevant if {@see setReconnectAutomatically()} is set to true.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setMaxReconnectAttempts(int $maxReconnectAttempts): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->maxReconnectAttempts = $maxReconnectAttempts;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getMaxReconnectAttempts(): int
|
||||
{
|
||||
return $this->maxReconnectAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the delay between reconnect attempts in milliseconds.
|
||||
* This setting is only relevant if {@see setReconnectAutomatically()} is set to true.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setDelayBetweenReconnectAttempts(int $delayBetweenReconnectAttempts): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->delayBetweenReconnectAttempts = $delayBetweenReconnectAttempts;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getDelayBetweenReconnectAttempts(): int
|
||||
{
|
||||
return $this->delayBetweenReconnectAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the broker should publish a last will message in the name of the client when the client
|
||||
* disconnects abruptly, this setting defines the topic on which the message will be published.
|
||||
*
|
||||
* A last will message will only be published if both this setting as well as the last will
|
||||
* message are configured.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setLastWillTopic(?string $lastWillTopic): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->lastWillTopic = $lastWillTopic;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getLastWillTopic(): ?string
|
||||
{
|
||||
return $this->lastWillTopic;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the broker should publish a last will message in the name of the client when the client
|
||||
* disconnects abruptly, this setting defines the message which will be published.
|
||||
*
|
||||
* A last will message will only be published if both this setting as well as the last will
|
||||
* topic are configured.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setLastWillMessage(?string $lastWillMessage): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->lastWillMessage = $lastWillMessage;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getLastWillMessage(): ?string
|
||||
{
|
||||
return $this->lastWillMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the client has a last will.
|
||||
*/
|
||||
public function hasLastWill(): bool
|
||||
{
|
||||
return $this->lastWillTopic !== null && $this->lastWillMessage !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The quality of service level the last will message of the client will be published with,
|
||||
* if it gets triggered.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setLastWillQualityOfService(int $lastWillQualityOfService): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->lastWillQualityOfService = $lastWillQualityOfService;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getLastWillQualityOfService(): int
|
||||
{
|
||||
return $this->lastWillQualityOfService;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag determines if the last will message of the client will be retained, if it gets
|
||||
* triggered. Using this setting can be handy to signal that a client is offline by publishing
|
||||
* a retained offline state in the last will and an online state as first message on connect.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setRetainLastWill(bool $lastWillRetain): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->lastWillRetain = $lastWillRetain;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function shouldRetainLastWill(): bool
|
||||
{
|
||||
return $this->lastWillRetain;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag determines if TLS should be used for the connection. The port which is used to
|
||||
* connect to the broker must support TLS connections.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setUseTls(bool $useTls): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->useTls = $useTls;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function shouldUseTls(): bool
|
||||
{
|
||||
return $this->useTls;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag determines if the peer certificate is verified, if TLS is used.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsVerifyPeer(bool $tlsVerifyPeer): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsVerifyPeer = $tlsVerifyPeer;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function shouldTlsVerifyPeer(): bool
|
||||
{
|
||||
return $this->tlsVerifyPeer;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag determines if the peer name is verified, if TLS is used.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsVerifyPeerName(bool $tlsVerifyPeerName): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsVerifyPeerName = $tlsVerifyPeerName;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function shouldTlsVerifyPeerName(): bool
|
||||
{
|
||||
return $this->tlsVerifyPeerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* This flag determines if self signed certificates of the peer should be accepted.
|
||||
* Setting this to TRUE implies a security risk and should be avoided for production
|
||||
* scenarios and public services.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsSelfSignedAllowed(bool $tlsSelfSignedAllowed): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsSelfSignedAllowed = $tlsSelfSignedAllowed;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function isTlsSelfSignedAllowed(): bool
|
||||
{
|
||||
return $this->tlsSelfSignedAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to a Certificate Authority certificate which is used to verify the peer
|
||||
* certificate, if TLS is used.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsCertificateAuthorityFile(?string $tlsCertificateAuthorityFile): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsCertificateAuthorityFile = $tlsCertificateAuthorityFile;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getTlsCertificateAuthorityFile(): ?string
|
||||
{
|
||||
return $this->tlsCertificateAuthorityFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to a directory containing Certificate Authority certificates which are
|
||||
* used to verify the peer certificate, if TLS is used.
|
||||
*
|
||||
* Certificate files in this directory must be named by the hash of the certificate,
|
||||
* ending with ".0" (without quotes). The certificate hash can be retrieved using the
|
||||
* openssl_x509_parse() function, which returns an array. The hash can be found in the
|
||||
* array under the key "hash".
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsCertificateAuthorityPath(?string $tlsCertificateAuthorityPath): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsCertificateAuthorityPath = $tlsCertificateAuthorityPath;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getTlsCertificateAuthorityPath(): ?string
|
||||
{
|
||||
return $this->tlsCertificateAuthorityPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to a client certificate file used for authentication, if TLS is used.
|
||||
*
|
||||
* The client certificate must be PEM encoded. It may optionally contain the
|
||||
* certificate chain of issuers. The certificate key can be included in this certificate
|
||||
* file or in a separate file ({@see ConnectionSettings::setTlsClientCertificateKeyFile()}).
|
||||
* A passphrase can be configured using {@see ConnectionSettings::setTlsClientCertificateKeyPassphrase()}.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsClientCertificateFile(?string $tlsClientCertificateFile): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsClientCertificateFile = $tlsClientCertificateFile;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getTlsClientCertificateFile(): ?string
|
||||
{
|
||||
return $this->tlsClientCertificateFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* The path to a client certificate key file used for authentication, if TLS is used.
|
||||
*
|
||||
* This option requires {@see ConnectionSettings::setTlsClientCertificateFile()}
|
||||
* to be used as well.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsClientCertificateKeyFile(?string $tlsClientCertificateKeyFile): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsClientCertificateKeyFile = $tlsClientCertificateKeyFile;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getTlsClientCertificateKeyFile(): ?string
|
||||
{
|
||||
return $this->tlsClientCertificateKeyFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* The passphrase used to decrypt the private key of the client certificate,
|
||||
* which in return is used for authentication, if TLS is used.
|
||||
*
|
||||
* This option requires {@see ConnectionSettings::setTlsClientCertificateFile()}
|
||||
* and {@see ConnectionSettings::setTlsClientCertificateKeyFile()} to be used as well.
|
||||
*
|
||||
* Please be aware that your passphrase is not stored in secure memory when using this option.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsClientCertificateKeyPassphrase(?string $tlsClientCertificateKeyPassphrase): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsClientCertificateKeyPassphrase = $tlsClientCertificateKeyPassphrase;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getTlsClientCertificateKeyPassphrase(): ?string
|
||||
{
|
||||
return $this->tlsClientCertificateKeyPassphrase;
|
||||
}
|
||||
|
||||
/**
|
||||
* The TLS ALPN is used to establish a TLS encrypted mqtt connection on port 443,
|
||||
* which usually is reserved for TLS encrypted HTTP traffic.
|
||||
*
|
||||
* @return ConnectionSettings A copy of the original object with the new setting applied.
|
||||
*/
|
||||
public function setTlsAlpn(?string $tlsAlpn): ConnectionSettings
|
||||
{
|
||||
$copy = clone $this;
|
||||
|
||||
$copy->tlsAlpn = $tlsAlpn;
|
||||
|
||||
return $copy;
|
||||
}
|
||||
|
||||
public function getTlsAlpn(): ?string
|
||||
{
|
||||
return $this->tlsAlpn;
|
||||
}
|
||||
}
|
||||
118
vendor/php-mqtt/client/src/Contracts/MessageProcessor.php
vendored
Normal file
118
vendor/php-mqtt/client/src/Contracts/MessageProcessor.php
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Contracts;
|
||||
|
||||
use PhpMqtt\Client\ConnectionSettings;
|
||||
use PhpMqtt\Client\Exceptions\ConnectingToBrokerFailedException;
|
||||
use PhpMqtt\Client\Exceptions\InvalidMessageException;
|
||||
use PhpMqtt\Client\Exceptions\MqttClientException;
|
||||
use PhpMqtt\Client\Exceptions\ProtocolViolationException;
|
||||
use PhpMqtt\Client\Message;
|
||||
use PhpMqtt\Client\Subscription;
|
||||
|
||||
/**
|
||||
* Implementations of this interface provide message parsing capabilities.
|
||||
* Services of this type are used by the {@see MqttClient} to implement multiple protocol versions.
|
||||
*
|
||||
* @package PhpMqtt\Client\Contracts
|
||||
*/
|
||||
interface MessageProcessor
|
||||
{
|
||||
/**
|
||||
* Try to parse a message from the incoming buffer. If a message could be parsed successfully,
|
||||
* the given message parameter is set to the parsed message and the result is true.
|
||||
* If no message could be parsed, the result is false and the required bytes parameter indicates
|
||||
* how many bytes are missing for the message to be complete. If this parameter is set to -1,
|
||||
* it means we have no (or not yet) knowledge about the required bytes.
|
||||
*/
|
||||
public function tryFindMessageInBuffer(string $buffer, int $bufferLength, ?string &$message = null, int &$requiredBytes = -1): bool;
|
||||
|
||||
/**
|
||||
* Parses and validates the given message based on its message type and contents.
|
||||
* If no valid message could be found in the data, and no further action is required by the caller,
|
||||
* null is returned.
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
* @throws ProtocolViolationException
|
||||
* @throws MqttClientException
|
||||
*/
|
||||
public function parseAndValidateMessage(string $message): ?Message;
|
||||
|
||||
/**
|
||||
* Builds a connect message from the given connection settings, taking the protocol
|
||||
* specifics into account.
|
||||
*/
|
||||
public function buildConnectMessage(ConnectionSettings $connectionSettings, bool $useCleanSession = false): string;
|
||||
|
||||
/**
|
||||
* Builds a ping request message.
|
||||
*/
|
||||
public function buildPingRequestMessage(): string;
|
||||
|
||||
/**
|
||||
* Builds a ping response message.
|
||||
*/
|
||||
public function buildPingResponseMessage(): string;
|
||||
|
||||
/**
|
||||
* Builds a disconnect message.
|
||||
*/
|
||||
public function buildDisconnectMessage(): string;
|
||||
|
||||
/**
|
||||
* Builds a subscribe message from the given parameters.
|
||||
*
|
||||
* @param Subscription[] $subscriptions
|
||||
*/
|
||||
public function buildSubscribeMessage(int $messageId, array $subscriptions, bool $isDuplicate = false): string;
|
||||
|
||||
/**
|
||||
* Builds an unsubscribe message from the given parameters.
|
||||
*
|
||||
* @param string[] $topics
|
||||
*/
|
||||
public function buildUnsubscribeMessage(int $messageId, array $topics, bool $isDuplicate = false): string;
|
||||
|
||||
/**
|
||||
* Builds a publish message based on the given parameters.
|
||||
*/
|
||||
public function buildPublishMessage(
|
||||
string $topic,
|
||||
string $message,
|
||||
int $qualityOfService,
|
||||
bool $retain,
|
||||
?int $messageId = null,
|
||||
bool $isDuplicate = false,
|
||||
): string;
|
||||
|
||||
/**
|
||||
* Builds a publish acknowledgement for the given message identifier.
|
||||
*/
|
||||
public function buildPublishAcknowledgementMessage(int $messageId): string;
|
||||
|
||||
/**
|
||||
* Builds a publish received message for the given message identifier.
|
||||
*/
|
||||
public function buildPublishReceivedMessage(int $messageId): string;
|
||||
|
||||
/**
|
||||
* Builds a publish release message for the given message identifier.
|
||||
*/
|
||||
public function buildPublishReleaseMessage(int $messageId): string;
|
||||
|
||||
/**
|
||||
* Builds a publish complete message for the given message identifier.
|
||||
*/
|
||||
public function buildPublishCompleteMessage(int $messageId): string;
|
||||
|
||||
/**
|
||||
* Handles the connect acknowledgement received from the broker. Exits normally if the
|
||||
* connection could be established successfully according to the response. Throws an
|
||||
* exception if the broker responded with an error.
|
||||
*
|
||||
* @throws ConnectingToBrokerFailedException
|
||||
*/
|
||||
public function handleConnectAcknowledgement(string $message): void;
|
||||
}
|
||||
266
vendor/php-mqtt/client/src/Contracts/MqttClient.php
vendored
Normal file
266
vendor/php-mqtt/client/src/Contracts/MqttClient.php
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Contracts;
|
||||
|
||||
use PhpMqtt\Client\ConnectionSettings;
|
||||
use PhpMqtt\Client\Exceptions\ConfigurationInvalidException;
|
||||
use PhpMqtt\Client\Exceptions\ConnectingToBrokerFailedException;
|
||||
use PhpMqtt\Client\Exceptions\DataTransferException;
|
||||
use PhpMqtt\Client\Exceptions\InvalidMessageException;
|
||||
use PhpMqtt\Client\Exceptions\MqttClientException;
|
||||
use PhpMqtt\Client\Exceptions\ProtocolViolationException;
|
||||
use PhpMqtt\Client\Exceptions\RepositoryException;
|
||||
|
||||
/**
|
||||
* An interface for the MQTT client.
|
||||
*
|
||||
* @package PhpMqtt\Client\Contracts
|
||||
*/
|
||||
interface MqttClient
|
||||
{
|
||||
/**
|
||||
* Connect to the MQTT broker using the given settings.
|
||||
* If no custom settings are passed, the client will use the default settings.
|
||||
* See {@see ConnectionSettings} for more details about the defaults.
|
||||
*
|
||||
* @throws ConfigurationInvalidException
|
||||
* @throws ConnectingToBrokerFailedException
|
||||
*/
|
||||
public function connect(?ConnectionSettings $settings = null, bool $useCleanSession = false): void;
|
||||
|
||||
/**
|
||||
* Sends a disconnect message to the broker and closes the socket.
|
||||
*
|
||||
* @throws DataTransferException
|
||||
*/
|
||||
public function disconnect(): void;
|
||||
|
||||
/**
|
||||
* Returns an indication, whether the client is supposed to be connected already or not.
|
||||
*
|
||||
* Note: the result of this method should be used carefully, since we can only detect a
|
||||
* closed socket once we try to send or receive data. Therefore, this method only gives
|
||||
* an indication whether the client is in a connected state or not.
|
||||
*
|
||||
* This information may be useful in applications where multiple parts use the client.
|
||||
*/
|
||||
public function isConnected(): bool;
|
||||
|
||||
/**
|
||||
* Publishes the given message on the given topic. If the additional quality of service
|
||||
* and retention flags are set, the message will be published using these settings.
|
||||
*
|
||||
* @throws DataTransferException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function publish(string $topic, string $message, int $qualityOfService = 0, bool $retain = false): void;
|
||||
|
||||
/**
|
||||
* Subscribe to the given topic with the given quality of service.
|
||||
*
|
||||
* The subscription callback is passed the topic as first and the message as second
|
||||
* parameter. A third parameter indicates whether the received message has been sent
|
||||
* because it was retained by the broker. A fourth parameter contains matched topic wildcards.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->subscribe(
|
||||
* '/foo/bar/+',
|
||||
* function (string $topic, string $message, bool $retained, array $matchedWildcards) use ($logger) {
|
||||
* $logger->info("Received {retained} message on topic [{topic}]: {message}", [
|
||||
* 'topic' => $topic,
|
||||
* 'message' => $message,
|
||||
* 'retained' => $retained ? 'retained' : 'live'
|
||||
* ]);
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* If no callback is passed, a subscription will still be made. Received messages are delivered only to
|
||||
* event handlers for received messages though.
|
||||
*
|
||||
* @throws DataTransferException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function subscribe(string $topicFilter, ?callable $callback = null, int $qualityOfService = 0): void;
|
||||
|
||||
/**
|
||||
* Unsubscribe from the given topic.
|
||||
*
|
||||
* @throws DataTransferException
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function unsubscribe(string $topicFilter): void;
|
||||
|
||||
/**
|
||||
* Sets the interrupted signal. Doing so instructs the client to exit the loop, if it is
|
||||
* actually looping.
|
||||
*
|
||||
* Sending multiple interrupt signals has no effect, unless the client exits the loop,
|
||||
* which resets the signal for another loop.
|
||||
*/
|
||||
public function interrupt(): void;
|
||||
|
||||
/**
|
||||
* Runs an event loop that handles messages from the server and calls the registered
|
||||
* callbacks for published messages.
|
||||
*
|
||||
* If the second parameter is provided, the loop will exit as soon as all
|
||||
* queues are empty. This means there may be no open subscriptions,
|
||||
* no pending messages as well as acknowledgments and no pending unsubscribe requests.
|
||||
*
|
||||
* The third parameter will, if set, lead to a forceful exit after the specified
|
||||
* amount of seconds, but only if the second parameter is set to true. This basically
|
||||
* means that if we wait for all pending messages to be acknowledged, we only wait
|
||||
* a maximum of $queueWaitLimit seconds until we give up. We do not exit after the
|
||||
* given amount of time if there are open topic subscriptions though.
|
||||
*
|
||||
* @throws DataTransferException
|
||||
* @throws InvalidMessageException
|
||||
* @throws MqttClientException
|
||||
* @throws ProtocolViolationException
|
||||
*/
|
||||
public function loop(bool $allowSleep = true, bool $exitWhenQueuesEmpty = false, ?int $queueWaitLimit = null): void;
|
||||
|
||||
/**
|
||||
* Runs an event loop iteration that handles messages from the server and calls the registered
|
||||
* callbacks for published messages. Also resends pending messages and calls loop event handlers.
|
||||
*
|
||||
* This method can be used to integrate the MQTT client in another event loop (like ReactPHP or Ratchet).
|
||||
*
|
||||
* Note: To ensure the event handlers called by this method will receive the correct elapsed time,
|
||||
* the caller is responsible to provide the correct starting time of the loop as returned by `microtime(true)`.
|
||||
*
|
||||
* @throws DataTransferException
|
||||
* @throws InvalidMessageException
|
||||
* @throws MqttClientException
|
||||
* @throws ProtocolViolationException
|
||||
*/
|
||||
public function loopOnce(float $loopStartedAt, bool $allowSleep = false, int $sleepMicroseconds = 100000): void;
|
||||
|
||||
/**
|
||||
* Returns the host used by the client to connect to.
|
||||
*/
|
||||
public function getHost(): string;
|
||||
|
||||
/**
|
||||
* Returns the port used by the client to connect to.
|
||||
*/
|
||||
public function getPort(): int;
|
||||
|
||||
/**
|
||||
* Returns the identifier used by the client.
|
||||
*/
|
||||
public function getClientId(): string;
|
||||
|
||||
/**
|
||||
* Returns the total number of received bytes, across reconnects.
|
||||
*/
|
||||
public function getReceivedBytes(): int;
|
||||
|
||||
/**
|
||||
* Returns the total number of sent bytes, across reconnects.
|
||||
*/
|
||||
public function getSentBytes(): int;
|
||||
|
||||
/**
|
||||
* Registers a loop event handler which is called each iteration of the loop.
|
||||
* This event handler can be used for example to interrupt the loop under
|
||||
* certain conditions.
|
||||
*
|
||||
* The loop event handler is passed the MQTT client instance as first and
|
||||
* the elapsed time which the loop is already running for as second
|
||||
* parameter. The elapsed time is a float containing seconds.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerLoopEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* float $elapsedTime
|
||||
* ) use ($logger) {
|
||||
* $logger->info("Running for [{$elapsedTime}] seconds already.");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerLoopEventHandler(\Closure $callback): MqttClient;
|
||||
|
||||
/**
|
||||
* Unregisters a loop event handler which prevents it from being called
|
||||
* in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterLoopEventHandler(?\Closure $callback = null): MqttClient;
|
||||
|
||||
/**
|
||||
* Registers a loop event handler which is called when a message is published.
|
||||
*
|
||||
* The loop event handler is passed the MQTT client as first, the topic as
|
||||
* second and the message as third parameter. As fourth parameter, the
|
||||
* message identifier will be passed. The QoS level as well as the retained
|
||||
* flag will also be passed as fifth and sixth parameters.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerPublishEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* string $topic,
|
||||
* string $message,
|
||||
* int $messageId,
|
||||
* int $qualityOfService,
|
||||
* bool $retain
|
||||
* ) use ($logger) {
|
||||
* $logger->info("Received message on topic [{$topic}]: {$message}");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerPublishEventHandler(\Closure $callback): MqttClient;
|
||||
|
||||
/**
|
||||
* Unregisters a publish event handler which prevents it from being called
|
||||
* in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterPublishEventHandler(?\Closure $callback = null): MqttClient;
|
||||
|
||||
/**
|
||||
* Registers an event handler which is called when a message is received from the broker.
|
||||
*
|
||||
* The message received event handler is passed the MQTT client as first, the topic as
|
||||
* second and the message as third parameter. As fourth parameter, the QoS level will be
|
||||
* passed and the retained flag as fifth.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
* $mqtt->registerReceivedMessageEventHandler(function (
|
||||
* MqttClient $mqtt,
|
||||
* string $topic,
|
||||
* string $message,
|
||||
* int $qualityOfService,
|
||||
* bool $retained
|
||||
* ) use ($logger) {
|
||||
* $logger->info("Received message on topic [{$topic}]: {$message}");
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Multiple event handlers can be registered at the same time.
|
||||
*/
|
||||
public function registerMessageReceivedEventHandler(\Closure $callback): MqttClient;
|
||||
|
||||
/**
|
||||
* Unregisters a message received event handler which prevents it from being called in the future.
|
||||
*
|
||||
* This does not affect other registered event handlers. It is possible
|
||||
* to unregister all registered event handlers by passing null as callback.
|
||||
*/
|
||||
public function unregisterMessageReceivedEventHandler(?\Closure $callback = null): MqttClient;
|
||||
}
|
||||
143
vendor/php-mqtt/client/src/Contracts/Repository.php
vendored
Normal file
143
vendor/php-mqtt/client/src/Contracts/Repository.php
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Contracts;
|
||||
|
||||
use DateTime;
|
||||
use PhpMqtt\Client\Exceptions\PendingMessageAlreadyExistsException;
|
||||
use PhpMqtt\Client\Exceptions\PendingMessageNotFoundException;
|
||||
use PhpMqtt\Client\Exceptions\RepositoryException;
|
||||
use PhpMqtt\Client\PendingMessage;
|
||||
use PhpMqtt\Client\Subscription;
|
||||
|
||||
/**
|
||||
* Implementations of this interface provide storage capabilities to an MQTT client.
|
||||
*
|
||||
* Services of this type have three primary goals:
|
||||
* 1. Providing and keeping track of message identifiers, since they must be unique
|
||||
* within the message flow (i.e. there may not be duplicates of different messages
|
||||
* at the same time).
|
||||
* 2. Storing and keeping track of subscriptions, which is especially necessary in case
|
||||
* of persisted sessions.
|
||||
* 3. Storing and keeping track of pending messages (i.e. sent messages, which have not
|
||||
* been acknowledged yet by the broker).
|
||||
*
|
||||
* @package PhpMqtt\Client\Contracts
|
||||
*/
|
||||
interface Repository
|
||||
{
|
||||
/**
|
||||
* Re-initializes the repository by deleting all persisted data and restoring the original state,
|
||||
* which was given when the repository was first created. This is used when a clean session
|
||||
* is requested by a client during connection.
|
||||
*/
|
||||
public function reset(): void;
|
||||
|
||||
/**
|
||||
* Returns a new message id. The message id might have been used before,
|
||||
* but it is currently not being used (i.e. in a resend queue).
|
||||
*
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
public function newMessageId(): int;
|
||||
|
||||
/**
|
||||
* Returns the number of pending outgoing messages.
|
||||
*/
|
||||
public function countPendingOutgoingMessages(): int;
|
||||
|
||||
/**
|
||||
* Gets a pending outgoing message with the given message identifier, if found.
|
||||
*/
|
||||
public function getPendingOutgoingMessage(int $messageId): ?PendingMessage;
|
||||
|
||||
/**
|
||||
* Gets a list of pending outgoing messages last sent before the given date time.
|
||||
*
|
||||
* If date time is `null`, all pending messages are returned.
|
||||
*
|
||||
* The messages are returned in the same order they were added to the repository.
|
||||
*
|
||||
* @return PendingMessage[]
|
||||
*/
|
||||
public function getPendingOutgoingMessagesLastSentBefore(?DateTime $dateTime = null): array;
|
||||
|
||||
/**
|
||||
* Adds a pending outgoing message to the repository.
|
||||
*
|
||||
* @throws PendingMessageAlreadyExistsException
|
||||
*/
|
||||
public function addPendingOutgoingMessage(PendingMessage $message): void;
|
||||
|
||||
/**
|
||||
* Marks an existing pending outgoing published message as received in the repository.
|
||||
*
|
||||
* If the message does not exists, an exception is thrown,
|
||||
* otherwise `true` is returned if the message was marked as received, and `false`
|
||||
* in case it was already marked as received.
|
||||
*
|
||||
* @throws PendingMessageNotFoundException
|
||||
*/
|
||||
public function markPendingOutgoingPublishedMessageAsReceived(int $messageId): bool;
|
||||
|
||||
/**
|
||||
* Removes a pending outgoing message from the repository.
|
||||
*
|
||||
* If a pending message with the given identifier is found and
|
||||
* successfully removed from the repository, `true` is returned.
|
||||
* Otherwise `false` will be returned.
|
||||
*/
|
||||
public function removePendingOutgoingMessage(int $messageId): bool;
|
||||
|
||||
/**
|
||||
* Returns the number of pending incoming messages.
|
||||
*/
|
||||
public function countPendingIncomingMessages(): int;
|
||||
|
||||
/**
|
||||
* Gets a pending incoming message with the given message identifier, if found.
|
||||
*/
|
||||
public function getPendingIncomingMessage(int $messageId): ?PendingMessage;
|
||||
|
||||
/**
|
||||
* Adds a pending outgoing message to the repository.
|
||||
*
|
||||
* @throws PendingMessageAlreadyExistsException
|
||||
*/
|
||||
public function addPendingIncomingMessage(PendingMessage $message): void;
|
||||
|
||||
/**
|
||||
* Removes a pending incoming message from the repository.
|
||||
*
|
||||
* If a pending message with the given identifier is found and
|
||||
* successfully removed from the repository, `true` is returned.
|
||||
* Otherwise `false` will be returned.
|
||||
*/
|
||||
public function removePendingIncomingMessage(int $messageId): bool;
|
||||
|
||||
/**
|
||||
* Returns the number of registered subscriptions.
|
||||
*/
|
||||
public function countSubscriptions(): int;
|
||||
|
||||
/**
|
||||
* Adds a subscription to the repository.
|
||||
*/
|
||||
public function addSubscription(Subscription $subscription): void;
|
||||
|
||||
/**
|
||||
* Gets all subscriptions matching the given topic.
|
||||
*
|
||||
* @return Subscription[]
|
||||
*/
|
||||
public function getSubscriptionsMatchingTopic(string $topicName): array;
|
||||
|
||||
/**
|
||||
* Removes the subscription with the given topic filter from the repository.
|
||||
*
|
||||
* Returns `true` if a topic subscription existed and has been removed.
|
||||
* Otherwise, `false` is returned.
|
||||
*/
|
||||
public function removeSubscription(string $topicFilter): bool;
|
||||
}
|
||||
24
vendor/php-mqtt/client/src/Exceptions/ClientNotConnectedToBrokerException.php
vendored
Normal file
24
vendor/php-mqtt/client/src/Exceptions/ClientNotConnectedToBrokerException.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client is not connected to a broker and tries
|
||||
* to perform an action which requires a connection (e.g. publish or subscribe).
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class ClientNotConnectedToBrokerException extends DataTransferException
|
||||
{
|
||||
public const EXCEPTION_CONNECTION_LOST = 0300;
|
||||
|
||||
/**
|
||||
* ClientNotConnectedToBrokerException constructor.
|
||||
*/
|
||||
public function __construct(string $error)
|
||||
{
|
||||
parent::__construct(self::EXCEPTION_CONNECTION_LOST, $error);
|
||||
}
|
||||
}
|
||||
15
vendor/php-mqtt/client/src/Exceptions/ConfigurationInvalidException.php
vendored
Normal file
15
vendor/php-mqtt/client/src/Exceptions/ConfigurationInvalidException.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client has been misconfigured or wrong connection
|
||||
* settings are being used.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class ConfigurationInvalidException extends MqttClientException
|
||||
{
|
||||
}
|
||||
54
vendor/php-mqtt/client/src/Exceptions/ConnectingToBrokerFailedException.php
vendored
Normal file
54
vendor/php-mqtt/client/src/Exceptions/ConnectingToBrokerFailedException.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client could not connect to the broker.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class ConnectingToBrokerFailedException extends MqttClientException
|
||||
{
|
||||
public const EXCEPTION_CONNECTION_FAILED = 0001;
|
||||
public const EXCEPTION_CONNECTION_PROTOCOL_VERSION = 0002;
|
||||
public const EXCEPTION_CONNECTION_IDENTIFIER_REJECTED = 0003;
|
||||
public const EXCEPTION_CONNECTION_BROKER_UNAVAILABLE = 0004;
|
||||
public const EXCEPTION_CONNECTION_INVALID_CREDENTIALS = 0005;
|
||||
public const EXCEPTION_CONNECTION_UNAUTHORIZED = 0006;
|
||||
public const EXCEPTION_CONNECTION_SOCKET_ERROR = 1000;
|
||||
public const EXCEPTION_CONNECTION_TLS_ERROR = 2000;
|
||||
|
||||
/**
|
||||
* ConnectingToBrokerFailedException constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
int $code,
|
||||
string $error,
|
||||
private ?string $connectionErrorCode = null,
|
||||
private ?string $connectionErrorMessage = null,
|
||||
)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf('[%s] Establishing a connection to the MQTT broker failed: %s', $code, $error),
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the connection error code.
|
||||
*/
|
||||
public function getConnectionErrorCode(): ?string
|
||||
{
|
||||
return $this->connectionErrorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the connection error message.
|
||||
*/
|
||||
public function getConnectionErrorMessage(): ?string
|
||||
{
|
||||
return $this->connectionErrorMessage;
|
||||
}
|
||||
}
|
||||
27
vendor/php-mqtt/client/src/Exceptions/DataTransferException.php
vendored
Normal file
27
vendor/php-mqtt/client/src/Exceptions/DataTransferException.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client encountered an error while transferring data.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class DataTransferException extends MqttClientException
|
||||
{
|
||||
public const EXCEPTION_TX_DATA = 0101;
|
||||
public const EXCEPTION_RX_DATA = 0102;
|
||||
|
||||
/**
|
||||
* DataTransferException constructor.
|
||||
*/
|
||||
public function __construct(int $code, string $error)
|
||||
{
|
||||
parent::__construct(
|
||||
sprintf('[%s] Transferring data over socket failed: %s', $code, $error),
|
||||
$code
|
||||
);
|
||||
}
|
||||
}
|
||||
14
vendor/php-mqtt/client/src/Exceptions/InvalidMessageException.php
vendored
Normal file
14
vendor/php-mqtt/client/src/Exceptions/InvalidMessageException.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client encounters an invalid message.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class InvalidMessageException extends MqttClientException
|
||||
{
|
||||
}
|
||||
29
vendor/php-mqtt/client/src/Exceptions/MqttClientException.php
vendored
Normal file
29
vendor/php-mqtt/client/src/Exceptions/MqttClientException.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client error occurs.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class MqttClientException extends \Exception
|
||||
{
|
||||
/**
|
||||
* MqttClientException constructor.
|
||||
*/
|
||||
public function __construct(string $message = '', int $code = 0, ?\Throwable $parentException = null)
|
||||
{
|
||||
if (empty($message)) {
|
||||
parent::__construct(
|
||||
sprintf('[%s] The MQTT client encountered an error.', $code),
|
||||
$code,
|
||||
$parentException
|
||||
);
|
||||
} else {
|
||||
parent::__construct($message, $code, $parentException);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
vendor/php-mqtt/client/src/Exceptions/PendingMessageAlreadyExistsException.php
vendored
Normal file
21
vendor/php-mqtt/client/src/Exceptions/PendingMessageAlreadyExistsException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if a pending message with the same packet identifier is still pending.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class PendingMessageAlreadyExistsException extends RepositoryException
|
||||
{
|
||||
/**
|
||||
* PendingMessageAlreadyExistsException constructor.
|
||||
*/
|
||||
public function __construct(int $messageId)
|
||||
{
|
||||
parent::__construct(sprintf('A pending message with the message identifier [%s] exists already.', $messageId));
|
||||
}
|
||||
}
|
||||
21
vendor/php-mqtt/client/src/Exceptions/PendingMessageNotFoundException.php
vendored
Normal file
21
vendor/php-mqtt/client/src/Exceptions/PendingMessageNotFoundException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if a pending message with the same packet identifier is not found.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class PendingMessageNotFoundException extends RepositoryException
|
||||
{
|
||||
/**
|
||||
* PendingMessageNotFoundException constructor.
|
||||
*/
|
||||
public function __construct(int $messageId)
|
||||
{
|
||||
parent::__construct(sprintf('No pending message with the message identifier [%s].', $messageId));
|
||||
}
|
||||
}
|
||||
21
vendor/php-mqtt/client/src/Exceptions/ProtocolNotSupportedException.php
vendored
Normal file
21
vendor/php-mqtt/client/src/Exceptions/ProtocolNotSupportedException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an invalid MQTT version is given.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class ProtocolNotSupportedException extends MqttClientException
|
||||
{
|
||||
/**
|
||||
* ProtocolNotSupportedException constructor.
|
||||
*/
|
||||
public function __construct(string $protocol)
|
||||
{
|
||||
parent::__construct(sprintf('The given protocol version [%s] is not supported.', $protocol));
|
||||
}
|
||||
}
|
||||
21
vendor/php-mqtt/client/src/Exceptions/ProtocolViolationException.php
vendored
Normal file
21
vendor/php-mqtt/client/src/Exceptions/ProtocolViolationException.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client encountered a protocol violation.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class ProtocolViolationException extends MqttClientException
|
||||
{
|
||||
/**
|
||||
* ProtocolViolationException constructor.
|
||||
*/
|
||||
public function __construct(string $error)
|
||||
{
|
||||
parent::__construct(sprintf('Protocol violation: %s.', $error));
|
||||
}
|
||||
}
|
||||
14
vendor/php-mqtt/client/src/Exceptions/RepositoryException.php
vendored
Normal file
14
vendor/php-mqtt/client/src/Exceptions/RepositoryException.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Exceptions;
|
||||
|
||||
/**
|
||||
* Exception to be thrown if an MQTT client repository encounters an error.
|
||||
*
|
||||
* @package PhpMqtt\Client\Exceptions
|
||||
*/
|
||||
class RepositoryException extends MqttClientException
|
||||
{
|
||||
}
|
||||
166
vendor/php-mqtt/client/src/Logger.php
vendored
Normal file
166
vendor/php-mqtt/client/src/Logger.php
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* Wrapper for another logger. Drops logged messages if no logger is available.
|
||||
*
|
||||
* @internal This class is not part of the public API of the library and used internally only.
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class Logger implements LoggerInterface
|
||||
{
|
||||
/**
|
||||
* Logger constructor.
|
||||
*
|
||||
* @param LoggerInterface|null $logger
|
||||
*/
|
||||
public function __construct(
|
||||
private string $host,
|
||||
private int $port,
|
||||
private string $clientId,
|
||||
private ?LoggerInterface $logger = null,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* System is unusable.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function emergency($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::EMERGENCY, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action must be taken immediately.
|
||||
*
|
||||
* Example: Entire website down, database unavailable, etc. This should
|
||||
* trigger the SMS alerts and wake you up.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function alert($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::ALERT, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Critical conditions.
|
||||
*
|
||||
* Example: Application component unavailable, unexpected exception.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function critical($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::CRITICAL, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime errors that do not require immediate action but should typically
|
||||
* be logged and monitored.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function error($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::ERROR, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exceptional occurrences that are not errors.
|
||||
*
|
||||
* Example: Use of deprecated APIs, poor use of an API, undesirable things
|
||||
* that are not necessarily wrong.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function warning($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::WARNING, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal but significant events.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function notice($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::NOTICE, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interesting events.
|
||||
*
|
||||
* Example: User logs in, SQL logs.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function info($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::INFO, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detailed debug information.
|
||||
*
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function debug($message, array $context = []): void
|
||||
{
|
||||
$this->log(LogLevel::DEBUG, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs with an arbitrary level.
|
||||
*
|
||||
* @param mixed $level
|
||||
* @param string $message
|
||||
* @param array $context
|
||||
*/
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
if ($this->logger === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->log($level, $this->wrapLogMessage($message), $this->mergeContext($context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given log message by prepending the client id and broker.
|
||||
*/
|
||||
protected function wrapLogMessage(string $message): string
|
||||
{
|
||||
return 'MQTT [{host}:{port}] [{clientId}] ' . $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds global context like host, port and client id to the log context.
|
||||
*/
|
||||
protected function mergeContext(array $context): array
|
||||
{
|
||||
return array_merge([
|
||||
'host' => $this->host,
|
||||
'port' => $this->port,
|
||||
'clientId' => $this->clientId,
|
||||
], $context);
|
||||
}
|
||||
}
|
||||
105
vendor/php-mqtt/client/src/Message.php
vendored
Normal file
105
vendor/php-mqtt/client/src/Message.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
use PhpMqtt\Client\Contracts\MessageProcessor;
|
||||
use PhpMqtt\Client\Contracts\MqttClient;
|
||||
|
||||
/**
|
||||
* Describes an action which is supposed to be performed after receiving a message.
|
||||
* Objects of this type are used by the {@see MessageProcessor} to instruct the
|
||||
* {@see MqttClient} about required steps to take.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
private ?int $messageId = null;
|
||||
private ?string $topic = null;
|
||||
private ?string $content = null;
|
||||
|
||||
/** @var int[] */
|
||||
private array $acknowledgedQualityOfServices = [];
|
||||
|
||||
/**
|
||||
* Message constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
private MessageType $type,
|
||||
private int $qualityOfService = 0,
|
||||
private bool $retained = false,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
public function getType(): MessageType
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getQualityOfService(): int
|
||||
{
|
||||
return $this->qualityOfService;
|
||||
}
|
||||
|
||||
public function getRetained(): bool
|
||||
{
|
||||
return $this->retained;
|
||||
}
|
||||
|
||||
public function getMessageId(): ?int
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
public function setMessageId(?int $messageId): Message
|
||||
{
|
||||
$this->messageId = $messageId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTopic(): ?string
|
||||
{
|
||||
return $this->topic;
|
||||
}
|
||||
|
||||
public function setTopic(?string $topic): Message
|
||||
{
|
||||
$this->topic = $topic;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setContent(?string $content): Message
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]
|
||||
*/
|
||||
public function getAcknowledgedQualityOfServices(): array
|
||||
{
|
||||
return $this->acknowledgedQualityOfServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $acknowledgedQualityOfServices
|
||||
*/
|
||||
public function setAcknowledgedQualityOfServices(array $acknowledgedQualityOfServices): Message
|
||||
{
|
||||
$this->acknowledgedQualityOfServices = $acknowledgedQualityOfServices;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
32
vendor/php-mqtt/client/src/MessageProcessors/BaseMessageProcessor.php
vendored
Normal file
32
vendor/php-mqtt/client/src/MessageProcessors/BaseMessageProcessor.php
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\MessageProcessors;
|
||||
|
||||
use PhpMqtt\Client\Concerns\TranscodesData;
|
||||
use PhpMqtt\Client\Concerns\WorksWithBuffers;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* This message processor serves as base for other message processors, providing
|
||||
* default implementations for some methods.
|
||||
*
|
||||
* @package PhpMqtt\Client\MessageProcessors
|
||||
*/
|
||||
abstract class BaseMessageProcessor
|
||||
{
|
||||
use TranscodesData;
|
||||
use WorksWithBuffers;
|
||||
|
||||
public const QOS_AT_MOST_ONCE = 0;
|
||||
public const QOS_AT_LEAST_ONCE = 1;
|
||||
public const QOS_EXACTLY_ONCE = 2;
|
||||
|
||||
/**
|
||||
* BaseMessageProcessor constructor.
|
||||
*/
|
||||
public function __construct(protected LoggerInterface $logger)
|
||||
{
|
||||
}
|
||||
}
|
||||
76
vendor/php-mqtt/client/src/MessageProcessors/Mqtt311MessageProcessor.php
vendored
Normal file
76
vendor/php-mqtt/client/src/MessageProcessors/Mqtt311MessageProcessor.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\MessageProcessors;
|
||||
|
||||
use PhpMqtt\Client\Exceptions\InvalidMessageException;
|
||||
use PhpMqtt\Client\Exceptions\ProtocolViolationException;
|
||||
use PhpMqtt\Client\Message;
|
||||
use PhpMqtt\Client\MessageType;
|
||||
|
||||
/**
|
||||
* This message processor implements the MQTT protocol version 3.1.1.
|
||||
*
|
||||
* @package PhpMqtt\Client\MessageProcessors
|
||||
*/
|
||||
class Mqtt311MessageProcessor extends Mqtt31MessageProcessor
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getEncodedProtocolNameAndVersion(): string
|
||||
{
|
||||
return $this->buildLengthPrefixedString('MQTT') . chr(0x04); // protocol version (4)
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function parseAndValidateMessage(string $message): ?Message
|
||||
{
|
||||
$result = parent::parseAndValidateMessage($message);
|
||||
|
||||
if ($this->isPublishMessageWithNullCharacter($result)) {
|
||||
throw new ProtocolViolationException('The broker sent us a message with the forbidden unicode character U+0000.');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function parseAndValidateSubscribeAcknowledgementMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) < 3) {
|
||||
$this->logger->notice('Received invalid subscribe acknowledgement from the broker.');
|
||||
throw new InvalidMessageException('Received invalid subscribe acknowledgement from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
// Parse and validate the QoS acknowledgements.
|
||||
$acknowledgements = array_map('ord', str_split($data));
|
||||
foreach ($acknowledgements as $acknowledgement) {
|
||||
if (!in_array($acknowledgement, [0, 1, 2, 128])) {
|
||||
throw new InvalidMessageException('Received subscribe acknowledgement with invalid QoS values from the broker.');
|
||||
}
|
||||
}
|
||||
|
||||
return (new Message(MessageType::SUBSCRIBE_ACKNOWLEDGEMENT()))
|
||||
->setMessageId($messageId)
|
||||
->setAcknowledgedQualityOfServices($acknowledgements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given message is a PUBLISH message and contains the unicode null character U+0000.
|
||||
*/
|
||||
private function isPublishMessageWithNullCharacter(?Message $message): bool
|
||||
{
|
||||
return $message !== null
|
||||
&& $message->getType()->equals(MessageType::PUBLISH())
|
||||
&& $message->getContent() !== null
|
||||
&& preg_match('/\x{0000}/u', $message->getContent());
|
||||
}
|
||||
}
|
||||
712
vendor/php-mqtt/client/src/MessageProcessors/Mqtt31MessageProcessor.php
vendored
Normal file
712
vendor/php-mqtt/client/src/MessageProcessors/Mqtt31MessageProcessor.php
vendored
Normal file
@@ -0,0 +1,712 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\MessageProcessors;
|
||||
|
||||
use PhpMqtt\Client\ConnectionSettings;
|
||||
use PhpMqtt\Client\Contracts\MessageProcessor;
|
||||
use PhpMqtt\Client\Exceptions\ConnectingToBrokerFailedException;
|
||||
use PhpMqtt\Client\Exceptions\InvalidMessageException;
|
||||
use PhpMqtt\Client\Exceptions\ProtocolViolationException;
|
||||
use PhpMqtt\Client\Message;
|
||||
use PhpMqtt\Client\MessageType;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* This message processor implements the MQTT protocol version 3.1.
|
||||
*
|
||||
* @package PhpMqtt\Client\MessageProcessors
|
||||
*/
|
||||
class Mqtt31MessageProcessor extends BaseMessageProcessor implements MessageProcessor
|
||||
{
|
||||
/**
|
||||
* Creates a new message processor instance which supports version 3.1 of the MQTT protocol.
|
||||
*/
|
||||
public function __construct(private string $clientId, LoggerInterface $logger)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function tryFindMessageInBuffer(string $buffer, int $bufferLength, ?string &$message = null, int &$requiredBytes = -1): bool
|
||||
{
|
||||
// If we received no input, we can return immediately without doing work.
|
||||
if ($bufferLength === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we received not at least the fixed header with one length indicating byte,
|
||||
// we know that there can't be a valid message in the buffer. So we return early.
|
||||
if ($bufferLength < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the second byte of the message to get the remaining length.
|
||||
// If the continuation bit (8) is set on the length byte, another byte will be read as length.
|
||||
$byteIndex = 1;
|
||||
$remainingLength = 0;
|
||||
$multiplier = 1;
|
||||
do {
|
||||
// If the buffer has no more data, but we need to read more for the length header,
|
||||
// we cannot give useful information about the remaining length and exit early.
|
||||
if ($byteIndex + 1 > $bufferLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// There can me a maximum of four bytes for the package length, which means we cann opt-out
|
||||
// when reaching the 6th byte in the buffer. This is only a safety measure in case the broker
|
||||
// is sending invalid messages. Normally, the loop exits on its own.
|
||||
if ($byteIndex >= 6) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise, we can take seven bits to calculate the length and the remaining eighth bit
|
||||
// as continuation bit.
|
||||
$digit = ord($buffer[$byteIndex]);
|
||||
$remainingLength += ($digit & 127) * $multiplier;
|
||||
$multiplier *= 128;
|
||||
$byteIndex++;
|
||||
} while (($digit & 128) !== 0);
|
||||
|
||||
// At this point, we can now tell whether the remaining length amount of bytes are available
|
||||
// or not. If not, we return the amount of bytes required for the message to be complete.
|
||||
$requiredBufferLength = $byteIndex + $remainingLength;
|
||||
if ($requiredBufferLength > $bufferLength) {
|
||||
$requiredBytes = $requiredBufferLength;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now that we have a full message in the buffer, we can set the output and return.
|
||||
$message = substr($buffer, 0, $requiredBufferLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildConnectMessage(ConnectionSettings $connectionSettings, bool $useCleanSession = false): string
|
||||
{
|
||||
// The protocol name and version.
|
||||
$buffer = $this->getEncodedProtocolNameAndVersion();
|
||||
|
||||
// Build connection flags based on the connection settings.
|
||||
$buffer .= chr($this->buildConnectionFlags($connectionSettings, $useCleanSession));
|
||||
|
||||
// Encode and add the keep alive interval.
|
||||
$buffer .= chr($connectionSettings->getKeepAliveInterval() >> 8);
|
||||
$buffer .= chr($connectionSettings->getKeepAliveInterval() & 0xff);
|
||||
|
||||
// Encode and add the client identifier.
|
||||
$buffer .= $this->buildLengthPrefixedString($this->clientId);
|
||||
|
||||
// Encode and add the last will topic and message, if configured.
|
||||
if ($connectionSettings->hasLastWill()) {
|
||||
$buffer .= $this->buildLengthPrefixedString($connectionSettings->getLastWillTopic());
|
||||
$buffer .= $this->buildLengthPrefixedString($connectionSettings->getLastWillMessage());
|
||||
}
|
||||
|
||||
// Encode and add the credentials, if configured.
|
||||
if ($connectionSettings->getUsername() !== null) {
|
||||
$buffer .= $this->buildLengthPrefixedString($connectionSettings->getUsername());
|
||||
}
|
||||
if ($connectionSettings->getPassword() !== null) {
|
||||
$buffer .= $this->buildLengthPrefixedString($connectionSettings->getPassword());
|
||||
}
|
||||
|
||||
// The header consists of the message type 0x10 and the length.
|
||||
$header = chr(0x10) . $this->encodeMessageLength(strlen($buffer));
|
||||
|
||||
return $header . $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the encoded protocol name and version, ready to be sent as part of the CONNECT message.
|
||||
*/
|
||||
protected function getEncodedProtocolNameAndVersion(): string
|
||||
{
|
||||
return $this->buildLengthPrefixedString('MQIsdp') . chr(0x03); // protocol version (3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the connection flags from the inputs and settings.
|
||||
*
|
||||
* The bit structure of the connection flags is as follows:
|
||||
* 0 - reserved
|
||||
* 1 - clean session flag
|
||||
* 2 - last will flag
|
||||
* 3 - QoS flag (1)
|
||||
* 4 - QoS flag (2)
|
||||
* 5 - retain last will flag
|
||||
* 6 - password flag
|
||||
* 7 - username flag
|
||||
*
|
||||
* @link http://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect MQTT 3.1 Spec
|
||||
*/
|
||||
protected function buildConnectionFlags(ConnectionSettings $connectionSettings, bool $useCleanSession = false): int
|
||||
{
|
||||
$flags = 0;
|
||||
|
||||
if ($useCleanSession) {
|
||||
$this->logger->debug('Using the [clean session] flag for the connection.');
|
||||
$flags += 1 << 1;
|
||||
}
|
||||
|
||||
if ($connectionSettings->hasLastWill()) {
|
||||
$this->logger->debug('Using the [will] flag for the connection.');
|
||||
$flags += 1 << 2;
|
||||
|
||||
if ($connectionSettings->getLastWillQualityOfService() > self::QOS_AT_MOST_ONCE) {
|
||||
$this->logger->debug('Using last will QoS level [{qos}] for the connection.', [
|
||||
'qos' => $connectionSettings->getLastWillQualityOfService(),
|
||||
]);
|
||||
$flags += $connectionSettings->getLastWillQualityOfService() << 3;
|
||||
}
|
||||
|
||||
if ($connectionSettings->shouldRetainLastWill()) {
|
||||
$this->logger->debug('Using the [retain last will] flag for the connection.');
|
||||
$flags += 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
if ($connectionSettings->getPassword() !== null) {
|
||||
$this->logger->debug('Using the [password] flag for the connection.');
|
||||
$flags += 1 << 6;
|
||||
}
|
||||
|
||||
if ($connectionSettings->getUsername() !== null) {
|
||||
$this->logger->debug('Using the [username] flag for the connection.');
|
||||
$flags += 1 << 7;
|
||||
}
|
||||
|
||||
return $flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function handleConnectAcknowledgement(string $message): void
|
||||
{
|
||||
if (strlen($message) !== 4 || ($messageType = ord($message[0]) >> 4) !== 2) {
|
||||
$this->logger->error('Expected connect acknowledgement; received a different response.', ['messageType' => $messageType ?? null]);
|
||||
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_FAILED,
|
||||
'A connection could not be established. Expected connect acknowledgement; received a different response else.'
|
||||
);
|
||||
}
|
||||
|
||||
$errorCode = ord($message[3]);
|
||||
$logContext = ['errorCode' => sprintf('0x%02X', $errorCode)];
|
||||
|
||||
switch ($errorCode) {
|
||||
case 0x00:
|
||||
$this->logger->info('Connection with broker established successfully.', $logContext);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
$this->logger->error('The broker does not support MQTT v3.1.', $logContext);
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_PROTOCOL_VERSION,
|
||||
'The configured broker does not support MQTT v3.1.'
|
||||
);
|
||||
|
||||
case 0x02:
|
||||
$this->logger->error('The broker rejected the sent identifier.', $logContext);
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_IDENTIFIER_REJECTED,
|
||||
'The configured broker rejected the sent identifier.'
|
||||
);
|
||||
|
||||
case 0x03:
|
||||
$this->logger->error('The broker is currently unavailable.', $logContext);
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_BROKER_UNAVAILABLE,
|
||||
'The configured broker is currently unavailable.'
|
||||
);
|
||||
|
||||
case 0x04:
|
||||
$this->logger->error('The broker reported the credentials as invalid.', $logContext);
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_INVALID_CREDENTIALS,
|
||||
'The configured broker reported the credentials as invalid.'
|
||||
);
|
||||
|
||||
case 0x05:
|
||||
$this->logger->error('The broker responded with unauthorized.', $logContext);
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_UNAUTHORIZED,
|
||||
'The configured broker responded with unauthorized.'
|
||||
);
|
||||
|
||||
default:
|
||||
$this->logger->error('The broker responded with an invalid error code [{errorCode}].', $logContext);
|
||||
throw new ConnectingToBrokerFailedException(
|
||||
ConnectingToBrokerFailedException::EXCEPTION_CONNECTION_FAILED,
|
||||
'The configured broker responded with an invalid error code. A connection could not be established.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a ping request message.
|
||||
*/
|
||||
public function buildPingRequestMessage(): string
|
||||
{
|
||||
// The message consists of the command 0xc0 and the length 0.
|
||||
return chr(0xc0) . chr(0x00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a ping response message.
|
||||
*/
|
||||
public function buildPingResponseMessage(): string
|
||||
{
|
||||
// The message consists of the command 0xd0 and the length 0.
|
||||
return chr(0xd0) . chr(0x00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a disconnect message.
|
||||
*/
|
||||
public function buildDisconnectMessage(): string
|
||||
{
|
||||
// The message consists of the command 0xe0 and the length 0.
|
||||
return chr(0xe0) . chr(0x00);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildSubscribeMessage(int $messageId, array $subscriptions, bool $isDuplicate = false): string
|
||||
{
|
||||
// Encode the message id, it always consists of two bytes.
|
||||
$buffer = $this->encodeMessageId($messageId);
|
||||
|
||||
foreach ($subscriptions as $subscription) {
|
||||
// Encode the topic as length prefixed string.
|
||||
$buffer .= $this->buildLengthPrefixedString($subscription->getTopicFilter());
|
||||
|
||||
// Encode the quality of service level.
|
||||
$buffer .= chr($subscription->getQualityOfServiceLevel());
|
||||
}
|
||||
|
||||
// The header consists of the message type 0x82 and the length.
|
||||
$header = chr(0x82) . $this->encodeMessageLength(strlen($buffer));
|
||||
|
||||
return $header . $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildUnsubscribeMessage(int $messageId, array $topics, bool $isDuplicate = false): string
|
||||
{
|
||||
// Encode the message id, it always consists of two bytes.
|
||||
$buffer = $this->encodeMessageId($messageId);
|
||||
|
||||
foreach ($topics as $topic) {
|
||||
// Encode the topic as length prefixed string.
|
||||
$buffer .= $this->buildLengthPrefixedString($topic);
|
||||
}
|
||||
|
||||
// The header consists of the message type 0xa2 and the length.
|
||||
// Additionally, the first byte may contain the duplicate flag.
|
||||
$command = 0xa2 | ($isDuplicate ? 1 << 3 : 0);
|
||||
$header = chr($command) . $this->encodeMessageLength(strlen($buffer));
|
||||
|
||||
return $header . $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildPublishMessage(
|
||||
string $topic,
|
||||
string $message,
|
||||
int $qualityOfService,
|
||||
bool $retain,
|
||||
?int $messageId = null,
|
||||
bool $isDuplicate = false,
|
||||
): string
|
||||
{
|
||||
// Encode the topic as length prefixed string.
|
||||
$buffer = $this->buildLengthPrefixedString($topic);
|
||||
|
||||
// Encode the message id, if given. It always consists of two bytes.
|
||||
if ($messageId !== null)
|
||||
{
|
||||
$buffer .= $this->encodeMessageId($messageId);
|
||||
}
|
||||
|
||||
// Add the message without encoding.
|
||||
$buffer .= $message;
|
||||
|
||||
// Encode the command with supported flags.
|
||||
$command = 0x30;
|
||||
if ($retain) {
|
||||
$command += 1 << 0;
|
||||
}
|
||||
if ($qualityOfService > self::QOS_AT_MOST_ONCE) {
|
||||
$command += $qualityOfService << 1;
|
||||
}
|
||||
if ($qualityOfService > self::QOS_AT_MOST_ONCE && $isDuplicate) {
|
||||
$command += 1 << 3;
|
||||
}
|
||||
|
||||
// Build the header from the command and the encoded message length.
|
||||
$header = chr($command) . $this->encodeMessageLength(strlen($buffer));
|
||||
|
||||
return $header . $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildPublishAcknowledgementMessage(int $messageId): string
|
||||
{
|
||||
return chr(0x40) . chr(0x02) . $this->encodeMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildPublishReceivedMessage(int $messageId): string
|
||||
{
|
||||
return chr(0x50) . chr(0x02) . $this->encodeMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildPublishReleaseMessage(int $messageId): string
|
||||
{
|
||||
return chr(0x62) . chr(0x02) . $this->encodeMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function buildPublishCompleteMessage(int $messageId): string
|
||||
{
|
||||
return chr(0x70) . chr(0x02) . $this->encodeMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function parseAndValidateMessage(string $message): ?Message
|
||||
{
|
||||
$qualityOfService = 0;
|
||||
$retained = false;
|
||||
$data = '';
|
||||
$result = $this->tryDecodeMessage($message, $command, $qualityOfService, $retained, $data);
|
||||
|
||||
if ($result === false) {
|
||||
throw new InvalidMessageException('The passed message could not be decoded.');
|
||||
}
|
||||
|
||||
// Ensure the command is supported by this version of the protocol.
|
||||
if ($command <= 0 || $command >= 15) {
|
||||
$this->logger->error('Reserved command received from the broker. Supported are commands (including) 1-14.', [
|
||||
'command' => $command,
|
||||
]);
|
||||
throw new InvalidMessageException('A reserved command has been used in the message.');
|
||||
}
|
||||
|
||||
// Then handle the command accordingly.
|
||||
switch ($command) {
|
||||
case 0x02:
|
||||
throw new ProtocolViolationException('Unexpected connection acknowledgement.');
|
||||
|
||||
case 0x03:
|
||||
return $this->parseAndValidatePublishMessage($data, $qualityOfService, $retained);
|
||||
|
||||
case 0x04:
|
||||
return $this->parseAndValidatePublishAcknowledgementMessage($data);
|
||||
|
||||
case 0x05:
|
||||
return $this->parseAndValidatePublishReceiptMessage($data);
|
||||
|
||||
case 0x06:
|
||||
return $this->parseAndValidatePublishReleaseMessage($data);
|
||||
|
||||
case 0x07:
|
||||
return $this->parseAndValidatePublishCompleteMessage($data);
|
||||
|
||||
case 0x09:
|
||||
return $this->parseAndValidateSubscribeAcknowledgementMessage($data);
|
||||
|
||||
case 0x0b:
|
||||
return $this->parseAndValidateUnsubscribeAcknowledgementMessage($data);
|
||||
|
||||
case 0x0c:
|
||||
return $this->parseAndValidatePingRequestMessage();
|
||||
|
||||
case 0x0d:
|
||||
return $this->parseAndValidatePingAcknowledgementMessage();
|
||||
|
||||
default:
|
||||
$this->logger->debug('Received message with unsupported command [{command}]. Skipping.', ['command' => $command]);
|
||||
break;
|
||||
}
|
||||
|
||||
// If we arrive here, we must have parsed a message with an unsupported type, and it cannot be
|
||||
// very relevant for us. So we return an empty result without information to skip processing.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to decode the given message. If successful, the result is true and the reference
|
||||
* parameters are set accordingly. Otherwise, false is returned and the reference parameters
|
||||
* remain untouched.
|
||||
*/
|
||||
protected function tryDecodeMessage(
|
||||
string $message,
|
||||
?int &$command = null,
|
||||
?int &$qualityOfService = null,
|
||||
?bool &$retained = null,
|
||||
?string &$data = null
|
||||
): bool
|
||||
{
|
||||
// If we received no input, we can return immediately without doing work.
|
||||
if (strlen($message) === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we received not at least the fixed header with one length indicating byte,
|
||||
// we know that there can't be a valid message in the buffer. So we return early.
|
||||
if (strlen($message) < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the first byte of a message (command and flags).
|
||||
$byte = $message[0];
|
||||
$command = (int) (ord($byte) / 16);
|
||||
$qualityOfService = (ord($byte) & 0x06) >> 1;
|
||||
$retained = (bool) (ord($byte) & 0x01);
|
||||
|
||||
// Read the second byte of a message (remaining length).
|
||||
// If the continuation bit (8) is set on the length byte, another byte will be read as length.
|
||||
$byteIndex = 1;
|
||||
$remainingLength = 0;
|
||||
$multiplier = 1;
|
||||
do {
|
||||
// If the buffer has no more data, but we need to read more for the length header,
|
||||
// we cannot give useful information about the remaining length and exit early.
|
||||
if ($byteIndex + 1 > strlen($message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, we can take seven bits to calculate the length and the remaining eighth bit
|
||||
// as continuation bit.
|
||||
$digit = ord($message[$byteIndex]);
|
||||
$remainingLength += ($digit & 127) * $multiplier;
|
||||
$multiplier *= 128;
|
||||
$byteIndex++;
|
||||
} while (($digit & 128) !== 0);
|
||||
|
||||
// At this point, we can now tell whether the remaining length amount of bytes are available
|
||||
// or not. If not, the message is incomplete.
|
||||
$requiredBytes = $byteIndex + $remainingLength;
|
||||
if ($requiredBytes > strlen($message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the output data based on the calculated bytes.
|
||||
$data = substr($message, $byteIndex, $remainingLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received published message. The data contains the whole message except the
|
||||
* fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [topic-length:topic:message]+
|
||||
*/
|
||||
protected function parseAndValidatePublishMessage(string $data, int $qualityOfServiceLevel, bool $retained): ?Message
|
||||
{
|
||||
$topicLength = (ord($data[0]) << 8) + ord($data[1]);
|
||||
$topic = substr($data, 2, $topicLength);
|
||||
$content = substr($data, ($topicLength + 2));
|
||||
|
||||
$message = new Message(MessageType::PUBLISH(), $qualityOfServiceLevel, $retained);
|
||||
|
||||
if ($qualityOfServiceLevel > self::QOS_AT_MOST_ONCE) {
|
||||
if (strlen($content) < 2) {
|
||||
$this->logger->error('Received a message with QoS level [{qos}] without message identifier. Waiting for retransmission.', [
|
||||
'qos' => $qualityOfServiceLevel,
|
||||
]);
|
||||
|
||||
// This message seems to be incomplete or damaged. We ignore it and wait for a retransmission,
|
||||
// which will occur at some point due to QoS level > 0.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Publish messages with a quality of service level > 0 require acknowledgement and therefore
|
||||
// also a message identifier.
|
||||
$messageId = $this->decodeMessageId($this->pop($content, 2));
|
||||
$message->setMessageId($messageId);
|
||||
}
|
||||
|
||||
return $message
|
||||
->setTopic($topic)
|
||||
->setContent($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received publish acknowledgement. The data contains the whole message except
|
||||
* the fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [message-identifier]
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
*/
|
||||
protected function parseAndValidatePublishAcknowledgementMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) !== 2) {
|
||||
$this->logger->notice('Received invalid publish acknowledgement from the broker.');
|
||||
throw new InvalidMessageException('Received invalid publish acknowledgement from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
return (new Message(MessageType::PUBLISH_ACKNOWLEDGEMENT()))
|
||||
->setMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received publish receipt. The data contains the whole message except the
|
||||
* fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [message-identifier]
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
*/
|
||||
protected function parseAndValidatePublishReceiptMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) !== 2) {
|
||||
$this->logger->notice('Received invalid publish receipt from the broker.');
|
||||
throw new InvalidMessageException('Received invalid publish receipt from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
return (new Message(MessageType::PUBLISH_RECEIPT()))
|
||||
->setMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received publish release message. The data contains the whole message except the
|
||||
* fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [message-identifier]
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
*/
|
||||
protected function parseAndValidatePublishReleaseMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) !== 2) {
|
||||
$this->logger->notice('Received invalid publish release from the broker.');
|
||||
throw new InvalidMessageException('Received invalid publish release from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
return (new Message(MessageType::PUBLISH_RELEASE()))
|
||||
->setMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received publish confirmation message. The data contains the whole message except the
|
||||
* fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [message-identifier]
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
*/
|
||||
protected function parseAndValidatePublishCompleteMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) !== 2) {
|
||||
$this->logger->notice('Received invalid publish complete from the broker.');
|
||||
throw new InvalidMessageException('Received invalid complete release from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
return (new Message(MessageType::PUBLISH_COMPLETE()))
|
||||
->setMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received subscription acknowledgement. The data contains the whole message except the
|
||||
* fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [message-identifier:[qos-level]+]
|
||||
*
|
||||
* The order of the received QoS levels matches the order of the sent subscriptions.
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
*/
|
||||
protected function parseAndValidateSubscribeAcknowledgementMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) < 3) {
|
||||
$this->logger->notice('Received invalid subscribe acknowledgement from the broker.');
|
||||
throw new InvalidMessageException('Received invalid subscribe acknowledgement from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
// Parse and validate the QoS acknowledgements.
|
||||
$acknowledgements = array_map('ord', str_split($data));
|
||||
foreach ($acknowledgements as $acknowledgement) {
|
||||
if (!in_array($acknowledgement, [0, 1, 2])) {
|
||||
throw new InvalidMessageException('Received subscribe acknowledgement with invalid QoS values from the broker.');
|
||||
}
|
||||
}
|
||||
|
||||
return (new Message(MessageType::SUBSCRIBE_ACKNOWLEDGEMENT()))
|
||||
->setMessageId($messageId)
|
||||
->setAcknowledgedQualityOfServices($acknowledgements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received unsubscribe acknowledgement. The data contains the whole message except the
|
||||
* fixed header with command and length. The message structure is:
|
||||
*
|
||||
* [message-identifier]
|
||||
*
|
||||
* @throws InvalidMessageException
|
||||
*/
|
||||
protected function parseAndValidateUnsubscribeAcknowledgementMessage(string $data): Message
|
||||
{
|
||||
if (strlen($data) !== 2) {
|
||||
$this->logger->notice('Received invalid unsubscribe acknowledgement from the broker.');
|
||||
throw new InvalidMessageException('Received invalid unsubscribe acknowledgement from the broker.');
|
||||
}
|
||||
|
||||
$messageId = $this->decodeMessageId($this->pop($data, 2));
|
||||
|
||||
return (new Message(MessageType::UNSUBSCRIBE_ACKNOWLEDGEMENT()))
|
||||
->setMessageId($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received ping request.
|
||||
*/
|
||||
protected function parseAndValidatePingRequestMessage(): Message
|
||||
{
|
||||
return new Message(MessageType::PING_REQUEST());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a received ping acknowledgement.
|
||||
*/
|
||||
protected function parseAndValidatePingAcknowledgementMessage(): Message
|
||||
{
|
||||
return new Message(MessageType::PING_RESPONSE());
|
||||
}
|
||||
}
|
||||
37
vendor/php-mqtt/client/src/MessageType.php
vendored
Normal file
37
vendor/php-mqtt/client/src/MessageType.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/** @noinspection PhpUnusedPrivateFieldInspection */
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
use MyCLabs\Enum\Enum;
|
||||
|
||||
/**
|
||||
* An enumeration describing types of messages.
|
||||
*
|
||||
* @method static MessageType PUBLISH()
|
||||
* @method static MessageType PUBLISH_ACKNOWLEDGEMENT()
|
||||
* @method static MessageType PUBLISH_RECEIPT()
|
||||
* @method static MessageType PUBLISH_RELEASE()
|
||||
* @method static MessageType PUBLISH_COMPLETE()
|
||||
* @method static MessageType SUBSCRIBE_ACKNOWLEDGEMENT()
|
||||
* @method static MessageType UNSUBSCRIBE_ACKNOWLEDGEMENT()
|
||||
* @method static MessageType PING_REQUEST()
|
||||
* @method static MessageType PING_RESPONSE()
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class MessageType extends Enum
|
||||
{
|
||||
private const PUBLISH = 'PUBLISH';
|
||||
private const PUBLISH_ACKNOWLEDGEMENT = 'PUBACK';
|
||||
private const PUBLISH_RECEIPT = 'PUBREC';
|
||||
private const PUBLISH_RELEASE = 'PUBREL';
|
||||
private const PUBLISH_COMPLETE = 'PUBCOMP';
|
||||
private const SUBSCRIBE_ACKNOWLEDGEMENT = 'SUBACK';
|
||||
private const UNSUBSCRIBE_ACKNOWLEDGEMENT = 'UNSUBACK';
|
||||
private const PING_REQUEST = 'PINGREQ';
|
||||
private const PING_RESPONSE = 'PINGRESP';
|
||||
}
|
||||
1284
vendor/php-mqtt/client/src/MqttClient.php
vendored
Normal file
1284
vendor/php-mqtt/client/src/MqttClient.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
76
vendor/php-mqtt/client/src/PendingMessage.php
vendored
Normal file
76
vendor/php-mqtt/client/src/PendingMessage.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Represents a pending message.
|
||||
*
|
||||
* For messages with QoS 1 and 2 the client is responsible to resend the message if no
|
||||
* acknowledgement is received from the broker within a given time period.
|
||||
*
|
||||
* This class serves as common base for message objects which need to be resent if no
|
||||
* acknowledgement is received.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
abstract class PendingMessage
|
||||
{
|
||||
private int $sendingAttempts = 1;
|
||||
private DateTime $lastSentAt;
|
||||
|
||||
/**
|
||||
* Creates a new pending message object.
|
||||
*/
|
||||
protected function __construct(private int $messageId, ?DateTime $sentAt = null)
|
||||
{
|
||||
$this->lastSentAt = $sentAt ?? new DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message identifier.
|
||||
*/
|
||||
public function getMessageId(): int
|
||||
{
|
||||
return $this->messageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date time when the message was last sent.
|
||||
*/
|
||||
public function getLastSentAt(): DateTime
|
||||
{
|
||||
return $this->lastSentAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of times the message has been sent.
|
||||
*/
|
||||
public function getSendingAttempts(): int
|
||||
{
|
||||
return $this->sendingAttempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the date time when the message was last sent.
|
||||
*/
|
||||
public function setLastSentAt(?DateTime $value = null): self
|
||||
{
|
||||
$this->lastSentAt = $value ?? new DateTime();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the sending attempts by one.
|
||||
*/
|
||||
public function incrementSendingAttempts(): self
|
||||
{
|
||||
$this->sendingAttempts++;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
84
vendor/php-mqtt/client/src/PublishedMessage.php
vendored
Normal file
84
vendor/php-mqtt/client/src/PublishedMessage.php
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
/**
|
||||
* A simple DTO for published messages which need to be stored in a repository
|
||||
* while waiting for the confirmation to be deliverable.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class PublishedMessage extends PendingMessage
|
||||
{
|
||||
private bool $received = false;
|
||||
|
||||
/**
|
||||
* Creates a new published message object.
|
||||
*/
|
||||
public function __construct(
|
||||
int $messageId,
|
||||
private string $topicName,
|
||||
private string $message,
|
||||
private int $qualityOfService,
|
||||
private bool $retain,
|
||||
)
|
||||
{
|
||||
parent::__construct($messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topic name of the published message.
|
||||
*/
|
||||
public function getTopicName(): string
|
||||
{
|
||||
return $this->topicName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content of the published message.
|
||||
*/
|
||||
public function getMessage(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested quality of service level.
|
||||
*/
|
||||
public function getQualityOfServiceLevel(): int
|
||||
{
|
||||
return $this->qualityOfService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this message wants to be retained.
|
||||
*/
|
||||
public function wantsToBeRetained(): bool
|
||||
{
|
||||
return $this->retain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the message has been confirmed as received.
|
||||
*/
|
||||
public function hasBeenReceived(): bool
|
||||
{
|
||||
return $this->received;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the published message as received (QoS level 2).
|
||||
*
|
||||
* Returns `true` if the message was not previously received. Otherwise `false` will be returned.
|
||||
*/
|
||||
public function markAsReceived(): bool
|
||||
{
|
||||
$result = !$this->received;
|
||||
|
||||
$this->received = true;
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
232
vendor/php-mqtt/client/src/Repositories/MemoryRepository.php
vendored
Normal file
232
vendor/php-mqtt/client/src/Repositories/MemoryRepository.php
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client\Repositories;
|
||||
|
||||
use PhpMqtt\Client\Contracts\Repository;
|
||||
use PhpMqtt\Client\Exceptions\PendingMessageAlreadyExistsException;
|
||||
use PhpMqtt\Client\Exceptions\PendingMessageNotFoundException;
|
||||
use PhpMqtt\Client\Exceptions\RepositoryException;
|
||||
use PhpMqtt\Client\PendingMessage;
|
||||
use PhpMqtt\Client\PublishedMessage;
|
||||
use PhpMqtt\Client\Subscription;
|
||||
|
||||
/**
|
||||
* Provides an in-memory implementation which manages message ids, subscriptions and pending messages.
|
||||
* Instances of this type do not persist any data and are only meant for simple uses cases.
|
||||
*
|
||||
* @package PhpMqtt\Client\Repositories
|
||||
*/
|
||||
class MemoryRepository implements Repository
|
||||
{
|
||||
private int $nextMessageId = 1;
|
||||
|
||||
/** @var array<int, PendingMessage> */
|
||||
private array $pendingOutgoingMessages = [];
|
||||
|
||||
/** @var array<int, PendingMessage> */
|
||||
private array $pendingIncomingMessages = [];
|
||||
|
||||
/** @var array<int, Subscription> */
|
||||
private array $subscriptions = [];
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function reset(): void
|
||||
{
|
||||
$this->nextMessageId = 1;
|
||||
$this->pendingOutgoingMessages = [];
|
||||
$this->pendingIncomingMessages = [];
|
||||
$this->subscriptions = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function newMessageId(): int
|
||||
{
|
||||
if (count($this->pendingOutgoingMessages) >= 65535) {
|
||||
// This should never happen, as the server receive queue is
|
||||
// normally smaller than the actual total number of message ids.
|
||||
// Also, when using MQTT 5.0 the server can specify a smaller
|
||||
// receive queue size (mosquitto for example has 20 by default),
|
||||
// so the client has to implement the logic to honor this
|
||||
// restriction and fallback to the protocol limit.
|
||||
throw new RepositoryException('No more message identifiers available. The queue is full.');
|
||||
}
|
||||
|
||||
while (isset($this->pendingOutgoingMessages[$this->nextMessageId])) {
|
||||
$this->nextMessageId++;
|
||||
if ($this->nextMessageId > 65535) {
|
||||
$this->nextMessageId = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->nextMessageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function countPendingOutgoingMessages(): int
|
||||
{
|
||||
return count($this->pendingOutgoingMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPendingOutgoingMessage(int $messageId): ?PendingMessage
|
||||
{
|
||||
return $this->pendingOutgoingMessages[$messageId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPendingOutgoingMessagesLastSentBefore(?\DateTime $dateTime = null): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->pendingOutgoingMessages as $pendingMessage) {
|
||||
if ($pendingMessage->getLastSentAt() < $dateTime) {
|
||||
$result[] = $pendingMessage;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function addPendingOutgoingMessage(PendingMessage $message): void
|
||||
{
|
||||
if (isset($this->pendingOutgoingMessages[$message->getMessageId()])) {
|
||||
throw new PendingMessageAlreadyExistsException($message->getMessageId());
|
||||
}
|
||||
|
||||
$this->pendingOutgoingMessages[$message->getMessageId()] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function markPendingOutgoingPublishedMessageAsReceived(int $messageId): bool
|
||||
{
|
||||
if (!isset($this->pendingOutgoingMessages[$messageId]) ||
|
||||
!$this->pendingOutgoingMessages[$messageId] instanceof PublishedMessage) {
|
||||
throw new PendingMessageNotFoundException($messageId);
|
||||
}
|
||||
|
||||
return $this->pendingOutgoingMessages[$messageId]->markAsReceived();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removePendingOutgoingMessage(int $messageId): bool
|
||||
{
|
||||
if (!isset($this->pendingOutgoingMessages[$messageId])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($this->pendingOutgoingMessages[$messageId]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function countPendingIncomingMessages(): int
|
||||
{
|
||||
return count($this->pendingIncomingMessages);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPendingIncomingMessage(int $messageId): ?PendingMessage
|
||||
{
|
||||
return $this->pendingIncomingMessages[$messageId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function addPendingIncomingMessage(PendingMessage $message): void
|
||||
{
|
||||
if (isset($this->pendingIncomingMessages[$message->getMessageId()])) {
|
||||
throw new PendingMessageAlreadyExistsException($message->getMessageId());
|
||||
}
|
||||
|
||||
$this->pendingIncomingMessages[$message->getMessageId()] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removePendingIncomingMessage(int $messageId): bool
|
||||
{
|
||||
if (!isset($this->pendingIncomingMessages[$messageId])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unset($this->pendingIncomingMessages[$messageId]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function countSubscriptions(): int
|
||||
{
|
||||
return count($this->subscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function addSubscription(Subscription $subscription): void
|
||||
{
|
||||
// Remove a potentially existing subscription for this topic filter.
|
||||
$this->removeSubscription($subscription->getTopicFilter());
|
||||
|
||||
$this->subscriptions[] = $subscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSubscriptionsMatchingTopic(string $topicName): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($this->subscriptions as $subscription) {
|
||||
if (!$subscription->matchesTopic($topicName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[] = $subscription;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function removeSubscription(string $topicFilter): bool
|
||||
{
|
||||
foreach ($this->subscriptions as $index => $subscription) {
|
||||
if ($subscription->getTopicFilter() === $topicFilter) {
|
||||
unset($this->subscriptions[$index]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
38
vendor/php-mqtt/client/src/SubscribeRequest.php
vendored
Normal file
38
vendor/php-mqtt/client/src/SubscribeRequest.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
/**
|
||||
* Represents a pending subscribe request.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class SubscribeRequest extends PendingMessage
|
||||
{
|
||||
/** @var Subscription[] */
|
||||
private array $subscriptions;
|
||||
|
||||
/**
|
||||
* Creates a new subscribe request message.
|
||||
*
|
||||
* @param Subscription[] $subscriptions
|
||||
*/
|
||||
public function __construct(int $messageId, array $subscriptions)
|
||||
{
|
||||
parent::__construct($messageId);
|
||||
|
||||
$this->subscriptions = array_values($subscriptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the subscriptions in this request.
|
||||
*
|
||||
* @return Subscription[]
|
||||
*/
|
||||
public function getSubscriptions(): array
|
||||
{
|
||||
return $this->subscriptions;
|
||||
}
|
||||
}
|
||||
106
vendor/php-mqtt/client/src/Subscription.php
vendored
Normal file
106
vendor/php-mqtt/client/src/Subscription.php
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
/**
|
||||
* A simple DTO for subscriptions to a topic which need to be stored in a repository.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class Subscription
|
||||
{
|
||||
private string $regexifiedTopicFilter;
|
||||
|
||||
/**
|
||||
* Creates a new subscription object.
|
||||
*/
|
||||
public function __construct(
|
||||
private string $topicFilter,
|
||||
private int $qualityOfService = 0,
|
||||
private ?\Closure $callback = null,
|
||||
)
|
||||
{
|
||||
$this->regexifyTopicFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the topic filter into a regular expression.
|
||||
*/
|
||||
private function regexifyTopicFilter(): void
|
||||
{
|
||||
$topicFilter = $this->topicFilter;
|
||||
|
||||
// If the topic filter is for a shared subscription, we remove the shared subscription prefix as well as the group name
|
||||
// from the topic filter. To do so, we look for the $share keyword and then try to find the second topic separator to
|
||||
// calculate the substring containing the actual topic filter.
|
||||
// Note: shared subscriptions always have the form: $share/<group>/<topic>
|
||||
if (str_starts_with($topicFilter, '$share/') && ($separatorIndex = strpos($topicFilter, '/', 7)) !== false) {
|
||||
$topicFilter = substr($topicFilter, $separatorIndex + 1);
|
||||
}
|
||||
|
||||
$this->regexifiedTopicFilter = '/^' . str_replace(['$', '/', '+', '#'], ['\$', '\/', '([^\/]*)', '(.*)'], $topicFilter) . '$/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topic of the subscription.
|
||||
*/
|
||||
public function getTopicFilter(): string
|
||||
{
|
||||
return $this->topicFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches the given topic name matches to the subscription's topic filter.
|
||||
*/
|
||||
public function matchesTopic(string $topicName): bool
|
||||
{
|
||||
return (bool) preg_match($this->regexifiedTopicFilter, $topicName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array which contains all matched wildcards of this subscription, taken from the given topic name.
|
||||
*
|
||||
* Example:
|
||||
* Subscription topic filter: foo/+/bar/+/baz/#
|
||||
* Result for 'foo/1/bar/2/baz': ['1', '2']
|
||||
* Result for 'foo/my/bar/subscription/baz/42': ['my', 'subscription', '42']
|
||||
* Result for 'foo/my/bar/subscription/baz/hello/world/123': ['my', 'subscription', 'hello', 'world', '123']
|
||||
* Result for invalid topic 'some/topic': []
|
||||
*
|
||||
* Note: This method should only be called if {@see matchesTopic} returned true. An empty array will be returned otherwise.
|
||||
*/
|
||||
public function getMatchedWildcards(string $topicName): array
|
||||
{
|
||||
if (!preg_match($this->regexifiedTopicFilter, $topicName, $matches)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_slice($matches, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the callback for this subscription.
|
||||
*/
|
||||
public function getCallback(): ?\Closure
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested quality of service level.
|
||||
*/
|
||||
public function getQualityOfServiceLevel(): int
|
||||
{
|
||||
return $this->qualityOfService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the actual quality of service level.
|
||||
*/
|
||||
public function setQualityOfServiceLevel(int $qualityOfService): void
|
||||
{
|
||||
$this->qualityOfService = $qualityOfService;
|
||||
}
|
||||
}
|
||||
38
vendor/php-mqtt/client/src/UnsubscribeRequest.php
vendored
Normal file
38
vendor/php-mqtt/client/src/UnsubscribeRequest.php
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMqtt\Client;
|
||||
|
||||
/**
|
||||
* Represents an unsubscribe request.
|
||||
*
|
||||
* @package PhpMqtt\Client
|
||||
*/
|
||||
class UnsubscribeRequest extends PendingMessage
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $topicFilters;
|
||||
|
||||
/**
|
||||
* Creates a new unsubscribe request object.
|
||||
*
|
||||
* @param string[] $topicFilters
|
||||
*/
|
||||
public function __construct(int $messageId, array $topicFilters)
|
||||
{
|
||||
parent::__construct($messageId);
|
||||
|
||||
$this->topicFilters = array_values($topicFilters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the topic filters in this request.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getTopicFilters(): array
|
||||
{
|
||||
return $this->topicFilters;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user