CMS 2.0
This commit is contained in:
@@ -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
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user