├── .gitignore ├── 01_publishing ├── 01_publish_with_qos_0.php ├── 02_publish_with_qos_1.php └── 03_publish_with_qos_2.php ├── 02_subscribing ├── 01_subscribe_with_qos_0.php ├── 02_subscribe_with_qos_1.php └── 03_subscribe_with_qos_2.php ├── 03_connection_settings ├── 01_authorize_with_username_and_password.php ├── 02_use_tls_without_client_certificate.php ├── 03_use_tls_with_client_certificate.php └── 04_declare_last_will_upon_connection.php ├── 04_hooks ├── 01_use_a_loop_event_handler_to_perform_periodic_actions.php ├── 02_use_a_publish_event_handler_to_log_published_messages.php ├── 03_use_a_message_received_event_handler_to_log_received_messages.php └── 04_use_a_loop_event_handler_to_timeout_a_remote_procedure_call.php ├── 05_interuppting_the_loop └── 01_use_pcntl_signal_to_interrupt_the_loop.php ├── LICENSE.md ├── README.md ├── composer.json ├── composer.lock └── shared ├── .gitignore ├── SimpleLogger.php └── config.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | /vendor/ 3 | -------------------------------------------------------------------------------- /01_publishing/01_publish_with_qos_0.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 0. 24 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_AT_MOST_ONCE); 25 | 26 | // Gracefully terminate the connection to the broker. 27 | $client->disconnect(); 28 | } catch (MqttClientException $e) { 29 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 30 | $logger->error('Publishing a message using QoS 0 failed. An exception occurred.', ['exception' => $e]); 31 | } 32 | -------------------------------------------------------------------------------- /01_publishing/02_publish_with_qos_1.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 1. 24 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_AT_LEAST_ONCE); 25 | 26 | // Since QoS 1 requires the publisher to await confirmation and resend the message if no confirmation is received, 27 | // we need to start the client loop which takes care of that. By passing `true` as second parameter, 28 | // we allow the loop to exit as soon as all confirmations have been received. 29 | $client->loop(true, true); 30 | 31 | // Gracefully terminate the connection to the broker. 32 | $client->disconnect(); 33 | } catch (MqttClientException $e) { 34 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 35 | $logger->error('Publishing a message using QoS 1 failed. An exception occurred.', ['exception' => $e]); 36 | } 37 | -------------------------------------------------------------------------------- /01_publishing/03_publish_with_qos_2.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 2. 24 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_EXACTLY_ONCE); 25 | 26 | // Since QoS 2 requires the publisher to await confirmation and resend the message if no confirmation is received, 27 | // we need to start the client loop which takes care of that. By passing `true` as second parameter, 28 | // we allow the loop to exit as soon as all confirmations have been received. 29 | $client->loop(true, true); 30 | 31 | // Gracefully terminate the connection to the broker. 32 | $client->disconnect(); 33 | } catch (MqttClientException $e) { 34 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 35 | $logger->error('Publishing a message using QoS 2 failed. An exception occurred.', ['exception' => $e]); 36 | } 37 | -------------------------------------------------------------------------------- /02_subscribing/01_subscribe_with_qos_0.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Subscribe to the topic 'foo/bar/baz' using QoS 0. 24 | $client->subscribe('foo/bar/baz', function (string $topic, string $message, bool $retained) use ($logger, $client) { 25 | $logger->info('We received a {typeOfMessage} on topic [{topic}]: {message}', [ 26 | 'topic' => $topic, 27 | 'message' => $message, 28 | 'typeOfMessage' => $retained ? 'retained message' : 'message', 29 | ]); 30 | 31 | // After receiving the first message on the subscribed topic, we want the client to stop listening for messages. 32 | $client->interrupt(); 33 | }, MqttClient::QOS_AT_MOST_ONCE); 34 | 35 | // Since subscribing requires to wait for messages, we need to start the client loop which takes care of receiving, 36 | // parsing and delivering messages to the registered callbacks. The loop will run indefinitely, until a message 37 | // is received, which will interrupt the loop. 38 | $client->loop(true); 39 | 40 | // Gracefully terminate the connection to the broker. 41 | $client->disconnect(); 42 | } catch (MqttClientException $e) { 43 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 44 | $logger->error('Subscribing to a topic using QoS 0 failed. An exception occurred.', ['exception' => $e]); 45 | } 46 | -------------------------------------------------------------------------------- /02_subscribing/02_subscribe_with_qos_1.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Subscribe to the topic 'foo/bar/baz' using QoS 1. 24 | $client->subscribe('foo/bar/baz', function (string $topic, string $message, bool $retained) use ($logger, $client) { 25 | $logger->info('We received a {typeOfMessage} on topic [{topic}]: {message}', [ 26 | 'topic' => $topic, 27 | 'message' => $message, 28 | 'typeOfMessage' => $retained ? 'retained message' : 'message', 29 | ]); 30 | 31 | // After receiving the first message on the subscribed topic, we want the client to stop listening for messages. 32 | $client->interrupt(); 33 | }, MqttClient::QOS_AT_LEAST_ONCE); 34 | 35 | // Since subscribing requires to wait for messages, we need to start the client loop which takes care of receiving, 36 | // parsing and delivering messages to the registered callbacks. The loop will run indefinitely, until a message 37 | // is received, which will interrupt the loop. 38 | $client->loop(true); 39 | 40 | // Gracefully terminate the connection to the broker. 41 | $client->disconnect(); 42 | } catch (MqttClientException $e) { 43 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 44 | $logger->error('Subscribing to a topic using QoS 1 failed. An exception occurred.', ['exception' => $e]); 45 | } 46 | -------------------------------------------------------------------------------- /02_subscribing/03_subscribe_with_qos_2.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Subscribe to the topic 'foo/bar/baz' using QoS 2. 24 | $client->subscribe('foo/bar/baz', function (string $topic, string $message, bool $retained) use ($logger, $client) { 25 | $logger->info('We received a {typeOfMessage} on topic [{topic}]: {message}', [ 26 | 'topic' => $topic, 27 | 'message' => $message, 28 | 'typeOfMessage' => $retained ? 'retained message' : 'message', 29 | ]); 30 | 31 | // After receiving the first message on the subscribed topic, we want the client to stop listening for messages. 32 | $client->interrupt(); 33 | }, MqttClient::QOS_EXACTLY_ONCE); 34 | 35 | // Since subscribing requires to wait for messages, we need to start the client loop which takes care of receiving, 36 | // parsing and delivering messages to the registered callbacks. The loop will run indefinitely, until a message 37 | // is received, which will interrupt the loop. 38 | $client->loop(true); 39 | 40 | // Gracefully terminate the connection to the broker. 41 | $client->disconnect(); 42 | } catch (MqttClientException $e) { 43 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 44 | $logger->error('Subscribing to a topic using QoS 2 failed. An exception occurred.', ['exception' => $e]); 45 | } 46 | -------------------------------------------------------------------------------- /03_connection_settings/01_authorize_with_username_and_password.php: -------------------------------------------------------------------------------- 1 | setUsername(AUTHORIZATION_USERNAME) 24 | ->setPassword(AUTHORIZATION_PASSWORD); 25 | 26 | // Connect to the broker with the configured connection settings and with a clean session. 27 | $client->connect($connectionSettings, true); 28 | 29 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 0. 30 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_AT_MOST_ONCE); 31 | 32 | // Gracefully terminate the connection to the broker. 33 | $client->disconnect(); 34 | } catch (MqttClientException $e) { 35 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 36 | $logger->error('Connecting with username and password or publishing with QoS 0 failed. An exception occurred.', ['exception' => $e]); 37 | } 38 | -------------------------------------------------------------------------------- /03_connection_settings/02_use_tls_without_client_certificate.php: -------------------------------------------------------------------------------- 1 | setUseTls(true) 24 | ->setTlsSelfSignedAllowed(true) // Allow self-signed certificates. Discouraged for production use. 25 | ->setTlsVerifyPeer(false); // Do not require the self-signed certificate to match the host. Discouraged. 26 | 27 | // Connect to the broker with the configured connection settings and with a clean session. 28 | $client->connect($connectionSettings, true); 29 | 30 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 0. 31 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_AT_MOST_ONCE); 32 | 33 | // Gracefully terminate the connection to the broker. 34 | $client->disconnect(); 35 | } catch (MqttClientException $e) { 36 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 37 | $logger->error('Connecting with TLS or publishing with QoS 0 failed. An exception occurred.', ['exception' => $e]); 38 | } 39 | -------------------------------------------------------------------------------- /03_connection_settings/03_use_tls_with_client_certificate.php: -------------------------------------------------------------------------------- 1 | setUseTls(true) 24 | ->setTlsClientCertificateFile(TLS_CLIENT_CERTIFICATE_FILE) 25 | ->setTlsClientCertificateKeyFile(TLS_CLIENT_CERTIFICATE_KEY_FILE) 26 | ->setTlsClientCertificateKeyPassphrase(TLS_CLIENT_CERTIFICATE_KEY_PASSPHRASE); 27 | 28 | // Connect to the broker with the configured connection settings and with a clean session. 29 | $client->connect($connectionSettings, true); 30 | 31 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 0. 32 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_AT_MOST_ONCE); 33 | 34 | // Gracefully terminate the connection to the broker. 35 | $client->disconnect(); 36 | } catch (MqttClientException $e) { 37 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 38 | $logger->error('Connecting with TLS or publishing with QoS 0 failed. An exception occurred.', ['exception' => $e]); 39 | } 40 | -------------------------------------------------------------------------------- /03_connection_settings/04_declare_last_will_upon_connection.php: -------------------------------------------------------------------------------- 1 | setLastWillTopic('test/client/test-publisher') 25 | ->setLastWillMessage('offline') 26 | ->setLastWillQualityOfService(MqttClient::QOS_AT_LEAST_ONCE) 27 | ->setRetainLastWill(true); 28 | 29 | // Connect to the broker with the configured connection settings and with a clean session. 30 | $client->connect($connectionSettings, true); 31 | 32 | // Publish the message 'online' on the topic 'test/client/test-publisher' using QoS 1. 33 | $client->publish('test/client/test-publisher', 'online', MqttClient::QOS_AT_LEAST_ONCE); 34 | 35 | // Do not terminate the connection to the broker gracefully, to trigger publishing of our last will. 36 | //$client->disconnect(); 37 | } catch (MqttClientException $e) { 38 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 39 | $logger->error('Connecting with last will or publishing online state with QoS 1 failed. An exception occurred.', ['exception' => $e]); 40 | } 41 | -------------------------------------------------------------------------------- /04_hooks/01_use_a_loop_event_handler_to_perform_periodic_actions.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Register an event handler which is run once per loop iteration. For demonstration purposes, we will fetch 24 | // a random user object from the random-data-api.com API and publish it. We only do so once every five minutes though. 25 | $lastPublish = 0; 26 | $client->registerLoopEventHandler(function (MqttClient $client, float $elapsedTime) use ($logger, &$lastPublish) { 27 | // If we fetched a user within the last five minutes, we skip this execution. 28 | if ($lastPublish + 300 > time()) { 29 | return; 30 | } 31 | 32 | // We fetch a random user JSON object as string. 33 | $randomUser = file_get_contents('https://random-data-api.com/api/users/random_user'); 34 | 35 | // Before publishing the details, we make sure the fetching actually worked. 36 | if ($randomUser === false) { 37 | $logger->error('Fetching random user from the random-data-api.com API failed.'); 38 | 39 | return; 40 | } 41 | 42 | // The user details are published on the topic 'random/user' as json object, just like we fetched it. 43 | $client->publish('random/user', $randomUser, MqttClient::QOS_AT_MOST_ONCE); 44 | 45 | // Also, we need to update the timestamp of our last random user fetch. 46 | $lastPublish = time(); 47 | 48 | // After half an hour, we will interrupt the client to stop publishing random user details. 49 | if ($elapsedTime >= 1800) { 50 | $client->interrupt(); 51 | } 52 | }); 53 | 54 | // Now, all we have to do is start the event loop. It will run our registered callback once per iteration. 55 | $client->loop(true); 56 | 57 | // Gracefully terminate the connection to the broker. 58 | $client->disconnect(); 59 | } catch (MqttClientException $e) { 60 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 61 | $logger->error('Running the loop with a loop event handler failed. An exception occurred.', ['exception' => $e]); 62 | } 63 | -------------------------------------------------------------------------------- /04_hooks/02_use_a_publish_event_handler_to_log_published_messages.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Register an event handler which logs all published messages of any topic and QoS level. 24 | $handler = function (MqttClient $client, string $topic, string $message, ?int $messageId, int $qualityOfService, bool $retain) use ($logger) { 25 | $logger->info('Sending message [{messageId}] on topic [{topic}] using QoS {qos}: {message}', [ 26 | 'topic' => $topic, 27 | 'message' => $message, 28 | 'messageId' => $messageId ?? 'no id', 29 | 'qos' => $qualityOfService, 30 | ]); 31 | }; 32 | $client->registerPublishEventHandler($handler); 33 | 34 | // Publish the message 'Hello world!' on the topic 'foo/bar/baz' using QoS 0. 35 | $client->publish('foo/bar/baz', 'Hello world!', MqttClient::QOS_AT_MOST_ONCE); 36 | 37 | // Gracefully terminate the connection to the broker. 38 | $client->disconnect(); 39 | } catch (MqttClientException $e) { 40 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 41 | $logger->error('Publishing a message using QoS 0 failed. An exception occurred.', ['exception' => $e]); 42 | } 43 | -------------------------------------------------------------------------------- /04_hooks/03_use_a_message_received_event_handler_to_log_received_messages.php: -------------------------------------------------------------------------------- 1 | connect(null, true); 22 | 23 | // Register an event handler which is called whenever a message is received. In our case, we log every received message. 24 | $handler = function (MqttClient $client, string $topic, string $message, int $qualityOfService, bool $retained) use ($logger) { 25 | $logger->info('Received message on topic [{topic}] with QoS {qos}: {message}', [ 26 | 'topic' => $topic, 27 | 'message' => $message, 28 | 'qos' => $qualityOfService, 29 | ]); 30 | 31 | // In this example, we interrupt the loop upon receiving the first message to avoid infinite blocking. 32 | $client->interrupt(); 33 | }; 34 | $client->registerMessageReceivedEventHandler($handler); 35 | 36 | // Subscribe to all topics starting with 'foo/' using QoS 0. 37 | $client->subscribe('foo/#'); 38 | 39 | // Start the event loop to receive messages on subscribed topics. 40 | $client->loop(true); 41 | 42 | // Gracefully terminate the connection to the broker. 43 | $client->disconnect(); 44 | } catch (MqttClientException $e) { 45 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 46 | $logger->error('Subscribing to a topic using QoS 0 failed. An exception occurred.', ['exception' => $e]); 47 | } 48 | -------------------------------------------------------------------------------- /04_hooks/04_use_a_loop_event_handler_to_timeout_a_remote_procedure_call.php: -------------------------------------------------------------------------------- 1 | base64_encode(random_bytes(10)), 21 | 'command' => 'echo', 22 | 'payload' => 'Hello World!', 23 | ]; 24 | $result = ['success' => false]; 25 | 26 | try { 27 | // Create a new instance of an MQTT client and configure it to use the shared broker host and port. 28 | $client = new MqttClient(MQTT_BROKER_HOST, MQTT_BROKER_PORT, 'test-client', MqttClient::MQTT_3_1, null, $logger); 29 | 30 | // Connect to the broker without specific connection settings but with a clean session. 31 | $client->connect(null, true); 32 | 33 | // Register a loop event handler which interrupts the loop after 10 seconds without response to our request. 34 | $client->registerLoopEventHandler(function (MqttClient $client, float $elapsedTime) { 35 | if ($elapsedTime >= 10) { 36 | $client->interrupt(); 37 | } 38 | }); 39 | 40 | // Subscribe to the response topic. This is where the receiver of the remote procedure call is supposed to answer with a response. 41 | // Since the result of the remote procedure call is needed outside the callback, it is important to pass the variable by reference. 42 | $client->subscribe('rpc/response/#', function (string $topic, string $message) use ($client, $request, &$result) { 43 | // The entire callback logic is just an example. Parsing and processing the response depends on the individual project. 44 | $json = json_decode($message, true); 45 | 46 | // In our fictitious example, the response json contains an array with the related command id as well as a response payload. 47 | // If the command id in the response matches our request id, this is the response we are looking for. 48 | if ($json['relates_to'] === $request['id']) { 49 | // We can therefore set the result now to success and pass the payload. 50 | $result['success'] = true; 51 | $result['payload'] = $json['payload']; 52 | 53 | // In this example, we interrupt the loop upon receiving the response to our remote procedure call. 54 | $client->interrupt(); 55 | } 56 | }); 57 | 58 | // Here we publish the remote procedure call on the topic where the other client, which will respond to it, is listening for requests. 59 | $client->publish('rpc/request', json_encode($request)); 60 | 61 | // Since subscribing requires waiting for messages, we need to start the client loop which takes care of receiving, 62 | // parsing and delivering messages to the registered callbacks. The loop will run until a response to our request is received 63 | // or a timeout occurs after 10 seconds. In this case, the registered loop event handler will interrupt the loop for us. 64 | $client->loop(true); 65 | 66 | // Gracefully terminate the connection to the broker. 67 | $client->disconnect(); 68 | } catch (MqttClientException $e) { 69 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 70 | $logger->error('Subscribing to a topic failed. An exception occurred.', ['exception' => $e]); 71 | } 72 | 73 | // Finally, the result can be used for whatever it is intended. 74 | if ($result['success'] === true) { 75 | $logger->info('Remote procedure call ended with success. Response payload: {payload}', ['payload' => $result['payload']]); 76 | } else { 77 | $logger->info('Remote procedure call failed. A timeout occurred. No response was received within 10 seconds.'); 78 | } 79 | -------------------------------------------------------------------------------- /05_interuppting_the_loop/01_use_pcntl_signal_to_interrupt_the_loop.php: -------------------------------------------------------------------------------- 1 | info('Received SIGINT signal, interrupting the client for a graceful shutdown...'); 25 | 26 | $client->interrupt(); 27 | }); 28 | 29 | // Connect to the broker without specific connection settings but with a clean session. 30 | $client->connect(null, true); 31 | 32 | // Subscribe to the topic 'foo/bar/baz' using QoS 0. 33 | $client->subscribe('foo/bar/baz', function (string $topic, string $message, bool $retained) use ($logger, $client) { 34 | $logger->info('We received a {typeOfMessage} on topic [{topic}]: {message}', [ 35 | 'topic' => $topic, 36 | 'message' => $message, 37 | 'typeOfMessage' => $retained ? 'retained message' : 'message', 38 | ]); 39 | 40 | // After receiving the first message on the subscribed topic, we want the client to stop listening for messages. 41 | $client->interrupt(); 42 | }, MqttClient::QOS_AT_MOST_ONCE); 43 | 44 | // Since subscribing requires to wait for messages, we need to start the client loop which takes care of receiving, 45 | // parsing and delivering messages to the registered callbacks. The loop will run indefinitely, until a message 46 | // is received, which will interrupt the loop. 47 | $client->loop(true); 48 | 49 | // Gracefully terminate the connection to the broker. 50 | $client->disconnect(); 51 | } catch (MqttClientException $e) { 52 | // MqttClientException is the base exception of all exceptions in the library. Catching it will catch all MQTT related exceptions. 53 | $logger->error('Subscribing to a topic using QoS 0 failed. An exception occurred.', ['exception' => $e]); 54 | } 55 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Marvin Mall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-mqtt/client-examples 2 | 3 | This repository contains examples for [`php-mqtt/client`](https://github.com/php-mqtt/client). 4 | 5 | ## Available Examples 6 | 7 | ### 1) Publishing 8 | 9 | When publishing, there is only a noteworthy difference between QoS 0 and QoS 1/2, since QoS 0 is fire and forget, 10 | while QoS 1/2 require confirmation. For completeness, an example for each QoS level is provided. 11 | 12 | - [Publish a message using QoS 0](01_publishing/01_publish_with_qos_0.php) (_unreliable delivery_) 13 | - [Publish a message using QoS 1](01_publishing/02_publish_with_qos_1.php) (_reliable delivery, but probably multiple times_) 14 | - [Publish a message using QoS 2](01_publishing/03_publish_with_qos_2.php) (_reliable delivery, exactly once_) 15 | 16 | ### 2) Subscribing 17 | 18 | When subscribing, there is not really a difference between the QoS levels. All three QoS levels work the same from a library API perspective. 19 | Under the hood, each of the QoS levels uses a slightly different implementation, though, to follow the protocol specification. 20 | Same as with publishing, an example for each QoS level is provided for completeness. 21 | 22 | - [Subscribe to a topic using QoS 0](02_subscribing/01_subscribe_with_qos_0.php) (_unreliable delivery_) 23 | - [Subscribe to a topic using QoS 1](02_subscribing/02_subscribe_with_qos_1.php) (_reliable delivery, but probably multiple times_) 24 | - [Subscribe to a topic using QoS 2](02_subscribing/03_subscribe_with_qos_2.php) (_reliable delivery, exactly once_) 25 | 26 | ### 3) Connection Settings 27 | 28 | For the connection to the broker, additional settings can be used. For example a username and password for authorization, 29 | a customized timeout for the connection attempt or TLS settings for a secured connection. 30 | For simplicity, all the following examples will publish a single message using QoS 0 after connecting to the broker. 31 | 32 | - [Authorize using username and password](03_connection_settings/01_authorize_with_username_and_password.php) 33 | - [Use TLS without client certificate](03_connection_settings/02_use_tls_without_client_certificate.php) 34 | - [Use TLS with client certificate](03_connection_settings/03_use_tls_with_client_certificate.php) 35 | - [Declare Last Will upon connection](03_connection_settings/04_declare_last_will_upon_connection.php) 36 | 37 | ### 4) Hooks 38 | 39 | To inject logic into the execution path of our MQTT client, it is possible to use so-called hooks. 40 | They are essentially callbacks which can be registered for a specific purpose and which are called upon certain events. 41 | The following gives a few examples and ideas what can be done with hooks, although they are a lot more versatile than can be shown here. 42 | 43 | - [Use a loop event handler to perform periodic actions](04_hooks/01_use_a_loop_event_handler_to_perform_periodic_actions.php) 44 | - [Use a publish event handler to log published messages](04_hooks/02_use_a_publish_event_handler_to_log_published_messages.php) 45 | - [Use a message received event handler to log received messages](04_hooks/03_use_a_message_received_event_handler_to_log_received_messages.php) 46 | - [Use a loop event handler to timeout a remote procedure call](04_hooks/04_use_a_loop_event_handler_to_timeout_a_remote_procedure_call.php) 47 | 48 | ### 5) Interrupting the Loop 49 | 50 | Since the event loop provided by `MqttClient::loop()` is an infinite loop by design, most applications need a way to escape it. 51 | Most often the primary use case is for a graceful shutdown of the application, to avoid forceful termination. 52 | 53 | - [Use `pcntl_signal` to interrupt the loop](05_interuppting_the_loop/01_use_pcntl_signal_to_interrupt_the_loop.php) 54 | 55 | ## How to run the examples? 56 | 57 | Simply clone the repository and run `composer install` to install the required dependencies. 58 | You can then run the examples one by one, but please don't forget to change the shared settings like the MQTT broker host and port before. 59 | The shared settings can be found in [`shared/config.php`](shared/config.php). Alternatively, the examples can be altered directly. 60 | 61 | ### Noteworthy 62 | 63 | The examples use a custom logger to give insight about what is happening internally. You can adjust the logging level as needed. 64 | 65 | ## License 66 | 67 | `php-mqtt/client-examples` is open-source software licensed under the [MIT license](LICENSE.md). 68 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "php-mqtt/client-examples", 3 | "description": "These are examples showcasing how to use php-mqtt/client.", 4 | "type": "project", 5 | "keywords": [ 6 | "mqtt", 7 | "client", 8 | "publish", 9 | "subscribe", 10 | "example" 11 | ], 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Marvin Mall", 16 | "email": "marvin-mall@msn.com", 17 | "role": "developer" 18 | } 19 | ], 20 | "autoload": { 21 | "psr-4": { 22 | "PhpMqtt\\Client\\Examples\\Shared\\": "shared" 23 | } 24 | }, 25 | "require": { 26 | "php": "^7.4|^8.0", 27 | "ext-json": "*", 28 | "ext-pcntl": "*", 29 | "php-mqtt/client": "^1.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "980f1f42d56b24555a1db31b9b75f665", 8 | "packages": [ 9 | { 10 | "name": "myclabs/php-enum", 11 | "version": "1.7.7", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/myclabs/php-enum.git", 15 | "reference": "d178027d1e679832db9f38248fcc7200647dc2b7" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/myclabs/php-enum/zipball/d178027d1e679832db9f38248fcc7200647dc2b7", 20 | "reference": "d178027d1e679832db9f38248fcc7200647dc2b7", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "ext-json": "*", 25 | "php": ">=7.1" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^7", 29 | "squizlabs/php_codesniffer": "1.*", 30 | "vimeo/psalm": "^3.8" 31 | }, 32 | "type": "library", 33 | "autoload": { 34 | "psr-4": { 35 | "MyCLabs\\Enum\\": "src/" 36 | } 37 | }, 38 | "notification-url": "https://packagist.org/downloads/", 39 | "license": [ 40 | "MIT" 41 | ], 42 | "authors": [ 43 | { 44 | "name": "PHP Enum contributors", 45 | "homepage": "https://github.com/myclabs/php-enum/graphs/contributors" 46 | } 47 | ], 48 | "description": "PHP Enum implementation", 49 | "homepage": "http://github.com/myclabs/php-enum", 50 | "keywords": [ 51 | "enum" 52 | ], 53 | "support": { 54 | "issues": "https://github.com/myclabs/php-enum/issues", 55 | "source": "https://github.com/myclabs/php-enum/tree/1.7.7" 56 | }, 57 | "funding": [ 58 | { 59 | "url": "https://github.com/mnapoli", 60 | "type": "github" 61 | }, 62 | { 63 | "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum", 64 | "type": "tidelift" 65 | } 66 | ], 67 | "time": "2020-11-14T18:14:52+00:00" 68 | }, 69 | { 70 | "name": "php-mqtt/client", 71 | "version": "v1.0.0", 72 | "source": { 73 | "type": "git", 74 | "url": "https://github.com/php-mqtt/client.git", 75 | "reference": "ca47fff0d5513c7ebeba8698e8ff8397414c38d4" 76 | }, 77 | "dist": { 78 | "type": "zip", 79 | "url": "https://api.github.com/repos/php-mqtt/client/zipball/ca47fff0d5513c7ebeba8698e8ff8397414c38d4", 80 | "reference": "ca47fff0d5513c7ebeba8698e8ff8397414c38d4", 81 | "shasum": "" 82 | }, 83 | "require": { 84 | "myclabs/php-enum": "^1.7", 85 | "php": "^7.4|^8.0", 86 | "psr/log": "^1.1" 87 | }, 88 | "require-dev": { 89 | "phpunit/php-invoker": "^3.0", 90 | "phpunit/phpunit": "^9.0", 91 | "squizlabs/php_codesniffer": "^3.5" 92 | }, 93 | "suggest": { 94 | "ext-redis": "Required for the RedisRepository" 95 | }, 96 | "type": "library", 97 | "autoload": { 98 | "psr-4": { 99 | "PhpMqtt\\Client\\": "src" 100 | } 101 | }, 102 | "notification-url": "https://packagist.org/downloads/", 103 | "license": [ 104 | "MIT" 105 | ], 106 | "authors": [ 107 | { 108 | "name": "Marvin Mall", 109 | "email": "marvin-mall@msn.com", 110 | "role": "developer" 111 | } 112 | ], 113 | "description": "An MQTT client written in and for PHP.", 114 | "keywords": [ 115 | "client", 116 | "mqtt", 117 | "publish", 118 | "subscribe" 119 | ], 120 | "support": { 121 | "issues": "https://github.com/php-mqtt/client/issues", 122 | "source": "https://github.com/php-mqtt/client/tree/v1.0.0" 123 | }, 124 | "time": "2021-01-10T18:42:03+00:00" 125 | }, 126 | { 127 | "name": "psr/log", 128 | "version": "1.1.3", 129 | "source": { 130 | "type": "git", 131 | "url": "https://github.com/php-fig/log.git", 132 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc" 133 | }, 134 | "dist": { 135 | "type": "zip", 136 | "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc", 137 | "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc", 138 | "shasum": "" 139 | }, 140 | "require": { 141 | "php": ">=5.3.0" 142 | }, 143 | "type": "library", 144 | "extra": { 145 | "branch-alias": { 146 | "dev-master": "1.1.x-dev" 147 | } 148 | }, 149 | "autoload": { 150 | "psr-4": { 151 | "Psr\\Log\\": "Psr/Log/" 152 | } 153 | }, 154 | "notification-url": "https://packagist.org/downloads/", 155 | "license": [ 156 | "MIT" 157 | ], 158 | "authors": [ 159 | { 160 | "name": "PHP-FIG", 161 | "homepage": "http://www.php-fig.org/" 162 | } 163 | ], 164 | "description": "Common interface for logging libraries", 165 | "homepage": "https://github.com/php-fig/log", 166 | "keywords": [ 167 | "log", 168 | "psr", 169 | "psr-3" 170 | ], 171 | "support": { 172 | "source": "https://github.com/php-fig/log/tree/1.1.3" 173 | }, 174 | "time": "2020-03-23T09:12:05+00:00" 175 | } 176 | ], 177 | "packages-dev": [], 178 | "aliases": [], 179 | "minimum-stability": "stable", 180 | "stability-flags": [], 181 | "prefer-stable": false, 182 | "prefer-lowest": false, 183 | "platform": { 184 | "php": "^7.4|^8.0", 185 | "ext-json": "*", 186 | "ext-pcntl": "*" 187 | }, 188 | "platform-dev": [], 189 | "plugin-api-version": "2.0.0" 190 | } 191 | -------------------------------------------------------------------------------- /shared/.gitignore: -------------------------------------------------------------------------------- 1 | *.crt 2 | *.csr 3 | *.key 4 | -------------------------------------------------------------------------------- /shared/SimpleLogger.php: -------------------------------------------------------------------------------- 1 | logLevel = $logLevel; 31 | $this->logLevelNumeric = $this->mapLogLevelToInteger($logLevel); 32 | } 33 | 34 | /** 35 | * Logs with an arbitrary level. 36 | * 37 | * @param mixed $level 38 | * @param string $message 39 | * @param array $context 40 | * @return void 41 | */ 42 | public function log($level, $message, array $context = []): void 43 | { 44 | if ($this->mapLogLevelToInteger($level) < $this->logLevelNumeric) { 45 | return; 46 | } 47 | 48 | echo $this->interpolate($message, $context) . PHP_EOL; 49 | } 50 | 51 | /** 52 | * Interpolates the given message with variables from the given context. 53 | * Replaced are placeholder of the form {foo} with variables of the same 54 | * name without curly braces in the context. 55 | * 56 | * @param $message 57 | * @param array $context 58 | * @return string 59 | */ 60 | private function interpolate($message, array $context = []) 61 | { 62 | // Build a replacement array with braces around the context keys. 63 | $replace = []; 64 | foreach ($context as $key => $val) { 65 | // Ensure that the value can be cast to string. 66 | if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { 67 | $replace['{' . $key . '}'] = $val; 68 | } 69 | } 70 | 71 | // Interpolate replacement values into the message and return the result. 72 | return strtr($message, $replace); 73 | } 74 | 75 | /** 76 | * Maps the string representation of a log level to the numeric level. 77 | * 78 | * @param string $level 79 | * @return int 80 | */ 81 | private function mapLogLevelToInteger(string $level): int 82 | { 83 | $map = $this->getLogLevelMap(); 84 | 85 | if (!array_key_exists($level, $map)) { 86 | return $map[LogLevel::DEBUG]; 87 | } 88 | 89 | return $map[$level]; 90 | } 91 | 92 | /** 93 | * Returns a log level map. 94 | * 95 | * @return array 96 | */ 97 | private function getLogLevelMap(): array 98 | { 99 | return [ 100 | LogLevel::DEBUG => 0, 101 | LogLevel::INFO => 1, 102 | LogLevel::NOTICE => 2, 103 | LogLevel::WARNING => 3, 104 | LogLevel::ERROR => 4, 105 | LogLevel::CRITICAL => 5, 106 | LogLevel::ALERT => 6, 107 | LogLevel::EMERGENCY => 7, 108 | ]; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /shared/config.php: -------------------------------------------------------------------------------- 1 |