mqtt client class
<?php
namespace App\Servers;
class MqttClient
{
private $socket;
private $msgid = 1;
public $keepalive = 10;
public $timesinceping;
public $topics = [];
public $debug = false;
public $address;
public $port;
public $client_id;
public $will;
private $username;
private $password;
public function __construct($address, $port, $client_id = null)
{
if (!$client_id) {
$client_id = uniqid(microtime(true));
}
$this->address = $address;
$this->port = $port;
$this->client_id = $client_id;
}
public function connect($clean = true, $will = NULL, $username = NULL, $password = NULL)
{
if ($will) $this->will = $will;
if ($username) $this->username = $username;
if ($password) $this->password = $password;
$address = gethostbyname($this->address);
$this->socket = fsockopen($address, $this->port, $errno, $errstr, 60);
if (!$this->socket) {
error_log("fsockopen() $errno, $errstr \n");
return false;
}
stream_set_timeout($this->socket, 5);
stream_set_blocking($this->socket, 0);
$i = 0;
$buffer = "";
$buffer .= chr(0x00);
$i++;
$buffer .= chr(0x06);
$i++;
$buffer .= chr(0x4d);
$i++;
$buffer .= chr(0x51);
$i++;
$buffer .= chr(0x49);
$i++;
$buffer .= chr(0x73);
$i++;
$buffer .= chr(0x64);
$i++;
$buffer .= chr(0x70);
$i++;
$buffer .= chr(0x03);
$i++;
$var = 0;
if ($clean) $var += 2;
if ($this->will != NULL) {
$var += 4;
$var += ($this->will['qos'] << 3);
if ($this->will['retain']) $var += 32;
}
if ($this->username != NULL) $var += 128;
if ($this->password != NULL) $var += 64;
$buffer .= chr($var);
$i++;
$buffer .= chr($this->keepalive >> 8);
$i++;
$buffer .= chr($this->keepalive & 0xff);
$i++;
$buffer .= $this->strwritestring($this->client_id, $i);
if ($this->will != NULL) {
$buffer .= $this->strwritestring($this->will['topic'], $i);
$buffer .= $this->strwritestring($this->will['content'], $i);
}
if ($this->username) $buffer .= $this->strwritestring($this->username, $i);
if ($this->password) $buffer .= $this->strwritestring($this->password, $i);
$head = "";
$head .= chr(0x10);
$head .= chr($i);
fwrite($this->socket, $head, 2);
fwrite($this->socket, $buffer);
$string = $this->read(4);
if (ord($string[0]) >> 4 == 2 && $string[3] == chr(0)) {
if ($this->debug) echo "Connected to Broker\n";
} else {
error_log(sprintf("Connection failed! (Error: 0x%02x 0x%02x)\n",
ord($string[0]), ord($string[3])));
return false;
}
$this->timesinceping = time();
return true;
}
public function read($int = 8192, $nb = false)
{
$string = "";
$togo = $int;
if ($nb) {
return fread($this->socket, $togo);
}
while (!feof($this->socket) && $togo > 0) {
$fread = fread($this->socket, $togo);
$string .= $fread;
$togo = $int - strlen($string);
}
return $string;
}
public function subscribe(array $topics, $qos = 0)
{
$i = 0;
$buffer = "";
$id = $this->msgid;
$buffer .= chr($id >> 8);
$i++;
$buffer .= chr($id % 256);
$i++;
foreach ($topics as $key => $topic) {
$buffer .= $this->strwritestring($key, $i);
$buffer .= chr($topic["qos"]);
$i++;
$this->topics[$key] = $topic;
}
$cmd = 0x80;
$cmd += ($qos << 1);
$head = chr($cmd);
$head .= chr($i);
fwrite($this->socket, $head, 2);
fwrite($this->socket, $buffer, $i);
$string = $this->read(2);
$bytes = ord(substr($string, 1, 1));
$string = $this->read($bytes);
}
public function ping()
{
$head = chr(0xc0);
$head .= chr(0x00);
fwrite($this->socket, $head, 2);
if ($this->debug) echo "ping sent\n";
}
public function disconnect()
{
$head = chr(0xe0);
$head .= chr(0x00);
fwrite($this->socket, $head, 2);
}
public function close()
{
$this->disconnect();
fclose($this->socket);
}
public function publish($topic, $content, $qos = 0, $retain = 0)
{
$i = 0;
$buffer = "";
$buffer .= $this->strwritestring($topic, $i);
if ($qos) {
$id = $this->msgid++;
$buffer .= chr($id >> 8);
$i++;
$buffer .= chr($id % 256);
$i++;
}
$buffer .= $content;
$i += strlen($content);
$cmd = 0x30;
if ($qos) $cmd += $qos << 1;
if ($retain) $cmd += 1;
$head = chr($cmd);
$head .= $this->setmsglength($i);
fwrite($this->socket, $head, strlen($head));
fwrite($this->socket, $buffer, $i);
}
public function message($msg)
{
$tlen = (ord($msg[0]) << 8) + ord($msg[1]);
$topic = substr($msg, 2, $tlen);
$msg = substr($msg, ($tlen + 2));
$found = 0;
foreach ($this->topics as $key => $top) {
if (preg_match("/^" . str_replace("#", ".*",
str_replace("+", "[^\/]*",
str_replace("/", "\/",
str_replace("$", '\$',
$key)))) . "$/", $topic)) {
if (is_string($top['function']) && function_exists($top['function'])) {
call_user_func($top['function'], $topic, $msg);
$found = 1;
} elseif (is_object($top['function'])) {
$top['function']($topic, $msg);
}
}
}
if ($this->debug && !$found) echo "msg recieved but no match in subscriptions\n";
}
public function proc($loop = true)
{
for (; ;) {
$sockets = array($this->socket);
$w = $e = NULL;
$cmd = 0;
if (feof($this->socket)) {
if ($this->debug) echo "eof receive going to reconnect for good measure\n";
fclose($this->socket);
$this->connect(false);
if (count($this->topics))
$this->subscribe($this->topics);
}
$byte = $this->read(1, true);
if (!strlen($byte)) {
if ($loop) {
usleep(100000);
}
} else {
$cmd = (int)(ord($byte) / 16);
if ($this->debug) echo "Recevid: $cmd\n";
$multiplier = 1;
$value = 0;
do {
$digit = ord($this->read(1));
$value += ($digit & 127) * $multiplier;
$multiplier *= 128;
} while (($digit & 128) != 0);
if ($this->debug) echo "Fetching: $value\n";
if ($value)
$string = $this->read($value, "fetch");
if ($cmd) {
switch ($cmd) {
case 3:
$this->message($string);
break;
}
$this->timesinceping = time();
}
}
if ($this->timesinceping < (time() - $this->keepalive)) {
if ($this->debug) echo "not found something so ping\n";
$this->ping();
}
if ($this->timesinceping < (time() - ($this->keepalive * 2))) {
if ($this->debug) echo "not seen a package in a while, disconnecting\n";
fclose($this->socket);
$this->connect(false);
if (count($this->topics))
$this->subscribe($this->topics);
}
}
return 1;
}
public function getmsglength(&$msg, &$i)
{
$multiplier = 1;
$value = 0;
do {
$digit = ord($msg[$i]);
$value += ($digit & 127) * $multiplier;
$multiplier *= 128;
$i++;
} while (($digit & 128) != 0);
return $value;
}
public function setmsglength($len)
{
$string = "";
do {
$digit = $len % 128;
$len = $len >> 7;
if ($len > 0)
$digit = ($digit | 0x80);
$string .= chr($digit);
} while ($len > 0);
return $string;
}
public function strwritestring($str, &$i)
{
$len = strlen($str);
$msb = $len >> 8;
$lsb = $len % 256;
$ret = chr($msb);
$ret .= chr($lsb);
$ret .= $str;
$i += ($len + 2);
return $ret;
}
public function printstr($string)
{
$strlen = strlen($string);
for ($j = 0; $j < $strlen; $j++) {
$num = ord($string[$j]);
if ($num > 31)
$chr = $string[$j]; else $chr = " ";
printf("%4d: %08b : 0x%02x : %s \n", $j, $num, $num, $chr);
}
}
}
- 釋出示例
$server mqtt伺服器地址,下同
$port 埠,下同<?php
$mqtt = new MqttClient($server, $port);
$mqtt->connect();
$mqtt->publish('topic', 'hello world!');
- 訂閱示例
$mqtt = new MqttClient($server, $port);
$mqtt->connect();
$topics['#'] = [
"qos" => 0,
"function" => function ($topic, $message) {
var_dump($topic, $message);
}
}];
$mqtt->subscribe($topics);
$mqtt->proc();
本作品採用《CC 協議》,轉載必須註明作者和本文連結