From 78af7a18890526c21e5b0eb6cbbee85febf8c06b Mon Sep 17 00:00:00 2001 From: Namoshek Date: Tue, 5 Jan 2021 17:05:06 +0100 Subject: [PATCH] Update the package for v1 of the MQTT client (#1) * Update composer packages and untrack composer.lock * Update code and config for v1.0.0-rc1 * Use contract instead of implementation * Update README.md --- .gitignore | 1 + README.md | 70 +- composer.json | 14 +- composer.lock | 686 ------------------ config/mqtt-client.php | 80 +- src/ConnectionManager.php | 110 +-- .../ConnectionNotAvailableException.php | 2 +- src/Facades/MQTT.php | 8 +- src/MqttClientServiceProvider.php | 19 +- 9 files changed, 198 insertions(+), 792 deletions(-) delete mode 100644 composer.lock diff --git a/.gitignore b/.gitignore index f7f8ac3..b3c4903 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea/ /vendor/ +composer.lock diff --git a/README.md b/README.md index 1e344fa..90260a8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ # php-mqtt/laravel-client -**:warning: Work in progress - use at own risk! :warning:** +[![Latest Stable Version](https://poser.pugx.org/php-mqtt/laravel-client/v)](https://packagist.org/packages/php-mqtt/laravel-client) +[![Total Downloads](https://poser.pugx.org/php-mqtt/laravel-client/downloads)](https://packagist.org/packages/php-mqtt/laravel-client) +[![License](https://poser.pugx.org/php-mqtt/laravel-client/license)](https://packagist.org/packages/php-mqtt/laravel-client) -`php-mqtt/laravel-client` was created by, and is maintained by [Namoshek](https://github.com/namoshek). +`php-mqtt/laravel-client` was created by, and is maintained by [Marvin Mall](https://github.com/namoshek). It is a Laravel wrapper for the [`php-mqtt/client`](https://github.com/php-mqtt/client) package and allows you to connect to an MQTT broker where you can publish messages and subscribe to topics. ## Installation +The package is available on [packagist.org](https://packagist.org/packages/php-mqtt/laravel-client) and can be installed using composer: + ```bash composer require php-mqtt/laravel-client ``` @@ -18,20 +22,17 @@ Registered will be the service provider as well as an `MQTT` facade. After installing the package, you should publish the configuration file using ```bash -php artisan vendor:publish --provider="PhpMqtt\Client\MqttClientServiceProvider" +php artisan vendor:publish --provider="PhpMqtt\Client\MqttClientServiceProvider" --tag="config" ``` and change the configuration in `config/mqtt-client.php` according to your needs. ## Configuration -The package allows you to configure multiple named connections. An initial example -can be found in the published configuration file. Except for the `host` parameter, -all configuration options are entirely optional and come with the defaults provided -to the `env()` helper in the example configuration file (no default means `null`). +The package allows you to configure multiple named connections. An initial example with inline documentation can be found in the published configuration file. +Most of the configuration options are optional and come with sane defaults (especially all of the `connection_settings`). -An example configuration of two connections, where one is meant for sharing public -data and one for private data, could look like the following: +An example configuration of two connections, where one is meant for sharing public data and one for private data, could look like the following: ```php 'default_connection' => 'private', @@ -48,6 +49,8 @@ data and one for private data, could look like the following: ``` In this example, the private connection is the default one. +_Note: it is recommended to use environment variables to configure the MQTT client. Available environment variables can be found in the configuration file._ + ## Usage ### Publish (QoS level 0) @@ -59,41 +62,51 @@ use PhpMqtt\Client\Facades\MQTT; MQTT::publish('some/topic', 'Hello World!'); ``` -If needed, the connection name can be passed as third parameter: +If needed, the _retain_ flag (default: `false`) can be passed as third and the connection name as fourth parameter: ```php use PhpMqtt\Client\Facades\MQTT; -MQTT::publish('some/topic', 'Hello World!', 'public'); +MQTT::publish('some/topic', 'Hello World!', true, 'public'); ``` -Using `MQTT::publish($topic, $message)` will implicitly call `MQTT::connection()`, -but the connection will not be closed after usage. If you want to close the connection -manually because your script does not need the connection any more, you can call -`MQTT:close()` (optionally with the connection name as a parameter). +Using `MQTT::publish($topic, $message)` will implicitly call `MQTT::connection()`, but the connection will not be closed after usage. +If you want to close the connection manually because your script does not need the connection anymore, +you can call `MQTT:disconnect()` (optionally with the connection name as a parameter). Please also note that using `MQTT::publish($topic, $message)` will always use QoS level 0. -If you need a different QoS level, then please follow the instructions below. +If you need a different QoS level, you will need to use the `MqttClient` directly which is explained below. ### Publish (QoS level 1 & 2) Different to QoS level 0, we need to run an event loop in order for QoS 1 and 2 to work. -This is because with a one-off command we cannot guarantee that a message reaches it's target. -The event loop will ensure a published message gets sent again if no acknowledgment is returned -by the broker within a grace period (in case of QoS level 1). Also handled by the event loop will -be the release of packages in case of QoS level 2. +This is because with a one-off command we cannot guarantee that a message reaches its target. +The event loop will ensure a published message gets sent again if no acknowledgment is returned by the broker within a grace period. +Only when the broker returns an acknowledgement (or all of the acknowledgements in case of QoS 2), +the client will stop resending the message. ```php use PhpMqtt\Client\Facades\MQTT; +/** @var \PhpMqtt\Client\Contracts\MqttClient $mqtt */ $mqtt = MQTT::connection(); $mqtt->publish('some/topic', 'foo', 1); -$mqtt->publish('some/other/topic', 'bar', 2); +$mqtt->publish('some/other/topic', 'bar', 2, true); // Retain the message $mqtt->loop(true); ``` -In order to escape the loop, you can call `$mqtt->interrupt()` which will exit the loop during -the next iteration. The method can for example be called in a registered signal handler: +`$mqtt->loop()` actually starts an infinite loop. To escape it, there are multiple options. +In case of simply publishing a message, all we want is to receive an acknowledgement. +Therefore, we can simply pass `true` as second parameter to exit the loop as soon as all resend queues are cleared: + ```php -pcntl_signal(SIGINT, function (int $signal, $info) use ($mqtt) { +/** @var \PhpMqtt\Client\Contracts\MqttClient $mqtt */ +$mqtt->loop(true, true); +``` + +In order to escape the loop, you can also call `$mqtt->interrupt()` which will exit the loop during +the next iteration. The method can, for example, be called in a registered signal handler: +```php +/** @var \PhpMqtt\Client\Contracts\MqttClient $mqtt */ +pcntl_signal(SIGINT, function () use ($mqtt) { $mqtt->interrupt(); }); ``` @@ -101,11 +114,12 @@ pcntl_signal(SIGINT, function (int $signal, $info) use ($mqtt) { ### Subscribe Very similar to publishing with QoS level 1 and 2, subscribing requires to run an event loop. -But before running the loop, topics need to be subscribed to: +Although before running the loop, topics need to be subscribed to: ```php use PhpMqtt\Client\Facades\MQTT; +/** @var \PhpMqtt\Client\Contracts\MqttClient $mqtt */ $mqtt = MQTT::connection(); $mqtt->subscribe('some/topic', function (string $topic, string $message) { echo sprintf('Received QoS level 1 message on topic [%s]: %s', $topic, $message); @@ -116,7 +130,11 @@ $mqtt->loop(true); ## Features This library allows you to use all the features provided by [`php-mqtt/client`](https://github.com/php-mqtt/client). +Simply retrieve an instance of `\PhpMqtt\Client\Contracts\MqttClient` with `MQTT::connection(string $name = null)` and use it directly. + +For an extensive collection of examples which explain how to use the MQTT client (directly), +you can visit the [`php-mqtt/client-examples` repository](https://github.com/php-mqtt/client-examples). ## License -`php-mqtt/laravel-client` is open-sourced software licensed under the [MIT license](LICENSE.md). +`php-mqtt/laravel-client` is open-source software licensed under the [MIT license](LICENSE.md). diff --git a/composer.json b/composer.json index 0f4a721..7f1ea9f 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "php-mqtt/laravel-client", - "description": "An MQTT client library for Laravel.", + "description": "A wrapper for the php-mqtt/client library for Laravel.", "type": "library", "keywords": [ "mqtt", @@ -13,16 +13,16 @@ "license": "MIT", "authors": [ { - "name": "Namoshek", - "email": "namoshek@gmx.at", + "name": "Marvin Mall", + "email": "marvin-mall@msn.com", "role": "developer" } ], "require": { - "php": "^7.2", - "illuminate/config": "~5.8|~6.0", - "illuminate/support": "~5.8|~6.0", - "php-mqtt/client": "0.1.*" + "php": "^7.4|^8.0", + "illuminate/config": "~7.0|~8.0", + "illuminate/support": "~7.0|~8.0", + "php-mqtt/client": "v1.0.0-rc1" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock deleted file mode 100644 index fa25cf9..0000000 --- a/composer.lock +++ /dev/null @@ -1,686 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "ddb796fbba02d54a14b0299e7af01b14", - "packages": [ - { - "name": "doctrine/inflector", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", - "reference": "5527a48b7313d15261292c149e55e26eae771b0a", - "shasum": "" - }, - "require": { - "php": "^7.1" - }, - "require-dev": { - "phpunit/phpunit": "^6.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Common String Manipulations with regard to casing and singular/plural rules.", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "inflection", - "pluralize", - "singularize", - "string" - ], - "time": "2018-01-09T20:05:19+00:00" - }, - { - "name": "illuminate/config", - "version": "v6.0.2", - "source": { - "type": "git", - "url": "https://github.com/illuminate/config.git", - "reference": "ac59ced5c18df9664dcfe4e33d9040c40386d7da" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/config/zipball/ac59ced5c18df9664dcfe4e33d9040c40386d7da", - "reference": "ac59ced5c18df9664dcfe4e33d9040c40386d7da", - "shasum": "" - }, - "require": { - "illuminate/contracts": "^6.0", - "illuminate/support": "^6.0", - "php": "^7.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.0-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Config\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Config package.", - "homepage": "https://laravel.com", - "time": "2019-07-25T05:06:48+00:00" - }, - { - "name": "illuminate/contracts", - "version": "v6.0.2", - "source": { - "type": "git", - "url": "https://github.com/illuminate/contracts.git", - "reference": "e5d7a6daa4beab3ec7c2d24679e213883a5fc09d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/e5d7a6daa4beab3ec7c2d24679e213883a5fc09d", - "reference": "e5d7a6daa4beab3ec7c2d24679e213883a5fc09d", - "shasum": "" - }, - "require": { - "php": "^7.2", - "psr/container": "^1.0", - "psr/simple-cache": "^1.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.0-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Contracts\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Contracts package.", - "homepage": "https://laravel.com", - "time": "2019-08-24T15:24:36+00:00" - }, - { - "name": "illuminate/support", - "version": "v6.0.2", - "source": { - "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "64669342920e11fb91bd1af838e3d69dac525eae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/64669342920e11fb91bd1af838e3d69dac525eae", - "reference": "64669342920e11fb91bd1af838e3d69dac525eae", - "shasum": "" - }, - "require": { - "doctrine/inflector": "^1.1", - "ext-json": "*", - "ext-mbstring": "*", - "illuminate/contracts": "^6.0", - "nesbot/carbon": "^2.0", - "php": "^7.2" - }, - "conflict": { - "tightenco/collect": "<5.5.33" - }, - "suggest": { - "illuminate/filesystem": "Required to use the composer class (^6.0).", - "moontoast/math": "Required to use ordered UUIDs (^1.1).", - "ramsey/uuid": "Required to use Str::uuid() (^3.7).", - "symfony/process": "Required to use the composer class (^4.3.4).", - "symfony/var-dumper": "Required to use the dd function (^4.3.4).", - "vlucas/phpdotenv": "Required to use the Env class and env helper (^3.3)." - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.0-dev" - } - }, - "autoload": { - "psr-4": { - "Illuminate\\Support\\": "" - }, - "files": [ - "helpers.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "The Illuminate Support package.", - "homepage": "https://laravel.com", - "time": "2019-09-03T17:25:40+00:00" - }, - { - "name": "nesbot/carbon", - "version": "2.24.0", - "source": { - "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/934459c5ac0658bc765ad1e53512c7c77adcac29", - "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29", - "shasum": "" - }, - "require": { - "ext-json": "*", - "php": "^7.1.8 || ^8.0", - "symfony/translation": "^3.4 || ^4.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^2.14 || ^3.0", - "kylekatarnls/multi-tester": "^1.1", - "phpmd/phpmd": "dev-php-7.1-compatibility", - "phpstan/phpstan": "^0.11", - "phpunit/phpunit": "^7.5 || ^8.0", - "squizlabs/php_codesniffer": "^3.4" - }, - "bin": [ - "bin/carbon" - ], - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Carbon\\Laravel\\ServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Carbon\\": "src/Carbon/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Nesbitt", - "email": "brian@nesbot.com", - "homepage": "http://nesbot.com" - }, - { - "name": "kylekatarnls", - "homepage": "http://github.com/kylekatarnls" - } - ], - "description": "A API extension for DateTime that supports 281 different languages.", - "homepage": "http://carbon.nesbot.com", - "keywords": [ - "date", - "datetime", - "time" - ], - "time": "2019-08-31T16:37:55+00:00" - }, - { - "name": "php-mqtt/client", - "version": "v0.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-mqtt/client.git", - "reference": "cfa1a0601a434f031c95df50da13dc3956d1fb96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-mqtt/client/zipball/cfa1a0601a434f031c95df50da13dc3956d1fb96", - "reference": "cfa1a0601a434f031c95df50da13dc3956d1fb96", - "shasum": "" - }, - "require": { - "php": "^7.2", - "psr/log": "^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "PhpMqtt\\Client\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Namoshek", - "email": "namoshek@gmx.at", - "role": "developer" - } - ], - "description": "An MQTT client written in and for PHP.", - "keywords": [ - "client", - "mqtt", - "publish", - "subscribe" - ], - "time": "2019-09-15T12:21:13+00:00" - }, - { - "name": "psr/container", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", - "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" - ], - "time": "2017-02-14T16:28:37+00:00" - }, - { - "name": "psr/log", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "Psr/Log/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2018-11-20T15:27:04+00:00" - }, - { - "name": "psr/simple-cache", - "version": "1.0.1", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\SimpleCache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interfaces for simple caching", - "keywords": [ - "cache", - "caching", - "psr", - "psr-16", - "simple-cache" - ], - "time": "2017-10-23T01:57:42+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.12.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.12-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "time": "2019-08-06T08:03:45+00:00" - }, - { - "name": "symfony/translation", - "version": "v4.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "28498169dd334095fa981827992f3a24d50fed0f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/28498169dd334095fa981827992f3a24d50fed0f", - "reference": "28498169dd334095fa981827992f3a24d50fed0f", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^1.1.6" - }, - "conflict": { - "symfony/config": "<3.4", - "symfony/dependency-injection": "<3.4", - "symfony/yaml": "<3.4" - }, - "provide": { - "symfony/translation-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.4|~4.0", - "symfony/console": "~3.4|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/finder": "~2.8|~3.0|~4.0", - "symfony/http-kernel": "~3.4|~4.0", - "symfony/intl": "~3.4|~4.0", - "symfony/service-contracts": "^1.1.2", - "symfony/var-dumper": "~3.4|~4.0", - "symfony/yaml": "~3.4|~4.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.3-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Translation Component", - "homepage": "https://symfony.com", - "time": "2019-08-26T08:55:16+00:00" - }, - { - "name": "symfony/translation-contracts", - "version": "v1.1.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/325b17c24f3ee23cbecfa63ba809c6d89b5fa04a", - "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a", - "shasum": "" - }, - "require": { - "php": "^7.1.3" - }, - "suggest": { - "symfony/translation-implementation": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to translation", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "time": "2019-08-02T12:15:04+00:00" - } - ], - "packages-dev": [], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": [], - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "^7.2" - }, - "platform-dev": [] -} diff --git a/config/mqtt-client.php b/config/mqtt-client.php index f42bb37..b6533a9 100644 --- a/config/mqtt-client.php +++ b/config/mqtt-client.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use PhpMqtt\Client\MqttClient; use PhpMqtt\Client\Repositories\MemoryRepository; return [ @@ -30,27 +31,80 @@ return [ */ 'connections' => [ + 'default' => [ + + // The host and port to which the client shall connect. 'host' => env('MQTT_HOST'), 'port' => env('MQTT_PORT', 1883), - 'username' => env('MQTT_USERNAME'), - 'password' => env('MQTT_PASSWORD'), + + // The MQTT protocol version used for the connection. + 'protocol' => MqttClient::MQTT_3_1, + + // A specific client id to be used for the connection. If omitted, + // a random client id will be generated for each new connection. 'client_id' => env('MQTT_CLIENT_ID'), - 'cafile' => env('MQTT_CAFILE'), - 'clean_session' => env('MQTT_CLEAN_SESSION', true), - 'logging_enabled' => env('MQTT_LOGGING', true), + + // Whether a clean session shall be used and requested by the client. + // A clean session will let the broker forget about subscriptions and + // queued messages when the client disconnects. Also, if available, + // data of a previous session will be deleted when connecting. + 'use_clean_session' => env('MQTT_CLEAN_SESSION', true), + + // Whether logging shall be enabled. The default logger will be used + // with the log level as configured. + 'enable_logging' => env('MQTT_ENABLE_LOGGING', true), + + // Defines which repository implementation shall be used. Currently, + // only a MemoryRepository is supported. 'repository' => MemoryRepository::class, - 'settings' => [ - 'quality_of_service' => env('MQTT_QUALITY_OF_SERVICE', 0), - 'block_socket' => env('MQTT_BLOCK_SOCKET', false), - 'keep_alive' => env('MQTT_KEEP_ALIVE', 10), - 'socket_timeout' => env('MQTT_TIMEOUT', 5), + + // Additional settings used for the connection to the broker. + // All of these settings are entirely optional and have sane defaults. + 'connection_settings' => [ + + // The TLS settings used for the connection. Must match the specified port. + 'tls' => [ + 'enabled' => env('MQTT_TLS_ENABLED', false), + 'allow_self_signed_certificate' => env('MQTT_TLS_ALLOW_SELF_SIGNED_CERT', false), + 'verify_peer' => env('MQTT_TLS_VERIFY_PEER', true), + 'verify_peer_name' => env('MQTT_TLS_VERIFY_PEER_NAME', true), + 'ca_file' => env('MQTT_TLS_CA_FILE'), + 'ca_path' => env('MQTT_TLS_CA_PATH'), + 'client_certificate_file' => env('MQTT_TLS_CLIENT_CERT_FILE'), + 'client_certificate_key_file' => env('MQTT_TLS_CLIENT_CERT_KEY_FILE'), + 'client_certificate_key_passphrase' => env('MQTT_TLS_CLIENT_CERT_KEY_PASSPHRASE'), + ], + + // Credentials used for authentication and authorization. + 'auth' => [ + 'username' => env('MQTT_AUTH_USERNAME'), + 'password' => env('MQTT_AUTH_PASSWORD'), + ], + + // Can be used to declare a last will during connection. The last will + // is published by the broker when the client disconnects abnormally + // (e.g. in case of a disconnect). + 'last_will' => [ + 'topic' => env('MQTT_LAST_WILL_TOPIC'), + 'message' => env('MQTT_LAST_WILL_MESSAGE'), + 'quality_of_service' => env('MQTT_LAST_WILL_QUALITY_OF_SERVICE', 0), + 'retain' => env('MQTT_LAST_WILL_RETAIN', false), + ], + + // The timeouts (in seconds) used for the connection. Some of these settings + // are only relevant when using the event loop of the MQTT client. + 'connect_timeout' => env('MQTT_CONNECT_TIMEOUT', 60), + 'socket_timeout' => env('MQTT_SOCKET_TIMEOUT', 5), 'resend_timeout' => env('MQTT_RESEND_TIMEOUT', 10), - 'retain' => env('MQTT_RETAIN', false), - 'last_will_topic' => env('MQTT_WILL_TOPIC'), - 'last_will_message' => env('MQTT_WILL_MESSAGE'), + + // The interval (in seconds) in which the client will send a ping to the broker, + // if no other message has been sent. + 'keep_alive_interval' => env('MQTT_KEEP_ALIVE_INTERVAL', 10), ], + ], + ], ]; diff --git a/src/ConnectionManager.php b/src/ConnectionManager.php index a16d94c..7bfd83f 100644 --- a/src/ConnectionManager.php +++ b/src/ConnectionManager.php @@ -6,10 +6,15 @@ namespace PhpMqtt\Client; use Illuminate\Contracts\Container\BindingResolutionException; use Illuminate\Contracts\Foundation\Application; +use Illuminate\Support\Arr; +use PhpMqtt\Client\Contracts\MqttClient as MqttClientContract; use PhpMqtt\Client\Contracts\Repository; +use PhpMqtt\Client\Exceptions\ConfigurationInvalidException; use PhpMqtt\Client\Exceptions\ConnectingToBrokerFailedException; use PhpMqtt\Client\Exceptions\ConnectionNotAvailableException; use PhpMqtt\Client\Exceptions\DataTransferException; +use PhpMqtt\Client\Exceptions\ProtocolNotSupportedException; +use PhpMqtt\Client\Exceptions\RepositoryException; /** * Manages the MQTT connections of the application. @@ -18,17 +23,12 @@ use PhpMqtt\Client\Exceptions\DataTransferException; */ class ConnectionManager { - /** @var Application */ - protected $application; + private Application $application; + private array $config; + private string $defaultConnection; - /** @var array */ - protected $config; - - /** @var string */ - protected $defaultConnection; - - /** @var MQTTClient[] */ - protected $connections = []; + /** @var MqttClientContract[] */ + private array $connections = []; /** * ConnectionManager constructor. @@ -40,19 +40,21 @@ class ConnectionManager { $this->application = $application; $this->config = $config; - $this->defaultConnection = array_get($config, 'default_connection', 'default'); + $this->defaultConnection = Arr::get($config, 'default_connection', 'default'); } /** * Gets the connection with the specified name. * * @param string|null $name - * @return MQTTClient + * @return MqttClientContract * @throws BindingResolutionException + * @throws ConfigurationInvalidException * @throws ConnectingToBrokerFailedException * @throws ConnectionNotAvailableException + * @throws ProtocolNotSupportedException */ - public function connection(string $name = null): MQTTClient + public function connection(string $name = null): MqttClientContract { if ($name === null) { $name = $this->defaultConnection; @@ -71,90 +73,104 @@ class ConnectionManager * @param string|null $connection * @throws DataTransferException */ - public function close(string $connection = null): void + public function disconnect(string $connection = null): void { if ($connection === null) { $connection = $this->defaultConnection; } if (array_key_exists($connection, $this->connections)) { - $this->connections[$connection]->close(); + $this->connections[$connection]->disconnect(); unset($this->connections[$connection]); } } /** - * Publishes a message on the given connection. The QoS level will be 0 - * and the message will not be retained by the broker. + * Publishes a message on the given connection. The QoS level will be 0. * * @param string $topic * @param string $message + * @param bool $retain * @param string|null $connection * @throws BindingResolutionException + * @throws ConfigurationInvalidException * @throws ConnectingToBrokerFailedException * @throws ConnectionNotAvailableException * @throws DataTransferException + * @throws ProtocolNotSupportedException + * @throws RepositoryException */ - public function publish(string $topic, string $message, string $connection = null): void + public function publish(string $topic, string $message, bool $retain = false, string $connection = null): void { $client = $this->connection($connection); - $client->publish($topic, $message); + $client->publish($topic, $message, MqttClient::QOS_AT_MOST_ONCE, $retain); } /** * Creates a new MQTT client and connects to the specified server. * * @param string $name - * @return MQTTClient + * @return MqttClientContract * @throws BindingResolutionException + * @throws ConfigurationInvalidException * @throws ConnectingToBrokerFailedException * @throws ConnectionNotAvailableException + * @throws ProtocolNotSupportedException */ - protected function createConnection(string $name): MQTTClient + protected function createConnection(string $name): MqttClientContract { - $config = array_get($this->config, "connections.{$name}"); + $config = Arr::get($this->config, "connections.{$name}"); + if ($config === null) { throw new ConnectionNotAvailableException($name); } - $host = array_get($config, 'host'); - $port = array_get($config, 'port', 1883); - $username = array_get($config, 'username'); - $password = array_get($config, 'password'); - $clientId = array_get($config, 'client_id'); - $caFile = array_get($config, 'cafile'); - $cleanSession = array_get($config, 'clean_session', true); - $loggingEnabled = array_get($config, 'logging_enabled', true); - $repository = array_get($config, 'repository', Repository::class); + $host = Arr::get($config, 'host'); + $port = Arr::get($config, 'port', 1883); + $clientId = Arr::get($config, 'client_id'); + $protocol = Arr::get($config, 'protocol', MqttClient::MQTT_3_1); + $cleanSession = Arr::get($config, 'use_clean_session', true); + $repository = Arr::get($config, 'repository', Repository::class); + $loggingEnabled = Arr::get($config, 'enable_logging', true); - $settings = $this->parseConnectionSettings(array_get($config, 'settings', [])); + $settings = $this->buildConnectionSettings(Arr::get($config, 'connection_settings', [])); $repository = $this->application->make($repository); $logger = $loggingEnabled ? $this->application->make('log') : null; - $client = new MQTTClient($host, $port, $clientId, $caFile, $repository, $logger); - $client->connect($username, $password, $settings, $cleanSession); + $client = new MqttClient($host, $port, $clientId, $protocol, $repository, $logger); + $client->connect($settings, $cleanSession); return $client; } /** - * Parses the given settings and returns a populated settings object. + * Builds the {@see ConnectionSettings} for the connection specified by the given config. * - * @param array $settings + * @param array $config * @return ConnectionSettings */ - protected function parseConnectionSettings(array $settings): ConnectionSettings + protected function buildConnectionSettings(array $config): ConnectionSettings { - $qos = array_get($settings, 'quality_of_service', 0); - $blockSocket = array_get($settings, 'block_socket', false); - $keepAlive = array_get($settings, 'keep_alive', 10); - $socketTimeout = array_get($settings, 'socket_timeout', 5); - $resendTimeout = array_get($settings, 'resend_timeout', 10); - $retain = array_get($settings, 'retain', false); - $lastWillTopic = array_get($settings, 'last_will_topic'); - $lastWillMessage = array_get($settings, 'last_will_message'); - - return new ConnectionSettings($qos, $retain, $blockSocket, $socketTimeout, $keepAlive, $resendTimeout, $lastWillTopic, $lastWillMessage); + return (new ConnectionSettings) + ->setConnectTimeout(Arr::get($config, 'connect_timeout', 60)) + ->setSocketTimeout(Arr::get($config, 'socket_timeout', 5)) + ->setResendTimeout(Arr::get($config, 'resend_timeout', 10)) + ->setKeepAliveInterval(Arr::get($config, 'keep_alive_interval', 10)) + ->setUsername(Arr::get($config, 'auth.username')) + ->setPassword(Arr::get($config, 'auth.password')) + ->setUseTls(Arr::get($config, 'tls.enabled', false)) + ->setTlsSelfSignedAllowed(Arr::get($config, 'tls.allow_self_signed_certificate', false)) + ->setTlsVerifyPeer(Arr::get($config, 'tls.verify_peer', true)) + ->setTlsVerifyPeerName(Arr::get($config, 'tls.verify_peer_name', true)) + ->setTlsCertificateAuthorityFile(Arr::get($config, 'tls.ca_file')) + ->setTlsCertificateAuthorityPath(Arr::get($config, 'tls.ca_path')) + ->setTlsClientCertificateFile(Arr::get($config, 'tls.client_certificate_file')) + ->setTlsClientCertificateKeyFile(Arr::get($config, 'tls.client_certificate_key_file')) + ->setTlsClientCertificateKeyPassphrase(Arr::get($config, 'tls.client_certificate_key_passphrase')) + ->setLastWillTopic(Arr::get($config, 'last_will.topic')) + ->setLastWillMessage(Arr::get($config, 'last_will.message')) + ->setLastWillQualityOfService(Arr::get($config, 'last_will.quality_of_service', MqttClient::QOS_AT_MOST_ONCE)) + ->setRetainLastWill(Arr::get($config, 'last_will.retain', false)); } } diff --git a/src/Exceptions/ConnectionNotAvailableException.php b/src/Exceptions/ConnectionNotAvailableException.php index 087df7e..0fef3fe 100644 --- a/src/Exceptions/ConnectionNotAvailableException.php +++ b/src/Exceptions/ConnectionNotAvailableException.php @@ -9,7 +9,7 @@ namespace PhpMqtt\Client\Exceptions; * * @package PhpMqtt\Client\Exceptions */ -class ConnectionNotAvailableException extends MQTTClientException +class ConnectionNotAvailableException extends MqttClientException { public function __construct(string $name) { diff --git a/src/Facades/MQTT.php b/src/Facades/MQTT.php index fa93b5b..0801cf3 100644 --- a/src/Facades/MQTT.php +++ b/src/Facades/MQTT.php @@ -6,12 +6,12 @@ namespace PhpMqtt\Client\Facades; use Illuminate\Support\Facades\Facade; use PhpMqtt\Client\ConnectionManager; -use PhpMqtt\Client\MQTTClient; +use PhpMqtt\Client\Contracts\MqttClient; /** - * @method static MQTTClient connection(string $name = null) - * @method static void close(string $connection = null) - * @method static void publish(string $topic, string $message, string $connection = null) + * @method static MqttClient connection(string $name = null) + * @method static void disconnect(string $connection = null) + * @method static void publish(string $topic, string $message, bool $retain = false, string $connection = null) * * @see ConnectionManager * @package PhpMqtt\Client\Facades diff --git a/src/MqttClientServiceProvider.php b/src/MqttClientServiceProvider.php index 22b7793..b488ca7 100644 --- a/src/MqttClientServiceProvider.php +++ b/src/MqttClientServiceProvider.php @@ -21,24 +21,26 @@ class MqttClientServiceProvider extends ServiceProvider */ public function register(): void { - $this->registerConfig(); + $this->mergeConfigFrom(__DIR__ . '/../config/mqtt-client.php', 'mqtt-client'); + $this->registerServices(); } /** - * Publishes and merges the configuration of the package. + * Bootstrap any application services. * * @return void + * @throws \BadFunctionCallException */ - protected function registerConfig(): void + public function boot(): void { - $configPath = __DIR__ . '/../config/mqtt-client.php'; - - if ($this->app->runningInConsole()) { - $this->publishes([$configPath => config_path('mqtt-client.php')], 'config'); + if (!function_exists('config_path')) { + throw new \BadFunctionCallException('config_path() function not found. Is the Laravel framework installed?'); } - $this->mergeConfigFrom($configPath, 'mqtt-client'); + if ($this->app->runningInConsole()) { + $this->publishes([__DIR__ . '/../config/mqtt-client.php' => config_path('mqtt-client.php')], 'config'); + } } /** @@ -50,6 +52,7 @@ class MqttClientServiceProvider extends ServiceProvider { $this->app->bind(ConnectionManager::class, function (Application $app) { $config = $app->make('config')->get('mqtt-client', []); + return new ConnectionManager($app, $config); });