├── .gitignore ├── .travis.yml ├── CREDITS ├── LICENSE ├── README.md ├── config.m4 ├── config.w32 ├── docs ├── .gitignore ├── Makefile ├── client.rst ├── conf.py ├── exception.rst ├── index.rst ├── make.bat ├── message.rst ├── overview.rst └── requirements.txt ├── examples ├── event.php ├── pub.php ├── subclass.php ├── test.php ├── testOnpublish.php └── testwill.php ├── mosquitto.c ├── mosquitto_message.c ├── package.xml ├── php_mosquitto.h ├── tests ├── Client │ ├── __construct.phpt │ ├── clearWill.phpt │ ├── connect.phpt │ ├── disconnect.phpt │ ├── exitLoop.phpt │ ├── getSocket.phpt │ ├── loop.phpt │ ├── loopForever.phpt │ ├── onConnect.phpt │ ├── onDisconnect.phpt │ ├── onLog.phpt │ ├── onMessage.phpt │ ├── onSubscribe.phpt │ ├── onUnsubscribe.phpt │ ├── publish.phpt │ ├── setCredentials.phpt │ ├── setMaxInFlightMessages.phpt │ ├── setMessageRetry.phpt │ ├── setReconnectDelay.phpt │ ├── setTlsCertificates.phpt │ ├── setTlsInsecure.phpt │ ├── setTlsOptions.phpt │ ├── setTlsPSK.phpt │ ├── setWill.phpt │ ├── subscribe.phpt │ └── unsubscribe.phpt ├── Message │ ├── __construct.phpt │ ├── tokeniseTopic.phpt │ └── topicMatchesSub.phpt ├── makeTestCerts.sh ├── mosquitto.conf ├── psk.db └── setup.php └── travis ├── compile-ext.sh ├── compile-mosquitto.sh ├── compile-php.sh └── run-tests.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /.deps 2 | /Makefile 3 | /*.lo 4 | /*.loT 5 | /*.slo 6 | /*.mk 7 | /*.la 8 | /.libs 9 | /libs.mk 10 | /ac*.m4 11 | /build 12 | /config.h 13 | /config.h.in 14 | /config.nice 15 | /config.sub 16 | /configure 17 | /configure.in 18 | /config.status 19 | /config.cache 20 | /conftest 21 | /conftest.c 22 | /core 23 | /dynlib.m4 24 | /install-sh 25 | /ltmain.sh 26 | /include 27 | /Makefile.fragments 28 | /Makefile.global 29 | /Makefile.objects 30 | /missing 31 | /mkinstalldirs 32 | /modules 33 | /scan_makefile_in.awk 34 | /config.guess 35 | /*swp 36 | /config.log 37 | /libtool 38 | /Debug 39 | /Release 40 | /Debug_TS 41 | /Release_TS 42 | /*.plg 43 | /*.patch 44 | /*.tgz 45 | /*.ncb 46 | /*.opt 47 | /*.dsw 48 | /autom4te.cache 49 | /run-tests-config.php 50 | /run-tests.php 51 | /tmp-php.ini 52 | .svn 53 | /tests/**/*.diff 54 | /tests/**/*.exp 55 | /tests/**/*.log 56 | /tests/**/*.out 57 | /tests/**/*.php 58 | /tests/**/*.sh 59 | 60 | /tests/certs 61 | 62 | .idea/ 63 | CMakeLists.txt -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | before_install: 4 | - sudo apt-get update -qq 5 | - sudo apt-get -y install libc-ares-dev libssl-dev uuid-dev 6 | 7 | before_script: 8 | - ./travis/compile-mosquitto.sh 9 | - ./travis/compile-php.sh 10 | - ./travis/compile-ext.sh 11 | 12 | script: 13 | - ./travis/run-tests.sh 14 | 15 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | # Mosquitto-PHP 2 | 3 | Original implementation by Michael Maclean 4 | onPublish callback from Github user @acrazing 5 | PHP 7 support by Sara Golemon 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Michael Maclean 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mosquitto-PHP 2 | 3 | This is an extension to allow using the [Eclipse Mosquitto™ MQTT client library](http://mosquitto.org) with PHP. See the `examples/` directory for usage. 4 | 5 | [![Build Status](https://travis-ci.org/mgdm/Mosquitto-PHP.svg?branch=master)](https://travis-ci.org/mgdm/Mosquitto-PHP) 6 | 7 | ## PHP 7 support 8 | 9 | Thanks to [Sara Golemon](https://twitter.com/SaraMG) this extension now supports PHP 7. I would be grateful if anyone using PHP 7 could test it and let me know how it works out. 10 | 11 | ## Requirements 12 | 13 | * PHP 5.3+ 14 | * libmosquitto 1.2.x or later 15 | * Linux or Mac OS X. I do not have a Windows machine handy, though patches or 16 | pull requests are of course very welcome! 17 | 18 | ## Installation 19 | 20 | If you've used a pre-built package to install Mosquitto, you need to make sure you have the development headers installed. On Red Hat-derived systems, this is probably called `libmosquitto-devel`, and on Debian-based systems it will be `libmosquitto-dev`. 21 | 22 | You may obtain this package using [PECL](http://pecl.php.net): 23 | 24 | ```` 25 | pecl install Mosquitto-alpha 26 | ```` 27 | 28 | Alternatively, you can use the normal extension build process: 29 | 30 | ```` 31 | phpize 32 | ./configure --with-mosquitto=/path/to/libmosquitto 33 | make 34 | make install 35 | ```` 36 | 37 | Then add `extension=mosquitto.so` to your `php.ini`. 38 | 39 | The `--with-mosquitto` argument is optional, and only required if your 40 | libmosquitto install cannot be found. 41 | 42 | ## General operation 43 | 44 | The underlying library is based on callbacks and asynchronous operation. As such, you have to call the `loop()` method of the `Client` frequently to permit the library to handle the messages in its queues. Also, you should use the callback functions to ensure that you only attempt to publish after the client has connected, etc. For example, here is how you would correctly publish a QoS=2 message: 45 | 46 | ```php 47 | onLog('var_dump'); 54 | $c->onConnect(function() use ($c, &$mid) { 55 | $mid = $c->publish("mgdm/test", "Hello", 2); 56 | }); 57 | 58 | $c->onPublish(function($publishedId) use ($c, $mid) { 59 | if ($publishedId == $mid) { 60 | $c->disconnect(); 61 | } 62 | }); 63 | 64 | $c->connect("localhost"); 65 | $c->loopForever(); 66 | 67 | echo "Finished" 68 | ``` 69 | 70 | ## Documentation 71 | 72 | Full documentation is [available on ReadTheDocs](http://mosquitto-php.readthedocs.io/). 73 | 74 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension mosquitto 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | PHP_ARG_WITH(mosquitto, for mosquitto support, 9 | [ --with-mosquitto Include mosquitto support]) 10 | 11 | 12 | if test "$PHP_MOSQUITTO" != "no"; then 13 | dnl Write more examples of tests here... 14 | 15 | # --with-mosquitto -> check with-path 16 | SEARCH_PATH="/usr/local /usr" # you might want to change this 17 | SEARCH_FOR="/include/mosquitto.h" # you most likely want to change this 18 | if test -r $PHP_MOSQUITTO/$SEARCH_FOR; then # path given as parameter 19 | MOSQUITTO_DIR=$PHP_MOSQUITTO 20 | else # search default path list 21 | AC_MSG_CHECKING([for mosquitto files in default path]) 22 | for i in $SEARCH_PATH ; do 23 | if test -r $i/$SEARCH_FOR; then 24 | MOSQUITTO_DIR=$i 25 | AC_MSG_CHECKING($MOSQUITTO_DIR) 26 | AC_MSG_RESULT(found in $i) 27 | fi 28 | done 29 | fi 30 | 31 | if test -z "$MOSQUITTO_DIR"; then 32 | AC_MSG_RESULT([not found]) 33 | AC_MSG_ERROR([Please reinstall the mosquitto distribution]) 34 | fi 35 | 36 | # --with-mosquitto -> add include path 37 | PHP_ADD_INCLUDE($MOSQUITTO_DIR/include) 38 | 39 | # --with-mosquitto -> check for lib and symbol presence 40 | LIBNAME=mosquitto # you may want to change this 41 | 42 | PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MOSQUITTO_DIR/$PHP_LIBDIR, MOSQUITTO_SHARED_LIBADD) 43 | 44 | PHP_SUBST(MOSQUITTO_SHARED_LIBADD) 45 | 46 | PHP_NEW_EXTENSION(mosquitto, mosquitto.c mosquitto_message.c, $ext_shared) 47 | 48 | AC_FUNC_STRERROR_R 49 | fi 50 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | 2 | ARG_WITH("mosquitto", "include hrtime support", "no"); 3 | 4 | if (PHP_MOSQUITTO != "no") { 5 | if (CHECK_HEADER_ADD_INCLUDE("mosquitto.h", "CFLAGS_MOSQUITTO", PHP_PHP_BUILD + "\\include;" + PHP_MOSQUITTO) 6 | && CHECK_LIB("mosquitto.lib", "mosquitto", PHP_PHP_BUILD + "\\lib;" + PHP_MOSQUITTO)) { 7 | 8 | EXTENSION('mosquitto', 'mosquitto.c mosquitto_message.c'); 9 | AC_DEFINE('HAVE_MOSQUITTO', 1); 10 | 11 | } else { 12 | WARNING("mosquitto not enabled; libraries and headers not found"); 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | 3 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " applehelp to make an Apple Help Book" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " epub3 to make an epub3" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 34 | @echo " text to make text files" 35 | @echo " man to make manual pages" 36 | @echo " texinfo to make Texinfo files" 37 | @echo " info to make Texinfo files and run them through makeinfo" 38 | @echo " gettext to make PO message catalogs" 39 | @echo " changes to make an overview of all changed/added/deprecated items" 40 | @echo " xml to make Docutils-native XML files" 41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | @echo " coverage to run coverage check of the documentation (if enabled)" 45 | @echo " dummy to check syntax errors of document sources" 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf $(BUILDDIR)/* 50 | 51 | .PHONY: html 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | .PHONY: dirhtml 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | .PHONY: singlehtml 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | .PHONY: pickle 70 | pickle: 71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 72 | @echo 73 | @echo "Build finished; now you can process the pickle files." 74 | 75 | .PHONY: json 76 | json: 77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 78 | @echo 79 | @echo "Build finished; now you can process the JSON files." 80 | 81 | .PHONY: htmlhelp 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | .PHONY: qthelp 89 | qthelp: 90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 91 | @echo 92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Mosquitto-PHP.qhcp" 95 | @echo "To view the help file:" 96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Mosquitto-PHP.qhc" 97 | 98 | .PHONY: applehelp 99 | applehelp: 100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 101 | @echo 102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 103 | @echo "N.B. You won't be able to view it unless you put it in" \ 104 | "~/Library/Documentation/Help or install it in your application" \ 105 | "bundle." 106 | 107 | .PHONY: devhelp 108 | devhelp: 109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 110 | @echo 111 | @echo "Build finished." 112 | @echo "To view the help file:" 113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Mosquitto-PHP" 114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Mosquitto-PHP" 115 | @echo "# devhelp" 116 | 117 | .PHONY: epub 118 | epub: 119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 120 | @echo 121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 122 | 123 | .PHONY: epub3 124 | epub3: 125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 126 | @echo 127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 128 | 129 | .PHONY: latex 130 | latex: 131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 132 | @echo 133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 135 | "(use \`make latexpdf' here to do that automatically)." 136 | 137 | .PHONY: latexpdf 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | .PHONY: latexpdfja 145 | latexpdfja: 146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | @echo "Running LaTeX files through platex and dvipdfmx..." 148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 150 | 151 | .PHONY: text 152 | text: 153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 154 | @echo 155 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 156 | 157 | .PHONY: man 158 | man: 159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 160 | @echo 161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 162 | 163 | .PHONY: texinfo 164 | texinfo: 165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 166 | @echo 167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 168 | @echo "Run \`make' in that directory to run these through makeinfo" \ 169 | "(use \`make info' here to do that automatically)." 170 | 171 | .PHONY: info 172 | info: 173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 174 | @echo "Running Texinfo files through makeinfo..." 175 | make -C $(BUILDDIR)/texinfo info 176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 177 | 178 | .PHONY: gettext 179 | gettext: 180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 181 | @echo 182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 183 | 184 | .PHONY: changes 185 | changes: 186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 187 | @echo 188 | @echo "The overview file is in $(BUILDDIR)/changes." 189 | 190 | .PHONY: linkcheck 191 | linkcheck: 192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 193 | @echo 194 | @echo "Link check complete; look for any errors in the above output " \ 195 | "or in $(BUILDDIR)/linkcheck/output.txt." 196 | 197 | .PHONY: doctest 198 | doctest: 199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 200 | @echo "Testing of doctests in the sources finished, look at the " \ 201 | "results in $(BUILDDIR)/doctest/output.txt." 202 | 203 | .PHONY: coverage 204 | coverage: 205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 206 | @echo "Testing of coverage in the sources finished, look at the " \ 207 | "results in $(BUILDDIR)/coverage/python.txt." 208 | 209 | .PHONY: xml 210 | xml: 211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 212 | @echo 213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 214 | 215 | .PHONY: pseudoxml 216 | pseudoxml: 217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 218 | @echo 219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 220 | 221 | .PHONY: dummy 222 | dummy: 223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 224 | @echo 225 | @echo "Build finished. Dummy builder generates no files." 226 | -------------------------------------------------------------------------------- /docs/client.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | Mosquitto\\Client 3 | ================= 4 | 5 | .. php:namespace:: Mosquitto 6 | 7 | .. php:class:: Client 8 | 9 | This is the main Mosquitto client. 10 | 11 | .. php:const:: LOG_DEBUG 12 | 13 | Identifies a debug-level log message 14 | 15 | .. php:const:: LOG_INFO 16 | 17 | Identifies an info-level log message 18 | 19 | .. php:const:: LOG_NOTICE 20 | 21 | Identifies a notice-level log message 22 | 23 | .. php:const:: LOG_WARNING 24 | 25 | Identifies a warning-level log message 26 | 27 | .. php:const:: LOG_ERR 28 | 29 | Identifies an error-level log message 30 | 31 | .. php:const:: SSL_VERIFY_NONE 32 | 33 | Used with :php:meth:`~Client::setTlsInsecure`. Do not verify the identity of the server, thus making the connection insecure. 34 | 35 | .. php:const:: SSL_VERIFY_PEER 36 | 37 | Used with :php:meth:`~Client::setTlsInsecure`. Verify the identity of the server. 38 | 39 | .. php:method:: __construct([$id = null, $cleanSession = true]) 40 | 41 | Construct a new Client instance. 42 | 43 | :param string $id: The client ID. If omitted or ``null``, one will be generated at random. 44 | :param boolean $cleanSession: Set to ``true`` to instruct the broker to clean all messages and subscriptions on disconnect. Must be ``true`` if the ``$id`` parameter is ``null``. 45 | 46 | 47 | .. php:method:: setCredentials($username, $password) 48 | 49 | Set the username and password to use on connecting to the broker. Must be called before :php:meth:`~Client::connect`. 50 | 51 | :param string $username: Username to supply to the broker 52 | :param string $password: Password to supply to the broker 53 | 54 | .. php:method:: setTlsCertificates($caPath[, $certFile, $keyFile, $password]) 55 | 56 | Configure the client for certificate based SSL/TLS support. Must be called before :php:meth:`~Client::connect`. Cannot be used in conjunction with :php:meth:`~Client::setTlsPSK`. 57 | 58 | Define the Certificate Authority certificates to be trusted (ie. the server certificate must be signed with one of these certificates) using ``$caFile``. If the server you are connecting to requires clients to provide a certificate, define ``$certFile`` and ``$keyFile`` with your client certificate and private key. If your private key is encrypted, provide the password as the fourth parameter. 59 | 60 | :param string $caPath: Path to the PEM encoded trusted CA certificate files, or to a directory containing them. 61 | :param string $certFile: Path to the PEM encoded certificate file for this client. Optional. 62 | :param string $keyFile: Path to a file containing the PEM encoded private key for this client. Required if certfile is set. 63 | :param string $password: The password for the keyfile, if it is encrypted. If null, the password will be asked for on the command line. 64 | 65 | .. php:method:: setTlsInsecure($value) 66 | 67 | Configure verification of the server hostname in the server certificate. If ``$value`` is ``true``, it is impossible to guarantee that the host you are connecting to is not impersonating your server. Do not use this function in a real system. Must be called before :php:meth:`~Client::connect`. 68 | 69 | :param boolean $value: If set to false, the default, certificate hostname checking is performed. If set to ``true``, no hostname checking is performed and the connection is insecure. 70 | 71 | .. php:method:: setTlsOptions($certReqs, $tlsVersion, $ciphers) 72 | 73 | Set advanced SSL/TLS options. Must be called before :php:meth:`~Client::connect`. 74 | 75 | :param int $certReqs: Whether or not to verify the server. Can be ``Mosquitto\\Client::SSL_VERIFY_NONE``, to disable certificate verification, or ``Mosquitto\Client::SSL_VERIFY_PEER`` (the default), to verify the server certificate. 76 | :param string $tlsVersion: The TLS version to use. If ``null``, a default is used. The default value depends on the version of OpenSSL the library was compiled against. Available options on OpenSSL >= 1.0.1 are ``tlsv1.2``, ``tlsv1.1`` and ``tlsv1``. 77 | :param string $cipers: A string describing the ciphers available for use. See the ``openssl ciphers`` tool for more information. If ``null``, the default set will be used. 78 | 79 | .. php:method:: setTlsPSK($psk, $identity[, $ciphers]) 80 | 81 | Configure the client for pre-shared-key based TLS support. Must be called before :php:meth:`~Client::connect`. Cannot be used in conjunction with setTlsCertificates. 82 | 83 | :param string $psk: The pre-shared key in hex format with no leading "0x". 84 | :param string $identity: The identity of this client. May be used as the username depending on server settings. 85 | :param string $cipers: Optional. A string describing the ciphers available for use. See the ``openssl ciphers`` tool for more information. If ``null``, the default set will be used. 86 | 87 | .. php:method:: setWill($topic, $payload[, $qos = 0, $retain = false]) 88 | 89 | Set the client "last will and testament", which will be sent on an unclean disconnection from the broker. Must be called before :php:meth:`~Client::connect`. 90 | 91 | :param string $topic: The topic on which to publish the will. 92 | :param string $payload: The data to send. 93 | :param int $qos: Optional. Default 0. Integer 0, 1, or 2 indicating the Quality of Service to be used. 94 | :param boolean $retain: Optional. Default false. If ``true``, the message will be retained. 95 | 96 | .. php:method:: clearWill() 97 | 98 | Remove a previously-set will. No parameters. 99 | 100 | .. php:method:: setReconnectDelay($reconnectDelay, $exponentialDelay, $exponentialBackoff) 101 | 102 | Control the behaviour of the client when it has unexpectedly disconnected in :php:meth:`Client::loopForever`. The default behaviour if this method is not used is to repeatedly attempt to reconnect with a delay of 1 second until the connection succeeds. 103 | 104 | :param int $reconnectDelay: Set delay between successive reconnection attempts. 105 | :param int $exponentialDelay: Set max delay between successive reconnection attempts when exponential backoff is enabled 106 | :param bool $exponentialBackoff: Pass ``true`` to enable exponential backoff 107 | 108 | .. php:method:: connect($host[, $port = 1883, $keepalive = 60, $interface = null]) 109 | 110 | Connect to an MQTT broker. 111 | 112 | :param string $host: Hostname to connect to 113 | :param int $port: Optional. Port number to connect to. Defaults to 1883. 114 | :param int $keepalive: Optional. Number of sections after which the broker should PING the client if no messages have been recieved. 115 | :param string $interface: Optional. The address or hostname of a local interface to bind to for this connection. 116 | 117 | .. php:method:: disconnect() 118 | 119 | Disconnect from the broker. No parameters. 120 | 121 | .. php:method:: onConnect($callback) 122 | 123 | Set the connect callback. This is called when the broker sends a CONNACK message in response to a connection. 124 | 125 | :param callable $callback: The callback 126 | 127 | The callback should take parameters of the form: 128 | 129 | :param int $rc: Response code from the broker. 130 | :param string $message: String description of the response code. 131 | 132 | Response codes are as follows: 133 | 134 | ===== ==== 135 | Code Meaning 136 | ----- ---- 137 | 0 Success 138 | 1 Connection refused (unacceptable protocol version) 139 | 2 Connection refused (identifier rejected) 140 | 3 Connection refused (broker unavailable ) 141 | 4-255 Reserved for future use 142 | ===== ==== 143 | 144 | .. php:method:: onDisconnect($callback) 145 | 146 | Set the disconnect callback. This is called when the broker has received the DISCONNECT command and has disconnected the client. 147 | 148 | :param callable $callback: The callback 149 | 150 | The callback should take parameters of the form: 151 | 152 | :param int $rc: Reason for the disconnection. 0 means the client requested it. Any other value indicates an unexpected disconnection. 153 | 154 | .. php:method:: onLog($callback) 155 | 156 | Set the logging callback. 157 | 158 | :param callable $callback: The callback 159 | 160 | The callback should take parameters of the form: 161 | 162 | :param int $level: The log message level from the values below 163 | :param string $str: The message string. 164 | 165 | The level can be one of: 166 | 167 | * :php:const:`Client::LOG_DEBUG` 168 | * :php:const:`Client::LOG_INFO` 169 | * :php:const:`Client::LOG_NOTICE` 170 | * :php:const:`Client::LOG_WARNING` 171 | * :php:const:`Client::LOG_ERR` 172 | 173 | .. php:method:: onSubscribe($callback) 174 | 175 | Set the subscribe callback. This is called when the broker responds to a subscription request. 176 | 177 | :param callable $callback: The callback 178 | 179 | The callback should take parameters of the form: 180 | 181 | :param int $mid: Message ID of the subscribe message 182 | :param int $qosCount: Number of granted subscriptions 183 | 184 | This function needs to return the granted QoS for each subscription, but currently cannot. 185 | 186 | .. php:method:: onUnsubscribe($callback) 187 | 188 | Set the unsubscribe callback. This is called when the broker responds to a unsubscribe request. 189 | 190 | :param callable $callback: The callback 191 | 192 | The callback should take parameters of the form: 193 | 194 | :param int $mid: Message ID of the unsubscribe message 195 | 196 | .. php:method:: onMessage($callback) 197 | 198 | Set the message callback. This is called when a message is received from the broker. 199 | 200 | :param callable $callback: The callback 201 | 202 | The callback should take parameters of the form: 203 | 204 | :param :php:class:`Message` $message: A :php:class:`Message` object containing the message data 205 | 206 | .. php:method:: onPublish($callback) 207 | 208 | Set the publish callback. This is called when a message is published by the client itself. 209 | 210 | **Warning**: this may be called before the method :php:meth:`~Client::publish` returns the message id, so, you need to create a queue to deal with the MID list. 211 | 212 | :param callable $callback: The callback 213 | 214 | The callback should take parameters of the form: 215 | 216 | :param int $mid: the message id returned by :php:meth:`~Client::publish` 217 | 218 | .. php:method:: setMaxInFlightMessages($maxInFlightMessages) 219 | 220 | Set the number of QoS 1 and 2 messages that can be “in flight” at one time. An in flight message is part way through its delivery flow. Attempts to send further messages with :php:meth:`~Client::publish` will result in the messages being queued until the number of in flight messages reduces. 221 | 222 | Set to 0 for no maximum. 223 | 224 | :param int $maxInFlightMessages: The maximum 225 | 226 | .. php:method:: setMessageRetry($messageRetryPeriod) 227 | 228 | Set the number of seconds to wait before retrying messages. This applies to publishing messages with QoS>0. May be called at any time. 229 | 230 | :param int $messageRetryPeriod: The retry period 231 | 232 | .. php:method:: publish($topic, $payload[, $qos = 0, $retain = false]) 233 | 234 | Publish a message on a given topic. 235 | 236 | :param string $topic: The topic to publish on 237 | :param string $payload: The message payload 238 | :param int $qos: Integer value 0, 1 or 2 indicating the QoS for this message 239 | :param boolean $retain: If ``true``, retain this message 240 | :returns: The message ID returned by the broker. **Warning**: the message ID is not unique. 241 | :returntype: int 242 | 243 | .. php:method:: subscribe($topic, $qos) 244 | 245 | Subscribe to a topic. 246 | 247 | :param string $topic: The topic. 248 | :param int $qos: The QoS to request for this subscription 249 | 250 | :returns: The message ID of the subscription message, so this can be matched up in the :php:meth:`~Client::onSubscribe` callback. 251 | :returntype: int 252 | 253 | .. php:method:: unsubscribe() 254 | 255 | Unsubscribe from a topic. 256 | 257 | :param string $topic: The topic. 258 | :param int $qos: The QoS to request for this subscription 259 | 260 | :returns: the message ID of the subscription message, so this can be matched up in the :php:meth:`~Client::onUnsubscribe` callback. 261 | :returntype: int 262 | 263 | .. php:method:: loop([$timeout = 1000]) 264 | 265 | The main network loop for the client. You must call this frequently in order to keep communications between the client and broker working. If incoming data is present it will then be processed. Outgoing commands, from e.g. :php:meth:`~Client::publish`, are normally sent immediately that their function is called, but this is not always possible. :php:meth:`~Client::loop` will also attempt to send any remaining outgoing messages, which also includes commands that are part of the flow for messages with QoS>0. 266 | 267 | :param int $timeout: Optional. Number of milliseconds to wait for network activity. Pass 0 for instant timeout. Defaults to 1000. 268 | 269 | .. php:method:: loopForever([$timeout = 1000]) 270 | 271 | Call loop() in an infinite blocking loop. Callbacks will be called as required. This will handle reconnecting if the connection is lost. Call :php:meth:`~Client::disconnect` in a callback to disconnect and return from the loop. Alternatively, call :php:meth:`~Client::exitLoop` to exit the loop without disconnecting. You will need to re-enter the loop again afterwards to maintain the connection. 272 | 273 | :param int $timeout: Optional. Number of milliseconds to wait for network activity. Pass 0 for instant timeout. Defaults to 1000. 274 | 275 | .. php:method:: exitLoop() 276 | 277 | Exit the :php:meth:`~Client::loopForever` event loop without disconnecting. You will need to re-enter the loop afterwards in order to maintain the connection. 278 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Mosquitto-PHP documentation build configuration file, created by 5 | # sphinx-quickstart on Mon Sep 5 20:57:18 2016. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | # import os 21 | # import sys 22 | # sys.path.insert(0, os.path.abspath('.')) 23 | 24 | # Set up PHP syntax highlights 25 | from sphinx.highlighting import lexers 26 | from pygments.lexers.web import PhpLexer 27 | lexers["php"] = PhpLexer(startinline=True, linenos=1) 28 | lexers["php-annotations"] = PhpLexer(startinline=True, linenos=1) 29 | primary_domain = "php" 30 | 31 | # -- General configuration ------------------------------------------------ 32 | 33 | # If your documentation needs a minimal Sphinx version, state it here. 34 | # 35 | # needs_sphinx = '1.0' 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = ["sphinxcontrib.phpdomain"] 41 | 42 | # Add any paths that contain templates here, relative to this directory. 43 | templates_path = ['_templates'] 44 | 45 | # The suffix(es) of source filenames. 46 | # You can specify multiple suffix as a list of string: 47 | # 48 | # source_suffix = ['.rst', '.md'] 49 | source_suffix = '.rst' 50 | 51 | # The encoding of source files. 52 | # 53 | # source_encoding = 'utf-8-sig' 54 | 55 | # The master toctree document. 56 | master_doc = 'index' 57 | 58 | # General information about the project. 59 | project = 'Mosquitto-PHP' 60 | copyright = '2016, Michael Maclean' 61 | author = 'Michael Maclean' 62 | 63 | # The version info for the project you're documenting, acts as replacement for 64 | # |version| and |release|, also used in various other places throughout the 65 | # built documents. 66 | # 67 | # The short X.Y version. 68 | version = '0.4' 69 | # The full version, including alpha/beta/rc tags. 70 | release = '0.4.0' 71 | 72 | # The language for content autogenerated by Sphinx. Refer to documentation 73 | # for a list of supported languages. 74 | # 75 | # This is also used if you do content translation via gettext catalogs. 76 | # Usually you set "language" from the command line for these cases. 77 | language = None 78 | 79 | # There are two options for replacing |today|: either, you set today to some 80 | # non-false value, then it is used: 81 | # 82 | # today = '' 83 | # 84 | # Else, today_fmt is used as the format for a strftime call. 85 | # 86 | # today_fmt = '%B %d, %Y' 87 | 88 | # List of patterns, relative to source directory, that match files and 89 | # directories to ignore when looking for source files. 90 | # This patterns also effect to html_static_path and html_extra_path 91 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 92 | 93 | # The reST default role (used for this markup: `text`) to use for all 94 | # documents. 95 | # 96 | # default_role = None 97 | 98 | # If true, '()' will be appended to :func: etc. cross-reference text. 99 | # 100 | # add_function_parentheses = True 101 | 102 | # If true, the current module name will be prepended to all description 103 | # unit titles (such as .. function::). 104 | # 105 | # add_module_names = True 106 | 107 | # If true, sectionauthor and moduleauthor directives will be shown in the 108 | # output. They are ignored by default. 109 | # 110 | # show_authors = False 111 | 112 | # The name of the Pygments (syntax highlighting) style to use. 113 | pygments_style = 'sphinx' 114 | 115 | # A list of ignored prefixes for module index sorting. 116 | # modindex_common_prefix = [] 117 | 118 | # If true, keep warnings as "system message" paragraphs in the built documents. 119 | # keep_warnings = False 120 | 121 | # If true, `todo` and `todoList` produce output, else they produce nothing. 122 | todo_include_todos = False 123 | 124 | 125 | # -- Options for HTML output ---------------------------------------------- 126 | 127 | # The theme to use for HTML and HTML Help pages. See the documentation for 128 | # a list of builtin themes. 129 | # 130 | #html_theme = 'alabaster' 131 | 132 | html_context = { 133 | "display_github": True, 134 | "github_user": "mgdm", 135 | "github_repo": project, 136 | "github_version": "master", 137 | "conf_py_path": "/docs/", 138 | "source_suffix": source_suffix, 139 | } 140 | 141 | # Theme options are theme-specific and customize the look and feel of a theme 142 | # further. For a list of options available for each theme, see the 143 | # documentation. 144 | # 145 | # html_theme_options = {} 146 | 147 | # Add any paths that contain custom themes here, relative to this directory. 148 | # html_theme_path = [] 149 | 150 | # The name for this set of Sphinx documents. 151 | # " v documentation" by default. 152 | # 153 | # html_title = 'Mosquitto-PHP v0.4.0' 154 | 155 | # A shorter title for the navigation bar. Default is the same as html_title. 156 | # 157 | # html_short_title = None 158 | 159 | # The name of an image file (relative to this directory) to place at the top 160 | # of the sidebar. 161 | # 162 | # html_logo = None 163 | 164 | # The name of an image file (relative to this directory) to use as a favicon of 165 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 166 | # pixels large. 167 | # 168 | # html_favicon = None 169 | 170 | # Add any paths that contain custom static files (such as style sheets) here, 171 | # relative to this directory. They are copied after the builtin static files, 172 | # so a file named "default.css" will overwrite the builtin "default.css". 173 | html_static_path = ['_static'] 174 | 175 | # Add any extra paths that contain custom files (such as robots.txt or 176 | # .htaccess) here, relative to this directory. These files are copied 177 | # directly to the root of the documentation. 178 | # 179 | # html_extra_path = [] 180 | 181 | # If not None, a 'Last updated on:' timestamp is inserted at every page 182 | # bottom, using the given strftime format. 183 | # The empty string is equivalent to '%b %d, %Y'. 184 | # 185 | # html_last_updated_fmt = None 186 | 187 | # If true, SmartyPants will be used to convert quotes and dashes to 188 | # typographically correct entities. 189 | # 190 | # html_use_smartypants = True 191 | 192 | # Custom sidebar templates, maps document names to template names. 193 | # 194 | # html_sidebars = {} 195 | 196 | # Additional templates that should be rendered to pages, maps page names to 197 | # template names. 198 | # 199 | # html_additional_pages = {} 200 | 201 | # If false, no module index is generated. 202 | # 203 | # html_domain_indices = True 204 | 205 | # If false, no index is generated. 206 | # 207 | # html_use_index = True 208 | 209 | # If true, the index is split into individual pages for each letter. 210 | # 211 | # html_split_index = False 212 | 213 | # If true, links to the reST sources are added to the pages. 214 | # 215 | # html_show_sourcelink = True 216 | 217 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 218 | # 219 | # html_show_sphinx = True 220 | 221 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 222 | # 223 | # html_show_copyright = True 224 | 225 | # If true, an OpenSearch description file will be output, and all pages will 226 | # contain a tag referring to it. The value of this option must be the 227 | # base URL from which the finished HTML is served. 228 | # 229 | # html_use_opensearch = '' 230 | 231 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 232 | # html_file_suffix = None 233 | 234 | # Language to be used for generating the HTML full-text search index. 235 | # Sphinx supports the following languages: 236 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 237 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 238 | # 239 | # html_search_language = 'en' 240 | 241 | # A dictionary with options for the search language support, empty by default. 242 | # 'ja' uses this config value. 243 | # 'zh' user can custom change `jieba` dictionary path. 244 | # 245 | # html_search_options = {'type': 'default'} 246 | 247 | # The name of a javascript file (relative to the configuration directory) that 248 | # implements a search results scorer. If empty, the default will be used. 249 | # 250 | # html_search_scorer = 'scorer.js' 251 | 252 | # Output file base name for HTML help builder. 253 | htmlhelp_basename = 'Mosquitto-PHPdoc' 254 | 255 | # -- Options for LaTeX output --------------------------------------------- 256 | 257 | latex_elements = { 258 | # The paper size ('letterpaper' or 'a4paper'). 259 | # 260 | # 'papersize': 'letterpaper', 261 | 262 | # The font size ('10pt', '11pt' or '12pt'). 263 | # 264 | # 'pointsize': '10pt', 265 | 266 | # Additional stuff for the LaTeX preamble. 267 | # 268 | # 'preamble': '', 269 | 270 | # Latex figure (float) alignment 271 | # 272 | # 'figure_align': 'htbp', 273 | } 274 | 275 | # Grouping the document tree into LaTeX files. List of tuples 276 | # (source start file, target name, title, 277 | # author, documentclass [howto, manual, or own class]). 278 | latex_documents = [ 279 | (master_doc, 'Mosquitto-PHP.tex', 'Mosquitto-PHP Documentation', 280 | 'Michael Maclean', 'manual'), 281 | ] 282 | 283 | # The name of an image file (relative to this directory) to place at the top of 284 | # the title page. 285 | # 286 | # latex_logo = None 287 | 288 | # For "manual" documents, if this is true, then toplevel headings are parts, 289 | # not chapters. 290 | # 291 | # latex_use_parts = False 292 | 293 | # If true, show page references after internal links. 294 | # 295 | # latex_show_pagerefs = False 296 | 297 | # If true, show URL addresses after external links. 298 | # 299 | # latex_show_urls = False 300 | 301 | # Documents to append as an appendix to all manuals. 302 | # 303 | # latex_appendices = [] 304 | 305 | # It false, will not define \strong, \code, itleref, \crossref ... but only 306 | # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added 307 | # packages. 308 | # 309 | # latex_keep_old_macro_names = True 310 | 311 | # If false, no module index is generated. 312 | # 313 | # latex_domain_indices = True 314 | 315 | 316 | # -- Options for manual page output --------------------------------------- 317 | 318 | # One entry per manual page. List of tuples 319 | # (source start file, name, description, authors, manual section). 320 | man_pages = [ 321 | (master_doc, 'mosquitto-php', 'Mosquitto-PHP Documentation', 322 | [author], 1) 323 | ] 324 | 325 | # If true, show URL addresses after external links. 326 | # 327 | # man_show_urls = False 328 | 329 | 330 | # -- Options for Texinfo output ------------------------------------------- 331 | 332 | # Grouping the document tree into Texinfo files. List of tuples 333 | # (source start file, target name, title, author, 334 | # dir menu entry, description, category) 335 | texinfo_documents = [ 336 | (master_doc, 'Mosquitto-PHP', 'Mosquitto-PHP Documentation', 337 | author, 'Mosquitto-PHP', 'One line description of project.', 338 | 'Miscellaneous'), 339 | ] 340 | 341 | # Documents to append as an appendix to all manuals. 342 | # 343 | # texinfo_appendices = [] 344 | 345 | # If false, no module index is generated. 346 | # 347 | # texinfo_domain_indices = True 348 | 349 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 350 | # 351 | # texinfo_show_urls = 'footnote' 352 | 353 | # If true, do not generate a @detailmenu in the "Top" node's menu. 354 | # 355 | # texinfo_no_detailmenu = False 356 | -------------------------------------------------------------------------------- /docs/exception.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Mosquitto\\Exception 3 | ==================== 4 | 5 | .. php:namespace:: Mosquitto 6 | 7 | .. php:class:: Exception 8 | 9 | This is an exception that may be thrown by many of the operations in the ``Client`` object. It does not add any features to the standard PHP ``Exception`` class. 10 | 11 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Mosquitto-PHP documentation master file, created by 2 | sphinx-quickstart on Mon Sep 5 20:57:18 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ============= 7 | Mosquitto-PHP 8 | ============= 9 | 10 | This is an extension to allow using the `Eclipse Mosquitto™ MQTT client library `_ with PHP. 11 | 12 | See the ``examples`` directory for usage. 13 | 14 | Contents 15 | ======== 16 | 17 | .. toctree:: 18 | :maxdepth: 2 19 | 20 | overview 21 | client 22 | message 23 | exception 24 | 25 | 26 | 27 | Indices and tables 28 | ================== 29 | 30 | * :ref:`genindex` 31 | * :ref:`modindex` 32 | * :ref:`search` 33 | 34 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. epub3 to make an epub3 31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 32 | echo. text to make text files 33 | echo. man to make manual pages 34 | echo. texinfo to make Texinfo files 35 | echo. gettext to make PO message catalogs 36 | echo. changes to make an overview over all changed/added/deprecated items 37 | echo. xml to make Docutils-native XML files 38 | echo. pseudoxml to make pseudoxml-XML files for display purposes 39 | echo. linkcheck to check all external links for integrity 40 | echo. doctest to run all doctests embedded in the documentation if enabled 41 | echo. coverage to run coverage check of the documentation if enabled 42 | echo. dummy to check syntax errors of document sources 43 | goto end 44 | ) 45 | 46 | if "%1" == "clean" ( 47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 48 | del /q /s %BUILDDIR%\* 49 | goto end 50 | ) 51 | 52 | 53 | REM Check if sphinx-build is available and fallback to Python version if any 54 | %SPHINXBUILD% 1>NUL 2>NUL 55 | if errorlevel 9009 goto sphinx_python 56 | goto sphinx_ok 57 | 58 | :sphinx_python 59 | 60 | set SPHINXBUILD=python -m sphinx.__init__ 61 | %SPHINXBUILD% 2> nul 62 | if errorlevel 9009 ( 63 | echo. 64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 65 | echo.installed, then set the SPHINXBUILD environment variable to point 66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 67 | echo.may add the Sphinx directory to PATH. 68 | echo. 69 | echo.If you don't have Sphinx installed, grab it from 70 | echo.http://sphinx-doc.org/ 71 | exit /b 1 72 | ) 73 | 74 | :sphinx_ok 75 | 76 | 77 | if "%1" == "html" ( 78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 79 | if errorlevel 1 exit /b 1 80 | echo. 81 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 82 | goto end 83 | ) 84 | 85 | if "%1" == "dirhtml" ( 86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 87 | if errorlevel 1 exit /b 1 88 | echo. 89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 90 | goto end 91 | ) 92 | 93 | if "%1" == "singlehtml" ( 94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 95 | if errorlevel 1 exit /b 1 96 | echo. 97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 98 | goto end 99 | ) 100 | 101 | if "%1" == "pickle" ( 102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 103 | if errorlevel 1 exit /b 1 104 | echo. 105 | echo.Build finished; now you can process the pickle files. 106 | goto end 107 | ) 108 | 109 | if "%1" == "json" ( 110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 111 | if errorlevel 1 exit /b 1 112 | echo. 113 | echo.Build finished; now you can process the JSON files. 114 | goto end 115 | ) 116 | 117 | if "%1" == "htmlhelp" ( 118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 119 | if errorlevel 1 exit /b 1 120 | echo. 121 | echo.Build finished; now you can run HTML Help Workshop with the ^ 122 | .hhp project file in %BUILDDIR%/htmlhelp. 123 | goto end 124 | ) 125 | 126 | if "%1" == "qthelp" ( 127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 128 | if errorlevel 1 exit /b 1 129 | echo. 130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 131 | .qhcp project file in %BUILDDIR%/qthelp, like this: 132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Mosquitto-PHP.qhcp 133 | echo.To view the help file: 134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Mosquitto-PHP.ghc 135 | goto end 136 | ) 137 | 138 | if "%1" == "devhelp" ( 139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 140 | if errorlevel 1 exit /b 1 141 | echo. 142 | echo.Build finished. 143 | goto end 144 | ) 145 | 146 | if "%1" == "epub" ( 147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 148 | if errorlevel 1 exit /b 1 149 | echo. 150 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 151 | goto end 152 | ) 153 | 154 | if "%1" == "epub3" ( 155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 156 | if errorlevel 1 exit /b 1 157 | echo. 158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 159 | goto end 160 | ) 161 | 162 | if "%1" == "latex" ( 163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 164 | if errorlevel 1 exit /b 1 165 | echo. 166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdf" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "latexpdfja" ( 181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 182 | cd %BUILDDIR%/latex 183 | make all-pdf-ja 184 | cd %~dp0 185 | echo. 186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 187 | goto end 188 | ) 189 | 190 | if "%1" == "text" ( 191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 192 | if errorlevel 1 exit /b 1 193 | echo. 194 | echo.Build finished. The text files are in %BUILDDIR%/text. 195 | goto end 196 | ) 197 | 198 | if "%1" == "man" ( 199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 200 | if errorlevel 1 exit /b 1 201 | echo. 202 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 203 | goto end 204 | ) 205 | 206 | if "%1" == "texinfo" ( 207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 208 | if errorlevel 1 exit /b 1 209 | echo. 210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 211 | goto end 212 | ) 213 | 214 | if "%1" == "gettext" ( 215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 216 | if errorlevel 1 exit /b 1 217 | echo. 218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 219 | goto end 220 | ) 221 | 222 | if "%1" == "changes" ( 223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 224 | if errorlevel 1 exit /b 1 225 | echo. 226 | echo.The overview file is in %BUILDDIR%/changes. 227 | goto end 228 | ) 229 | 230 | if "%1" == "linkcheck" ( 231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 232 | if errorlevel 1 exit /b 1 233 | echo. 234 | echo.Link check complete; look for any errors in the above output ^ 235 | or in %BUILDDIR%/linkcheck/output.txt. 236 | goto end 237 | ) 238 | 239 | if "%1" == "doctest" ( 240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 241 | if errorlevel 1 exit /b 1 242 | echo. 243 | echo.Testing of doctests in the sources finished, look at the ^ 244 | results in %BUILDDIR%/doctest/output.txt. 245 | goto end 246 | ) 247 | 248 | if "%1" == "coverage" ( 249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 250 | if errorlevel 1 exit /b 1 251 | echo. 252 | echo.Testing of coverage in the sources finished, look at the ^ 253 | results in %BUILDDIR%/coverage/python.txt. 254 | goto end 255 | ) 256 | 257 | if "%1" == "xml" ( 258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 259 | if errorlevel 1 exit /b 1 260 | echo. 261 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 262 | goto end 263 | ) 264 | 265 | if "%1" == "pseudoxml" ( 266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 267 | if errorlevel 1 exit /b 1 268 | echo. 269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 270 | goto end 271 | ) 272 | 273 | if "%1" == "dummy" ( 274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy 275 | if errorlevel 1 exit /b 1 276 | echo. 277 | echo.Build finished. Dummy builder generates no files. 278 | goto end 279 | ) 280 | 281 | :end 282 | -------------------------------------------------------------------------------- /docs/message.rst: -------------------------------------------------------------------------------- 1 | ================== 2 | Mosquitto\\Message 3 | ================== 4 | 5 | .. php:namespace:: Mosquitto 6 | 7 | .. php:class:: Message 8 | 9 | Represents a message received from a broker. All data is represented as properties. 10 | 11 | .. php:attr:: $topic 12 | 13 | (*string*) The topic this message was delivered to. 14 | 15 | .. php:attr:: $payload 16 | 17 | (*string*) The payload of this message. 18 | 19 | .. php:attr:: $mid 20 | 21 | (*int*) The ID of this message. 22 | 23 | .. php:attr:: $qos 24 | 25 | (*int*) The QoS value applied to this message. 26 | 27 | .. php:attr:: $retain 28 | 29 | (*boolean*) Whether this is a retained message or not. 30 | 31 | This class has two static methods. 32 | 33 | .. php:staticmethod:: topicMatchesSub() 34 | 35 | Returns true if the supplied topic matches the supplied description, and otherwise false. 36 | 37 | :param string $topic: The topic to match 38 | :param string $subscription: The subscription to match 39 | 40 | .. php:staticmethod:: tokeniseTopic() 41 | 42 | Tokenise a topic or subscription string into an array of strings representing the topic hierarchy. 43 | 44 | :param string $topic: The topic to tokenise 45 | -------------------------------------------------------------------------------- /docs/overview.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Overview 3 | ======== 4 | 5 | Requirements 6 | ============ 7 | 8 | * PHP 5.3 or newer, including PHP 7+ 9 | * libmosquitto 1.2.x or later 10 | 11 | Installation 12 | ============ 13 | 14 | If you've used a pre-built package to install Mosquitto, you need to make sure you have the development headers installed. On Red Hat-derived systems, this is probably called ``libmosquitto-devel``, and on Debian-based systems it will be ``libmosquitto-dev``. 15 | 16 | You may obtain this package using `PECL `_:: 17 | 18 | pecl install Mosquitto-alpha 19 | 20 | Alternatively, you can use the normal extension build process:: 21 | 22 | phpize 23 | ./configure --with-mosquitto=/path/to/libmosquitto 24 | make 25 | make install 26 | 27 | Then add ``extension=mosquitto.so`` to your ``php.ini``. 28 | 29 | The ``--with-mosquitto`` argument is optional, and only required if your 30 | libmosquitto install cannot be found. 31 | 32 | General Operation 33 | ================= 34 | 35 | The underlying library is based on callbacks and event-driven operation. As such, you have to call the ``loop()`` method of the ``Client`` frequently to permit the library to handle the messages in its queues. You can use ``loopForever()`` to ensure that the client handles this itself. Also, you should use the callback functions to ensure that you only attempt to publish after the client has connected, etc. For example, here is how you would correctly publish a QoS=2 message: 36 | 37 | .. code-block:: php 38 | 39 | onConnect(function() use ($c) { 43 | $c->publish('mgdm/test', 'Hello', 2); 44 | $c->disconnect(); 45 | }); 46 | 47 | $c->connect('test.mosquitto.org'); 48 | 49 | // Loop around to permit the library to do its work 50 | // This function will call the callback defined in `onConnect()` 51 | // and disconnect cleanly when the message has been sent 52 | $c->loopForever(); 53 | 54 | echo "Finished\n"; 55 | 56 | 57 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinxcontrib-phpdomain==0.2.0 2 | -------------------------------------------------------------------------------- /examples/event.php: -------------------------------------------------------------------------------- 1 | onConnect(function($code, $message) { 5 | echo "I'm connected\n"; 6 | }); 7 | 8 | $c->connect('localhost', 1883, 60); 9 | $c->subscribe('#', 1); 10 | $c->onMessage(function($m) { 11 | var_dump($m); 12 | }); 13 | 14 | $socket = $c->getSocket(); 15 | 16 | $base = new EventBase(); 17 | $ev = new Event($base, $socket, Event::READ | Event::PERSIST, 'cb', $base); 18 | 19 | function cb($fd, $what, $arg) { 20 | global $c; 21 | echo "Triggered\n"; 22 | var_dump(func_get_args()); 23 | $c->loop(); 24 | } 25 | 26 | $ev->add(); 27 | $base->dispatch(); 28 | -------------------------------------------------------------------------------- /examples/pub.php: -------------------------------------------------------------------------------- 1 | onConnect('connect'); 5 | $client->onDisconnect('disconnect'); 6 | $client->onSubscribe('subscribe'); 7 | $client->onMessage('message'); 8 | $client->connect("localhost", 1883, 5); 9 | $client->subscribe('/#', 1); 10 | 11 | while (true) { 12 | $client->loop(); 13 | $mid = $client->publish('/hello', "Hello from PHP at " . date('Y-m-d H:i:s'), 1, 0); 14 | echo "Sent message ID: {$mid}\n"; 15 | $client->loop(); 16 | 17 | sleep(2); 18 | } 19 | 20 | $client->disconnect(); 21 | unset($client); 22 | 23 | function connect($r) { 24 | echo "I got code {$r}\n"; 25 | } 26 | 27 | function subscribe() { 28 | echo "Subscribed to a topic\n"; 29 | } 30 | 31 | function message($message) { 32 | printf("Got a message ID %d on topic %s with payload:\n%s\n\n", $message->mid, $message->topic, $message->payload); 33 | } 34 | 35 | function disconnect() { 36 | echo "Disconnected cleanly\n"; 37 | } 38 | -------------------------------------------------------------------------------- /examples/subclass.php: -------------------------------------------------------------------------------- 1 | pendingSubs[$mid])) { 17 | return; 18 | } 19 | 20 | $topic = $this->pendingSubs[$mid]; 21 | $this->grantedSubs[$topic] = $grantedQos; 22 | echo "Subscribed to topic {$topic} with message ID {$mid}\n"; 23 | 24 | if (is_callable($this->subscribeCallback)) { 25 | $this->subscribeCallback($mid, $qosCount, $grantedQos); 26 | } 27 | } 28 | 29 | public function subscribe($topic, $qos) { 30 | $mid = parent::subscribe($topic, $qos); 31 | $this->pendingSubs[$mid] = $topic; 32 | } 33 | 34 | public function onSubscribe(callable $callable) { 35 | $this->subscribeCallback = $callable; 36 | } 37 | 38 | public function getSubscriptions() { 39 | return $this->grantedSubs; 40 | } 41 | } 42 | 43 | $c = new MyClient('subscriptionTest'); 44 | $c->onSubscribe(function() { echo "Hello, I got subscribed\n"; }); 45 | $c->connect('localhost', 1883, 50); 46 | $c->subscribe('#', 1); 47 | 48 | for ($i = 0; $i < 5; $i++) { 49 | $c->loop(10); 50 | } 51 | 52 | var_dump($c->getSubscriptions()); 53 | 54 | -------------------------------------------------------------------------------- /examples/test.php: -------------------------------------------------------------------------------- 1 | onConnect('connect'); 5 | $client->onDisconnect('disconnect'); 6 | $client->onSubscribe('subscribe'); 7 | $client->onMessage('message'); 8 | $client->connect("localhost", 1883, 5); 9 | $client->onLog('logger'); 10 | $client->subscribe('#', 1); 11 | 12 | for ($i = 0; $i < 10; $i++) { 13 | $client->loop(); 14 | } 15 | 16 | $client->unsubscribe('#'); 17 | 18 | for ($i = 0; $i < 10; $i++) { 19 | $client->loop(); 20 | } 21 | 22 | function connect($r, $message) { 23 | echo "I got code {$r} and message {$message}\n"; 24 | } 25 | 26 | function subscribe() { 27 | echo "Subscribed to a topic\n"; 28 | } 29 | 30 | function unsubscribe() { 31 | echo "Unsubscribed from a topic\n"; 32 | } 33 | 34 | function message($message) { 35 | printf("Got a message on topic %s with payload:\n%s\n", $message->topic, $message->payload); 36 | } 37 | 38 | function disconnect() { 39 | echo "Disconnected cleanly\n"; 40 | } 41 | 42 | function logger() { 43 | var_dump(func_get_args()); 44 | } 45 | -------------------------------------------------------------------------------- /examples/testOnpublish.php: -------------------------------------------------------------------------------- 1 | id = $mid; 7 | self::$publish[$mid] = $msg; 8 | } 9 | 10 | public static function confirm($mid) { 11 | if(array_key_exists($mid, self::$publish)) { 12 | self::$publish[$mid]->state = true; 13 | } 14 | } 15 | 16 | public static function addReceive($msg) { 17 | $msg = Message::factory($msg, true); 18 | self::$receive[$msg->id] = $msg; 19 | } 20 | } 21 | 22 | class Message { 23 | public $id; 24 | public $state = false; 25 | public $msg; 26 | public static function factory(Mosquitto\Message $msg, $state = false) { 27 | $message = new Message(); 28 | $message->state = $state; 29 | $message->msg = $msg; 30 | $message->id = $msg->mid; 31 | return $message; 32 | } 33 | } 34 | $client = new Mosquitto\Client('client.terminal.onpublish', false); 35 | 36 | $client->onMessage(function($msg) { 37 | print_r(array('receive', $msg)); 38 | MQ::addReceive($msg); 39 | }); 40 | 41 | $client->onPublish(function($mid) { 42 | MQ::confirm($mid); 43 | print_r(array('comfirm publish', MQ::$publish[$mid])); 44 | }); 45 | $client->onConnect(function($rc, $msg) { 46 | print_r(array('rc' => $rc, 'message' => $msg)); 47 | }); 48 | 49 | $client->connect('localhost', 1883, 60); 50 | 51 | sleep(1); 52 | 53 | 54 | $client->subscribe('/test/publish', 1); 55 | $msg = Message::factory(new Mosquitto\Message()); 56 | $msg->msg->topic = '/test/publish'; 57 | $msg->msg->payload = 'hello from on publish'; 58 | $msg->msg->qos = 1; 59 | $mid = $client->publish($msg->msg->topic, $msg->msg->payload, $msg->msg->qos); 60 | print_r(array('publish', $msg)); 61 | MQ::addPublish($mid, $msg); 62 | 63 | sleep(1); 64 | 65 | $client->loopForever(); -------------------------------------------------------------------------------- /examples/testwill.php: -------------------------------------------------------------------------------- 1 | onConnect('connect'); 5 | $client->onDisconnect('disconnect'); 6 | $client->onSubscribe('subscribe'); 7 | $client->onMessage('message'); 8 | $client->setWill('/hello', "Client died :-(", 1, 0); 9 | $client->connect("localhost", 1883, 5); 10 | $client->subscribe('/#', 1); 11 | 12 | $client->loopForever(); 13 | 14 | function connect($r) { 15 | echo "I got code {$r}\n"; 16 | } 17 | 18 | function subscribe() { 19 | echo "Subscribed to a topic\n"; 20 | } 21 | 22 | function message($message) { 23 | printf("Got a message on topic %s with payload:\n%s\n", $message->topic, $message->payload); 24 | } 25 | 26 | function disconnect() { 27 | echo "Disconnected cleanly\n"; 28 | } 29 | -------------------------------------------------------------------------------- /mosquitto.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include "php.h" 6 | #include "php_ini.h" 7 | #include "zend_variables.h" 8 | #include "zend_exceptions.h" 9 | #include "zend_API.h" 10 | #include "ext/standard/php_filestat.h" 11 | #include "ext/standard/info.h" 12 | #include "php_mosquitto.h" 13 | 14 | zend_class_entry *mosquitto_ce_client; 15 | zend_class_entry *mosquitto_ce_exception; 16 | zend_object_handlers mosquitto_std_object_handlers; 17 | 18 | ZEND_DECLARE_MODULE_GLOBALS(mosquitto) 19 | 20 | #ifdef ZEND_ENGINE_3 21 | # ifndef Z_BVAL 22 | # define Z_BVAL(zv) (Z_TYPE(zv) == IS_TRUE) 23 | # define Z_BVAL_P(pzv) Z_BVAL(*pzv) 24 | # endif 25 | # define ZO_HANDLE_DC 26 | typedef size_t mosquitto_strlen_type; 27 | #else /* ZEND_ENGINE_2 */ 28 | # ifndef Z_OBJ_P 29 | # define Z_OBJ_P(pzv) ((zend_object*)zend_object_store_get_object(pzv TSRMLS_CC)) 30 | # endif 31 | # define ZO_HANDLE_DC , zend_object_handle handle 32 | typedef int mosquitto_strlen_type; 33 | typedef long zend_long; 34 | #endif 35 | 36 | static inline mosquitto_client_object *mosquitto_client_object_get(zval *zobj TSRMLS_DC) { 37 | // TODO: ZEND_ASSERT()s 38 | mosquitto_client_object *obj = mosquitto_client_object_from_zend_object(Z_OBJ_P(zobj)); 39 | if (!obj->client) { 40 | php_error(E_ERROR, "Internal surface object missing in %s wrapper, " 41 | "you must call parent::__construct in extended classes", Z_OBJCE_P(zobj)->name); 42 | } 43 | return obj; 44 | } 45 | 46 | static inline void mosquitto_callback_addref(zend_fcall_info *finfo) { 47 | #ifdef ZEND_ENGINE_3 48 | zval tmp; 49 | Z_TRY_ADDREF(finfo->function_name); 50 | if (finfo->object) { 51 | ZVAL_OBJ(&tmp, finfo->object); 52 | Z_TRY_ADDREF(tmp); 53 | } 54 | #else 55 | Z_ADDREF_P(finfo->function_name); 56 | if (finfo->object_ptr) { 57 | Z_ADDREF_P(finfo->object_ptr); 58 | } 59 | #endif 60 | } 61 | 62 | static inline const char *mosquitto_finfo_name(zend_fcall_info *info) { 63 | #ifdef ZEND_ENGINE_3 64 | return Z_STRVAL(info->function_name); 65 | #else 66 | return Z_STRVAL_P(info->function_name); 67 | #endif 68 | } 69 | 70 | static int php_mosquitto_pw_callback(char *buf, int size, int rwflag, void *userdata); 71 | 72 | /* {{{ Arginfo */ 73 | 74 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client___construct_args, ZEND_SEND_BY_VAL) 75 | ZEND_ARG_INFO(0, id) 76 | ZEND_ARG_INFO(0, cleanSession) 77 | ZEND_END_ARG_INFO() 78 | 79 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_callback_args, ZEND_SEND_BY_VAL) 80 | #if PHP_VERSION_ID > 50400 81 | ZEND_ARG_TYPE_INFO(0, onConnect, IS_CALLABLE, 0) 82 | #else 83 | ZEND_ARG_INFO(0, onConnect) 84 | #endif 85 | ZEND_END_ARG_INFO() 86 | 87 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_setCredentials_args, ZEND_SEND_BY_VAL) 88 | ZEND_ARG_INFO(0, username) 89 | ZEND_ARG_INFO(0, password) 90 | ZEND_END_ARG_INFO() 91 | 92 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_setWill_args, ZEND_SEND_BY_VAL) 93 | ZEND_ARG_INFO(0, topic) 94 | ZEND_ARG_INFO(0, payload) 95 | ZEND_ARG_INFO(0, qos) 96 | ZEND_ARG_INFO(0, retain) 97 | ZEND_END_ARG_INFO() 98 | 99 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_setReconnectDelay_args, ZEND_SEND_BY_VAL) 100 | ZEND_ARG_INFO(0, reconnectDelay) 101 | ZEND_ARG_INFO(0, maxReconnectDelay) 102 | ZEND_ARG_INFO(0, exponentialBackoff) 103 | ZEND_END_ARG_INFO() 104 | 105 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_setMessageRetry_args, ZEND_SEND_BY_VAL) 106 | ZEND_ARG_INFO(0, messageRetry) 107 | ZEND_END_ARG_INFO() 108 | 109 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_connect_args, ZEND_SEND_BY_VAL) 110 | ZEND_ARG_INFO(0, host) 111 | ZEND_ARG_INFO(0, port) 112 | ZEND_ARG_INFO(0, keepalive) 113 | ZEND_ARG_INFO(0, interface) 114 | ZEND_END_ARG_INFO() 115 | 116 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_setMaxInFlightMessages_args, ZEND_SEND_BY_VAL) 117 | ZEND_ARG_INFO(0, max) 118 | ZEND_END_ARG_INFO() 119 | 120 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_publish_args, ZEND_SEND_BY_VAL) 121 | ZEND_ARG_INFO(0, topic) 122 | ZEND_ARG_INFO(0, payload) 123 | ZEND_ARG_INFO(0, qos) 124 | ZEND_ARG_INFO(0, retain) 125 | ZEND_END_ARG_INFO() 126 | 127 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_subscribe_args, ZEND_SEND_BY_VAL) 128 | ZEND_ARG_INFO(0, topic) 129 | ZEND_ARG_INFO(0, qos) 130 | ZEND_END_ARG_INFO() 131 | 132 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_unsubscribe_args, ZEND_SEND_BY_VAL) 133 | ZEND_ARG_INFO(0, topic) 134 | ZEND_END_ARG_INFO() 135 | 136 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_loop_args, ZEND_SEND_BY_VAL) 137 | ZEND_ARG_INFO(0, timeout) 138 | ZEND_ARG_INFO(0, maxPackets) 139 | ZEND_END_ARG_INFO() 140 | 141 | ZEND_BEGIN_ARG_INFO(Mosquitto_Client_loopForever_args, ZEND_SEND_BY_VAL) 142 | ZEND_ARG_INFO(0, timeout) 143 | ZEND_ARG_INFO(0, maxPackets) 144 | ZEND_END_ARG_INFO() 145 | 146 | /* }}} */ 147 | 148 | /* {{{ Mosquitto\Client::__construct() */ 149 | PHP_METHOD(Mosquitto_Client, __construct) 150 | { 151 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 152 | char *id = NULL; 153 | mosquitto_strlen_type id_len = 0; 154 | zend_bool clean_session = 1; 155 | 156 | PHP_MOSQUITTO_ERROR_HANDLING(); 157 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!b", &id, &id_len, &clean_session) == FAILURE) { 158 | PHP_MOSQUITTO_RESTORE_ERRORS(); 159 | return; 160 | } 161 | PHP_MOSQUITTO_RESTORE_ERRORS(); 162 | 163 | object->client = mosquitto_new(id, clean_session, object); 164 | if (!object->client) { 165 | char *message = php_mosquitto_strerror_wrapper(errno); 166 | zend_throw_exception(mosquitto_ce_exception, message, 1 TSRMLS_CC); 167 | if (message) { 168 | efree(message); 169 | } 170 | } 171 | } 172 | /* }}} */ 173 | 174 | /* {{{ Mosquitto\Client::setTlsCertificates() */ 175 | PHP_METHOD(Mosquitto_Client, setTlsCertificates) 176 | { 177 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 178 | char *ca_path = NULL, *cert_path = NULL, *key_path = NULL, *key_pw = NULL; 179 | mosquitto_strlen_type ca_path_len = 0, cert_path_len = 0, key_path_len = 0, key_pw_len; 180 | int retval = 0; 181 | zval stat; 182 | zend_bool is_dir = 0; 183 | int (*pw_callback)(char *, int, int, void *) = NULL; 184 | 185 | PHP_MOSQUITTO_ERROR_HANDLING(); 186 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!|s!s!s!", 187 | &ca_path, &ca_path_len, 188 | &cert_path, &cert_path_len, 189 | &key_path, &key_path_len, 190 | &key_pw, &key_pw_len) == FAILURE) { 191 | PHP_MOSQUITTO_RESTORE_ERRORS(); 192 | return; 193 | } 194 | 195 | if ((php_check_open_basedir(ca_path TSRMLS_CC) < 0) || 196 | (php_check_open_basedir(cert_path TSRMLS_CC) < 0) || 197 | (php_check_open_basedir(key_path TSRMLS_CC) < 0)) 198 | { 199 | PHP_MOSQUITTO_RESTORE_ERRORS(); 200 | return; 201 | } 202 | 203 | PHP_MOSQUITTO_RESTORE_ERRORS(); 204 | 205 | php_stat(ca_path, ca_path_len, FS_IS_DIR, &stat TSRMLS_CC); 206 | is_dir = Z_BVAL(stat); 207 | 208 | if (key_pw != NULL) { 209 | pw_callback = php_mosquitto_pw_callback; 210 | MQTTG(client_key) = estrdup(key_pw); 211 | MQTTG(client_key_len) = key_pw_len; 212 | } 213 | 214 | if (is_dir) { 215 | retval = mosquitto_tls_set(object->client, NULL, ca_path, cert_path, key_path, pw_callback); 216 | } else { 217 | retval = mosquitto_tls_set(object->client, ca_path, NULL, cert_path, key_path, pw_callback); 218 | } 219 | 220 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 221 | RETURN_LONG(retval); 222 | } 223 | /* }}} */ 224 | 225 | /* {{{ Mosquitto\Client::setTlsInsecure() */ 226 | PHP_METHOD(Mosquitto_Client, setTlsInsecure) 227 | { 228 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 229 | zend_bool value = 0; 230 | int retval = 0; 231 | 232 | PHP_MOSQUITTO_ERROR_HANDLING(); 233 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &value) == FAILURE) { 234 | PHP_MOSQUITTO_RESTORE_ERRORS(); 235 | return; 236 | } 237 | PHP_MOSQUITTO_RESTORE_ERRORS(); 238 | 239 | retval = mosquitto_tls_insecure_set(object->client, value); 240 | 241 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 242 | } 243 | /* }}} */ 244 | 245 | /* {{{ Mosquitto\Client::setTlsOptions() */ 246 | PHP_METHOD(Mosquitto_Client, setTlsOptions) 247 | { 248 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 249 | char *tls_version = NULL, *ciphers = NULL; 250 | mosquitto_strlen_type tls_version_len = 0, ciphers_len = 0; 251 | int retval = 0, verify_peer = 0; 252 | 253 | PHP_MOSQUITTO_ERROR_HANDLING(); 254 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s!s!", 255 | &verify_peer, 256 | &tls_version, &tls_version_len, 257 | &ciphers, &ciphers_len 258 | ) == FAILURE) { 259 | PHP_MOSQUITTO_RESTORE_ERRORS(); 260 | return; 261 | } 262 | PHP_MOSQUITTO_RESTORE_ERRORS(); 263 | 264 | retval = mosquitto_tls_opts_set(object->client, verify_peer, tls_version, ciphers); 265 | 266 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 267 | RETURN_LONG(retval); 268 | } 269 | /* }}} */ 270 | 271 | /* {{{ Mosquitto\Client::setTlsPSK() */ 272 | PHP_METHOD(Mosquitto_Client, setTlsPSK) 273 | { 274 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 275 | char *psk = NULL, *identity = NULL, *ciphers = NULL; 276 | mosquitto_strlen_type psk_len = 0, identity_len = 0, ciphers_len = 0; 277 | int retval = 0; 278 | 279 | PHP_MOSQUITTO_ERROR_HANDLING(); 280 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!s!|s!", 281 | &psk, &psk_len, &identity, &identity_len, &ciphers, &ciphers_len 282 | ) == FAILURE) { 283 | PHP_MOSQUITTO_RESTORE_ERRORS(); 284 | return; 285 | } 286 | PHP_MOSQUITTO_RESTORE_ERRORS(); 287 | 288 | retval = mosquitto_tls_psk_set(object->client, psk, identity, ciphers); 289 | 290 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 291 | RETURN_LONG(retval); 292 | } 293 | /* }}} */ 294 | 295 | /* {{{ Mosquitto\Client::setCredentials() */ 296 | PHP_METHOD(Mosquitto_Client, setCredentials) 297 | { 298 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 299 | char *username = NULL, *password = NULL; 300 | mosquitto_strlen_type username_len, password_len; 301 | int retval; 302 | 303 | PHP_MOSQUITTO_ERROR_HANDLING(); 304 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &username, &username_len, &password, &password_len) == FAILURE) { 305 | PHP_MOSQUITTO_RESTORE_ERRORS(); 306 | return; 307 | } 308 | PHP_MOSQUITTO_RESTORE_ERRORS(); 309 | 310 | retval = mosquitto_username_pw_set(object->client, username, password); 311 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 312 | } 313 | /* }}} */ 314 | 315 | /* {{{ Mosquitto\Client::setWill() */ 316 | PHP_METHOD(Mosquitto_Client, setWill) 317 | { 318 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 319 | mosquitto_strlen_type topic_len, payload_len; 320 | int retval; 321 | zend_long qos; 322 | zend_bool retain; 323 | char *topic, *payload; 324 | 325 | PHP_MOSQUITTO_ERROR_HANDLING(); 326 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sslb", 327 | &topic, &topic_len, &payload, &payload_len, &qos, &retain) == FAILURE) { 328 | PHP_MOSQUITTO_RESTORE_ERRORS(); 329 | return; 330 | } 331 | PHP_MOSQUITTO_RESTORE_ERRORS(); 332 | 333 | retval = mosquitto_will_set(object->client, topic, payload_len, (void *) payload, qos, retain); 334 | 335 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 336 | } 337 | /* }}} */ 338 | 339 | /* {{{ Mosquitto\Client::clearWill() */ 340 | PHP_METHOD(Mosquitto_Client, clearWill) 341 | { 342 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 343 | int retval; 344 | 345 | PHP_MOSQUITTO_ERROR_HANDLING(); 346 | if (zend_parse_parameters_none() == FAILURE) { 347 | PHP_MOSQUITTO_RESTORE_ERRORS(); 348 | return; 349 | } 350 | PHP_MOSQUITTO_RESTORE_ERRORS(); 351 | 352 | retval = mosquitto_will_clear(object->client); 353 | 354 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 355 | } 356 | /* }}} */ 357 | 358 | /* {{{ Mosquitto\Client::setReconnectDelay() */ 359 | PHP_METHOD(Mosquitto_Client, setReconnectDelay) 360 | { 361 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 362 | int retval; 363 | zend_long reconnect_delay = 0, reconnect_delay_max = 0; 364 | zend_bool exponential_backoff = 0; 365 | 366 | PHP_MOSQUITTO_ERROR_HANDLING(); 367 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|lb", 368 | &reconnect_delay, &reconnect_delay_max, &exponential_backoff) == FAILURE) { 369 | PHP_MOSQUITTO_RESTORE_ERRORS(); 370 | return; 371 | } 372 | PHP_MOSQUITTO_RESTORE_ERRORS(); 373 | 374 | retval = mosquitto_reconnect_delay_set(object->client, reconnect_delay, reconnect_delay_max, exponential_backoff); 375 | 376 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 377 | } 378 | /* }}} */ 379 | 380 | /* {{{ Mosquitto\Client::connect() */ 381 | PHP_METHOD(Mosquitto_Client, connect) 382 | { 383 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 384 | char *host = NULL, *interface = NULL; 385 | mosquitto_strlen_type host_len, interface_len; 386 | int retval; 387 | zend_long port = 1883; 388 | zend_long keepalive = 60; 389 | 390 | PHP_MOSQUITTO_ERROR_HANDLING(); 391 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lls!", 392 | &host, &host_len, &port, &keepalive, 393 | &interface, &interface_len) == FAILURE) { 394 | 395 | PHP_MOSQUITTO_RESTORE_ERRORS(); 396 | return; 397 | } 398 | PHP_MOSQUITTO_RESTORE_ERRORS(); 399 | 400 | if (interface == NULL) { 401 | retval = mosquitto_connect(object->client, host, port, keepalive); 402 | } else { 403 | retval = mosquitto_connect_bind(object->client, host, port, keepalive, interface); 404 | } 405 | 406 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 407 | RETURN_LONG(retval); 408 | } 409 | /* }}} */ 410 | 411 | /* {{{ Mosquitto\Client::disconnect() */ 412 | PHP_METHOD(Mosquitto_Client, disconnect) 413 | { 414 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 415 | int retval; 416 | 417 | PHP_MOSQUITTO_ERROR_HANDLING(); 418 | if (zend_parse_parameters_none() == FAILURE) { 419 | PHP_MOSQUITTO_RESTORE_ERRORS(); 420 | return; 421 | } 422 | PHP_MOSQUITTO_RESTORE_ERRORS(); 423 | 424 | retval = mosquitto_disconnect(object->client); 425 | php_mosquitto_exit_loop(object); 426 | 427 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 428 | } 429 | /* }}} */ 430 | 431 | /* {{{ Mosquitto\Client::onConnect() */ 432 | PHP_METHOD(Mosquitto_Client, onConnect) 433 | { 434 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 435 | zend_fcall_info connect_callback = empty_fcall_info; 436 | zend_fcall_info_cache connect_callback_cache = empty_fcall_info_cache; 437 | 438 | PHP_MOSQUITTO_ERROR_HANDLING(); 439 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 440 | &connect_callback, &connect_callback_cache) == FAILURE) { 441 | 442 | PHP_MOSQUITTO_RESTORE_ERRORS(); 443 | return; 444 | } 445 | PHP_MOSQUITTO_RESTORE_ERRORS(); 446 | 447 | if (!ZEND_FCI_INITIALIZED(connect_callback)) { 448 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 449 | } 450 | 451 | PHP_MOSQUITTO_FREE_CALLBACK(object, connect); 452 | object->connect_callback = connect_callback; 453 | object->connect_callback_cache = connect_callback_cache; 454 | mosquitto_callback_addref(&(object->connect_callback)); 455 | 456 | mosquitto_connect_callback_set(object->client, php_mosquitto_connect_callback); 457 | } 458 | /* }}} */ 459 | 460 | /* {{{ Mosquitto\Client::onDisconnect() */ 461 | PHP_METHOD(Mosquitto_Client, onDisconnect) 462 | { 463 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 464 | zend_fcall_info disconnect_callback = empty_fcall_info; 465 | zend_fcall_info_cache disconnect_callback_cache = empty_fcall_info_cache; 466 | 467 | PHP_MOSQUITTO_ERROR_HANDLING(); 468 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 469 | &disconnect_callback, &disconnect_callback_cache) == FAILURE) { 470 | 471 | PHP_MOSQUITTO_RESTORE_ERRORS(); 472 | return; 473 | } 474 | PHP_MOSQUITTO_RESTORE_ERRORS(); 475 | 476 | if (!ZEND_FCI_INITIALIZED(disconnect_callback)) { 477 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 478 | } 479 | 480 | PHP_MOSQUITTO_FREE_CALLBACK(object, disconnect); 481 | object->disconnect_callback = disconnect_callback; 482 | object->disconnect_callback_cache = disconnect_callback_cache; 483 | mosquitto_callback_addref(&(object->disconnect_callback)); 484 | 485 | mosquitto_disconnect_callback_set(object->client, php_mosquitto_disconnect_callback); 486 | } 487 | /* }}} */ 488 | 489 | /* {{{ Mosquitto\Client::onLog() */ 490 | PHP_METHOD(Mosquitto_Client, onLog) 491 | { 492 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 493 | zend_fcall_info log_callback = empty_fcall_info; 494 | zend_fcall_info_cache log_callback_cache = empty_fcall_info_cache; 495 | 496 | PHP_MOSQUITTO_ERROR_HANDLING(); 497 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 498 | &log_callback, &log_callback_cache) == FAILURE) { 499 | 500 | PHP_MOSQUITTO_RESTORE_ERRORS(); 501 | return; 502 | } 503 | PHP_MOSQUITTO_RESTORE_ERRORS(); 504 | 505 | if (!ZEND_FCI_INITIALIZED(log_callback)) { 506 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 507 | } 508 | 509 | PHP_MOSQUITTO_FREE_CALLBACK(object, log); 510 | object->log_callback = log_callback; 511 | object->log_callback_cache = log_callback_cache; 512 | mosquitto_callback_addref(&(object->log_callback)); 513 | 514 | mosquitto_log_callback_set(object->client, php_mosquitto_log_callback); 515 | } 516 | /* }}} */ 517 | 518 | /* {{{ Mosquitto\Client::onSubscribe() */ 519 | PHP_METHOD(Mosquitto_Client, onSubscribe) 520 | { 521 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 522 | zend_fcall_info subscribe_callback = empty_fcall_info; 523 | zend_fcall_info_cache subscribe_callback_cache = empty_fcall_info_cache; 524 | 525 | PHP_MOSQUITTO_ERROR_HANDLING(); 526 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 527 | &subscribe_callback, &subscribe_callback_cache) == FAILURE) { 528 | 529 | PHP_MOSQUITTO_RESTORE_ERRORS(); 530 | return; 531 | } 532 | PHP_MOSQUITTO_RESTORE_ERRORS(); 533 | 534 | if (!ZEND_FCI_INITIALIZED(subscribe_callback)) { 535 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 536 | } 537 | 538 | PHP_MOSQUITTO_FREE_CALLBACK(object, subscribe); 539 | object->subscribe_callback = subscribe_callback; 540 | object->subscribe_callback_cache = subscribe_callback_cache; 541 | mosquitto_callback_addref(&(object->subscribe_callback)); 542 | 543 | mosquitto_subscribe_callback_set(object->client, php_mosquitto_subscribe_callback); 544 | } 545 | /* }}} */ 546 | 547 | /* {{{ Mosquitto\Client::onUnsubscribe() */ 548 | PHP_METHOD(Mosquitto_Client, onUnsubscribe) 549 | { 550 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 551 | zend_fcall_info unsubscribe_callback = empty_fcall_info; 552 | zend_fcall_info_cache unsubscribe_callback_cache = empty_fcall_info_cache; 553 | 554 | PHP_MOSQUITTO_ERROR_HANDLING(); 555 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 556 | &unsubscribe_callback, &unsubscribe_callback_cache) == FAILURE) { 557 | 558 | PHP_MOSQUITTO_RESTORE_ERRORS(); 559 | return; 560 | } 561 | PHP_MOSQUITTO_RESTORE_ERRORS(); 562 | 563 | if (!ZEND_FCI_INITIALIZED(unsubscribe_callback)) { 564 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 565 | } 566 | 567 | PHP_MOSQUITTO_FREE_CALLBACK(object, unsubscribe); 568 | object->unsubscribe_callback = unsubscribe_callback; 569 | object->unsubscribe_callback_cache = unsubscribe_callback_cache; 570 | mosquitto_callback_addref(&(object->unsubscribe_callback)); 571 | 572 | mosquitto_unsubscribe_callback_set(object->client, php_mosquitto_unsubscribe_callback); 573 | } 574 | /* }}} */ 575 | 576 | /* {{{ Mosquitto\Client::onMessage() */ 577 | PHP_METHOD(Mosquitto_Client, onMessage) 578 | { 579 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 580 | zend_fcall_info message_callback = empty_fcall_info; 581 | zend_fcall_info_cache message_callback_cache = empty_fcall_info_cache; 582 | 583 | PHP_MOSQUITTO_ERROR_HANDLING(); 584 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 585 | &message_callback, &message_callback_cache) == FAILURE) { 586 | 587 | PHP_MOSQUITTO_RESTORE_ERRORS(); 588 | return; 589 | } 590 | PHP_MOSQUITTO_RESTORE_ERRORS(); 591 | 592 | if (!ZEND_FCI_INITIALIZED(message_callback)) { 593 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 594 | } 595 | 596 | PHP_MOSQUITTO_FREE_CALLBACK(object, message); 597 | object->message_callback = message_callback; 598 | object->message_callback_cache = message_callback_cache; 599 | mosquitto_callback_addref(&(object->message_callback)); 600 | 601 | mosquitto_message_callback_set(object->client, php_mosquitto_message_callback); 602 | } 603 | /* }}} */ 604 | 605 | /* {{{ Mosquitto\Client::onPublish() */ 606 | PHP_METHOD(Mosquitto_Client, onPublish) 607 | { 608 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 609 | zend_fcall_info publish_callback = empty_fcall_info; 610 | zend_fcall_info_cache publish_callback_cache = empty_fcall_info_cache; 611 | 612 | PHP_MOSQUITTO_ERROR_HANDLING(); 613 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", 614 | &publish_callback, &publish_callback_cache) == FAILURE) { 615 | 616 | PHP_MOSQUITTO_RESTORE_ERRORS(); 617 | return; 618 | } 619 | PHP_MOSQUITTO_RESTORE_ERRORS(); 620 | 621 | if (!ZEND_FCI_INITIALIZED(publish_callback)) { 622 | zend_throw_exception(mosquitto_ce_exception, "Need a valid callback", 0 TSRMLS_CC); 623 | } 624 | 625 | PHP_MOSQUITTO_FREE_CALLBACK(object, publish); 626 | object->publish_callback = publish_callback; 627 | object->publish_callback_cache = publish_callback_cache; 628 | mosquitto_callback_addref(&(object->publish_callback)); 629 | 630 | mosquitto_publish_callback_set(object->client, php_mosquitto_publish_callback); 631 | } 632 | /* }}} */ 633 | 634 | /* {{{ Mosquitto\Client::getSocket() */ 635 | PHP_METHOD(Mosquitto_Client, getSocket) 636 | { 637 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 638 | 639 | PHP_MOSQUITTO_ERROR_HANDLING(); 640 | if (zend_parse_parameters_none() == FAILURE) { 641 | PHP_MOSQUITTO_RESTORE_ERRORS(); 642 | return; 643 | } 644 | PHP_MOSQUITTO_RESTORE_ERRORS(); 645 | 646 | RETURN_LONG(mosquitto_socket(object->client)); 647 | } 648 | /* }}} */ 649 | 650 | /* {{{ Mosquitto\Client::setMaxInFlightMessages() */ 651 | PHP_METHOD(Mosquitto_Client, setMaxInFlightMessages) 652 | { 653 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 654 | int retval; 655 | zend_long max = 0; 656 | 657 | PHP_MOSQUITTO_ERROR_HANDLING(); 658 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &max) == FAILURE) { 659 | PHP_MOSQUITTO_RESTORE_ERRORS(); 660 | return; 661 | } 662 | PHP_MOSQUITTO_RESTORE_ERRORS(); 663 | 664 | retval = mosquitto_max_inflight_messages_set(object->client, max); 665 | 666 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 667 | } 668 | /* }}} */ 669 | 670 | /* {{{ Mosquitto\Client::setMessageRetry() */ 671 | PHP_METHOD(Mosquitto_Client, setMessageRetry) 672 | { 673 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 674 | zend_long retry = 0; 675 | 676 | PHP_MOSQUITTO_ERROR_HANDLING(); 677 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &retry) == FAILURE) { 678 | PHP_MOSQUITTO_RESTORE_ERRORS(); 679 | return; 680 | } 681 | PHP_MOSQUITTO_RESTORE_ERRORS(); 682 | 683 | mosquitto_message_retry_set(object->client, retry); 684 | } 685 | /* }}} */ 686 | 687 | /* {{{ Mosquitto\Client::publish() */ 688 | PHP_METHOD(Mosquitto_Client, publish) 689 | { 690 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 691 | int mid, retval; 692 | mosquitto_strlen_type topic_len, payload_len; 693 | zend_long qos = 0; 694 | zend_bool retain = 0; 695 | char *topic, *payload; 696 | 697 | PHP_MOSQUITTO_ERROR_HANDLING(); 698 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|lb", 699 | &topic, &topic_len, &payload, &payload_len, &qos, &retain) == FAILURE) { 700 | PHP_MOSQUITTO_RESTORE_ERRORS(); 701 | return; 702 | } 703 | PHP_MOSQUITTO_RESTORE_ERRORS(); 704 | 705 | retval = mosquitto_publish(object->client, &mid, topic, payload_len, (void *) payload, qos, retain); 706 | 707 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 708 | 709 | RETURN_LONG(mid); 710 | } 711 | /* }}} */ 712 | 713 | /* {{{ Mosquitto\Client::subscribe() */ 714 | PHP_METHOD(Mosquitto_Client, subscribe) 715 | { 716 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 717 | char *sub; 718 | mosquitto_strlen_type sub_len; 719 | int retval, mid; 720 | zend_long qos; 721 | 722 | PHP_MOSQUITTO_ERROR_HANDLING(); 723 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", 724 | &sub, &sub_len, &qos) == FAILURE) { 725 | PHP_MOSQUITTO_RESTORE_ERRORS(); 726 | return; 727 | } 728 | PHP_MOSQUITTO_RESTORE_ERRORS(); 729 | 730 | retval = mosquitto_subscribe(object->client, &mid, sub, qos); 731 | 732 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 733 | 734 | RETURN_LONG(mid); 735 | } 736 | /* }}} */ 737 | 738 | /* {{{ Mosquitto\Client::unsubscribe() */ 739 | PHP_METHOD(Mosquitto_Client, unsubscribe) 740 | { 741 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 742 | char *sub; 743 | mosquitto_strlen_type sub_len; 744 | int retval, mid; 745 | 746 | PHP_MOSQUITTO_ERROR_HANDLING(); 747 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", 748 | &sub, &sub_len) == FAILURE) { 749 | PHP_MOSQUITTO_RESTORE_ERRORS(); 750 | return; 751 | } 752 | PHP_MOSQUITTO_RESTORE_ERRORS(); 753 | 754 | retval = mosquitto_unsubscribe(object->client, &mid, sub); 755 | 756 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 757 | 758 | RETURN_LONG(mid); 759 | } 760 | /* }}} */ 761 | 762 | /* {{{ Mosquitto\Client::loop() */ 763 | PHP_METHOD(Mosquitto_Client, loop) 764 | { 765 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 766 | zend_long timeout = 1000, max_packets = 1; 767 | long retval = 0; 768 | 769 | PHP_MOSQUITTO_ERROR_HANDLING(); 770 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", 771 | &timeout, &max_packets) == FAILURE) { 772 | PHP_MOSQUITTO_RESTORE_ERRORS(); 773 | return; 774 | } 775 | PHP_MOSQUITTO_RESTORE_ERRORS(); 776 | 777 | retval = mosquitto_loop(object->client, timeout, max_packets); 778 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 779 | } 780 | /* }}} */ 781 | 782 | /* {{{ Mosquitto\Client::loopForever() */ 783 | PHP_METHOD(Mosquitto_Client, loopForever) 784 | { 785 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 786 | zend_long timeout = 1000, max_packets = 1; 787 | long retval = 0; 788 | 789 | PHP_MOSQUITTO_ERROR_HANDLING(); 790 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|ll", 791 | &timeout, &max_packets) == FAILURE) { 792 | PHP_MOSQUITTO_RESTORE_ERRORS(); 793 | return; 794 | } 795 | PHP_MOSQUITTO_RESTORE_ERRORS(); 796 | 797 | object->looping = 1; 798 | 799 | while (object->looping) { 800 | retval = mosquitto_loop(object->client, timeout, max_packets); 801 | php_mosquitto_handle_errno(retval, errno TSRMLS_CC); 802 | 803 | if (EG(exception)) { 804 | break; 805 | } 806 | } 807 | } 808 | /* }}} */ 809 | 810 | /* {{{ Mosquitto\Client::exitLoop() */ 811 | PHP_METHOD(Mosquitto_Client, exitLoop) 812 | { 813 | mosquitto_client_object *object = mosquitto_client_object_from_zend_object(Z_OBJ_P(getThis())); 814 | 815 | PHP_MOSQUITTO_ERROR_HANDLING(); 816 | if (zend_parse_parameters_none() == FAILURE) { 817 | PHP_MOSQUITTO_RESTORE_ERRORS(); 818 | return; 819 | } 820 | PHP_MOSQUITTO_RESTORE_ERRORS(); 821 | 822 | php_mosquitto_exit_loop(object); 823 | } 824 | /* }}} */ 825 | 826 | /* Internal functions */ 827 | 828 | #if defined(PHP_WIN32) 829 | static int strerror_r(int errnum, char *buf, size_t buf_len) 830 | { 831 | return strerror_s(buf, buf_len, errnum); 832 | } 833 | #endif 834 | 835 | PHP_MOSQUITTO_API char *php_mosquitto_strerror_wrapper(int err) 836 | { 837 | char *buf = ecalloc(256, sizeof(char)); 838 | strerror_r(err, buf, 256); 839 | if (!buf[0]) { 840 | efree(buf); 841 | return NULL; 842 | } 843 | return buf; 844 | } 845 | 846 | PHP_MOSQUITTO_API void php_mosquitto_exit_loop(mosquitto_client_object *object) 847 | { 848 | object->looping = 0; 849 | } 850 | 851 | static void mosquitto_client_object_destroy(zend_object *object ZO_HANDLE_DC TSRMLS_DC) 852 | { 853 | mosquitto_client_object *client = mosquitto_client_object_from_zend_object(object); 854 | 855 | /* Disconnect cleanly, but disregard an error if it wasn't connected */ 856 | /* We must loop here so that the disconnect packet is sent and acknowledged */ 857 | mosquitto_disconnect(client->client); 858 | mosquitto_loop(client->client, 100, 1); 859 | mosquitto_destroy(client->client); 860 | client->client = NULL; 861 | 862 | PHP_MOSQUITTO_FREE_CALLBACK(client, connect); 863 | PHP_MOSQUITTO_FREE_CALLBACK(client, subscribe); 864 | PHP_MOSQUITTO_FREE_CALLBACK(client, unsubscribe); 865 | PHP_MOSQUITTO_FREE_CALLBACK(client, publish); 866 | PHP_MOSQUITTO_FREE_CALLBACK(client, message); 867 | PHP_MOSQUITTO_FREE_CALLBACK(client, disconnect); 868 | PHP_MOSQUITTO_FREE_CALLBACK(client, log); 869 | } 870 | 871 | static void mosquitto_client_object_free(zend_object *object TSRMLS_DC) { 872 | mosquitto_client_object *client = mosquitto_client_object_from_zend_object(object); 873 | 874 | #ifdef ZEND_ENGINE_3 875 | zend_object_std_dtor(object); 876 | #else 877 | if (object->properties) { 878 | zend_hash_destroy(object->properties); 879 | FREE_HASHTABLE(object->properties); 880 | } 881 | efree(object); 882 | #endif 883 | } 884 | 885 | #ifdef ZEND_ENGINE_3 886 | static zend_object *mosquitto_client_object_new(zend_class_entry *ce) { 887 | mosquitto_client_object *client = ecalloc(1, sizeof(mosquitto_client_object) + zend_object_properties_size(ce)); 888 | zend_object *ret = mosquitto_client_object_to_zend_object(client); 889 | 890 | #ifdef MOSQUITTO_NEED_TSRMLS 891 | client->TSRMLS_C = TSRMLS_C; 892 | #endif 893 | 894 | zend_object_std_init(ret, ce); 895 | ret->handlers = &mosquitto_std_object_handlers; 896 | 897 | return ret; 898 | } 899 | #else 900 | static zend_object_value mosquitto_client_object_new(zend_class_entry *ce TSRMLS_DC) { 901 | 902 | zend_object_value retval; 903 | mosquitto_client_object *client; 904 | #if PHP_VERSION_ID < 50399 905 | zval *temp; 906 | #endif 907 | 908 | client = ecalloc(1, sizeof(mosquitto_client_object)); 909 | client->std.ce = ce; 910 | client->client = NULL; 911 | 912 | #ifdef MOSQUITTO_NEED_TSRMLS 913 | client->TSRMLS_C = TSRMLS_C; 914 | #endif 915 | 916 | ALLOC_HASHTABLE(client->std.properties); 917 | zend_hash_init(client->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); 918 | #if PHP_VERSION_ID < 50399 919 | zend_hash_copy(client->std.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref,(void *) &temp, sizeof(zval *)); 920 | #else 921 | object_properties_init(&client->std, ce); 922 | #endif 923 | retval.handle = zend_objects_store_put(client, 924 | (zend_objects_store_dtor_t)mosquitto_client_object_destroy, 925 | (zend_objects_free_object_storage_t)mosquitto_client_object_free, NULL TSRMLS_CC); 926 | retval.handlers = &mosquitto_std_object_handlers; 927 | return retval; 928 | } 929 | #endif 930 | 931 | void php_mosquitto_handle_errno(int retval, int err TSRMLS_DC) { 932 | if (retval == MOSQ_ERR_ERRNO) { 933 | char *message = php_mosquitto_strerror_wrapper(errno); 934 | if (message) { 935 | zend_throw_exception(mosquitto_ce_exception, message, 0 TSRMLS_CC); 936 | efree(message); 937 | } 938 | } else if (retval != MOSQ_ERR_SUCCESS) { 939 | const char *message = mosquitto_strerror(retval); 940 | if (message && *message) { 941 | zend_throw_exception(mosquitto_ce_exception, message, 0 TSRMLS_CC); 942 | } 943 | } 944 | } 945 | 946 | PHP_MOSQUITTO_API void php_mosquitto_connect_callback(struct mosquitto *mosq, void *obj, int rc) 947 | { 948 | mosquitto_client_object *object = (mosquitto_client_object*)obj; 949 | #ifdef ZEND_ENGINE_3 950 | zval params[2], retval; 951 | #else 952 | zval *retval_ptr = NULL, *rc_zval = NULL, *message_zval = NULL; 953 | zval **params[2]; 954 | #endif 955 | const char *message; 956 | #ifdef MOSQUITTO_NEED_TSRMLS 957 | TSRMLS_D = object->TSRMLS_C; 958 | #endif 959 | 960 | if (!ZEND_FCI_INITIALIZED(object->connect_callback)) { 961 | return; 962 | } 963 | 964 | message = mosquitto_connack_string(rc); 965 | #ifdef ZEND_ENGINE_3 966 | ZVAL_LONG(¶ms[0], rc); 967 | ZVAL_STRING(¶ms[1], message); 968 | 969 | ZVAL_UNDEF(&retval); 970 | object->connect_callback.retval = &retval; 971 | #else 972 | MAKE_STD_ZVAL(rc_zval); 973 | ZVAL_LONG(rc_zval, rc); 974 | params[0] = &rc_zval; 975 | 976 | MAKE_STD_ZVAL(message_zval); 977 | ZVAL_STRING(message_zval, message, 1); 978 | params[1] = &message_zval; 979 | 980 | object->connect_callback.retval_ptr_ptr = &retval_ptr; 981 | #endif 982 | 983 | object->connect_callback.params = params; 984 | object->connect_callback.param_count = 2; 985 | 986 | if (zend_call_function(&object->connect_callback, &object->connect_callback_cache TSRMLS_CC) == FAILURE) { 987 | if (!EG(exception)) { 988 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke connect callback %s()", mosquitto_finfo_name(&object->connect_callback)); 989 | } 990 | } 991 | 992 | #ifdef ZEND_ENGINE_3 993 | zval_ptr_dtor(¶ms[0]); 994 | zval_ptr_dtor(¶ms[1]); 995 | zval_ptr_dtor(&retval); 996 | #else 997 | zval_ptr_dtor(&rc_zval); 998 | zval_ptr_dtor(&message_zval); 999 | 1000 | if (retval_ptr != NULL) { 1001 | zval_ptr_dtor(&retval_ptr); 1002 | } 1003 | #endif 1004 | } 1005 | 1006 | PHP_MOSQUITTO_API void php_mosquitto_disconnect_callback(struct mosquitto *mosq, void *obj, int rc) 1007 | { 1008 | mosquitto_client_object *object = (mosquitto_client_object*)obj; 1009 | #ifdef ZEND_ENGINE_3 1010 | zval params[1], retval; 1011 | #else 1012 | zval *retval_ptr = NULL, *rc_zval = NULL; 1013 | zval **params[1]; 1014 | #endif 1015 | #ifdef MOSQUITTO_NEED_TSRMLS 1016 | TSRMLS_D = object->TSRMLS_C; 1017 | #endif 1018 | 1019 | if (!ZEND_FCI_INITIALIZED(object->disconnect_callback)) { 1020 | return; 1021 | } 1022 | 1023 | #ifdef ZEND_ENGINE_3 1024 | ZVAL_LONG(¶ms[0], rc); 1025 | ZVAL_UNDEF(&retval); 1026 | 1027 | object->disconnect_callback.retval = &retval; 1028 | #else 1029 | MAKE_STD_ZVAL(rc_zval); 1030 | ZVAL_LONG(rc_zval, rc); 1031 | params[0] = &rc_zval; 1032 | 1033 | object->disconnect_callback.retval_ptr_ptr = &retval_ptr; 1034 | #endif 1035 | 1036 | object->disconnect_callback.params = params; 1037 | object->disconnect_callback.param_count = 1; 1038 | 1039 | if (zend_call_function(&object->disconnect_callback, &object->disconnect_callback_cache TSRMLS_CC) == FAILURE) { 1040 | if (!EG(exception)) { 1041 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke disconnect callback %s()", mosquitto_finfo_name(&object->disconnect_callback)); 1042 | } 1043 | } 1044 | 1045 | #ifdef ZEND_ENGINE_3 1046 | zval_ptr_dtor(¶ms[0]); 1047 | zval_ptr_dtor(&retval); 1048 | #else 1049 | zval_ptr_dtor(&rc_zval); 1050 | 1051 | if (retval_ptr != NULL) { 1052 | zval_ptr_dtor(&retval_ptr); 1053 | } 1054 | #endif 1055 | } 1056 | 1057 | PHP_MOSQUITTO_API void php_mosquitto_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str) 1058 | { 1059 | mosquitto_client_object *object = (mosquitto_client_object*)obj; 1060 | #ifdef ZEND_ENGINE_3 1061 | zval params[2], retval; 1062 | #else 1063 | zval *retval_ptr = NULL, *level_zval = NULL, *str_zval = NULL; 1064 | zval **params[2]; 1065 | #endif 1066 | #ifdef MOSQUITTO_NEED_TSRMLS 1067 | TSRMLS_D = object->TSRMLS_C; 1068 | #endif 1069 | 1070 | if (!ZEND_FCI_INITIALIZED(object->log_callback)) { 1071 | return; 1072 | } 1073 | 1074 | #ifdef ZEND_ENGINE_3 1075 | ZVAL_LONG(¶ms[0], level); 1076 | ZVAL_STRING(¶ms[1], str); 1077 | ZVAL_UNDEF(&retval); 1078 | 1079 | object->log_callback.retval = &retval; 1080 | #else 1081 | MAKE_STD_ZVAL(level_zval); 1082 | ZVAL_LONG(level_zval, level); 1083 | MAKE_STD_ZVAL(str_zval); 1084 | ZVAL_STRING(str_zval, str, 1); 1085 | 1086 | params[0] = &level_zval; 1087 | params[1] = &str_zval; 1088 | 1089 | object->log_callback.retval_ptr_ptr = &retval_ptr; 1090 | #endif 1091 | 1092 | object->log_callback.params = params; 1093 | object->log_callback.param_count = 2; 1094 | 1095 | if (zend_call_function(&object->log_callback, &object->log_callback_cache TSRMLS_CC) == FAILURE) { 1096 | if (!EG(exception)) { 1097 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke log callback %s()", mosquitto_finfo_name(&object->log_callback)); 1098 | } 1099 | } 1100 | 1101 | #ifdef ZEND_ENGINE_3 1102 | zval_ptr_dtor(¶ms[0]); 1103 | zval_ptr_dtor(¶ms[1]); 1104 | zval_ptr_dtor(&retval); 1105 | #else 1106 | zval_ptr_dtor(params[0]); 1107 | zval_ptr_dtor(params[1]); 1108 | 1109 | if (retval_ptr != NULL) { 1110 | zval_ptr_dtor(&retval_ptr); 1111 | } 1112 | #endif 1113 | } 1114 | 1115 | PHP_MOSQUITTO_API void php_mosquitto_message_callback(struct mosquitto *mosq, void *client_obj, const struct mosquitto_message *message) 1116 | { 1117 | mosquitto_client_object *object = (mosquitto_client_object*)client_obj; 1118 | mosquitto_message_object *message_object; 1119 | #ifdef ZEND_ENGINE_3 1120 | zval params[1], retval, *message_zval; 1121 | #else 1122 | zval *retval_ptr = NULL, *message_zval = NULL; 1123 | zval **params[1]; 1124 | #endif 1125 | #ifdef MOSQUITTO_NEED_TSRMLS 1126 | TSRMLS_D = object->TSRMLS_C; 1127 | #endif 1128 | 1129 | if (!ZEND_FCI_INITIALIZED(object->message_callback)) { 1130 | return; 1131 | } 1132 | 1133 | #ifdef ZEND_ENGINE_3 1134 | message_zval = ¶ms[0]; 1135 | ZVAL_UNDEF(&retval); 1136 | object->message_callback.retval = &retval; 1137 | #else 1138 | MAKE_STD_ZVAL(message_zval); 1139 | params[0] = &message_zval; 1140 | object->message_callback.retval_ptr_ptr = &retval_ptr; 1141 | #endif 1142 | 1143 | object_init_ex(message_zval, mosquitto_ce_message); 1144 | message_object = mosquitto_message_object_from_zend_object(Z_OBJ_P(message_zval)); 1145 | message_object->message.mid = message->mid; 1146 | message_object->message.qos = message->qos; 1147 | message_object->message.retain = message->retain; 1148 | message_object->message.topic = estrdup(message->topic); 1149 | message_object->owned_topic = 1; 1150 | message_object->message.payloadlen = message->payloadlen; 1151 | 1152 | message_object->message.payload = ecalloc(message->payloadlen, sizeof(char)); 1153 | memcpy(message_object->message.payload, message->payload, message->payloadlen); 1154 | message_object->owned_payload = 1; 1155 | 1156 | object->message_callback.params = params; 1157 | object->message_callback.param_count = 1; 1158 | 1159 | if (zend_call_function(&object->message_callback, &object->message_callback_cache TSRMLS_CC) == FAILURE) { 1160 | if (!EG(exception)) { 1161 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke message callback %s()", mosquitto_finfo_name(&object->message_callback)); 1162 | } 1163 | } 1164 | 1165 | #ifdef ZEND_ENGINE_3 1166 | zval_ptr_dtor(¶ms[0]); 1167 | zval_ptr_dtor(&retval); 1168 | #else 1169 | zval_ptr_dtor(&message_zval); 1170 | 1171 | if (retval_ptr != NULL) { 1172 | zval_ptr_dtor(&retval_ptr); 1173 | } 1174 | #endif 1175 | } 1176 | 1177 | 1178 | PHP_MOSQUITTO_API void php_mosquitto_publish_callback(struct mosquitto *mosq, void *client_obj, int mid) 1179 | { 1180 | mosquitto_client_object *object = (mosquitto_client_object*)client_obj; 1181 | #ifdef ZEND_ENGINE_3 1182 | zval params[1], retval; 1183 | #else 1184 | zval *retval_ptr = NULL, *mid_zval; 1185 | zval **params[1]; 1186 | #endif 1187 | #ifdef MOSQUITTO_NEED_TSRMLS 1188 | TSRMLS_D = object->TSRMLS_C; 1189 | #endif 1190 | 1191 | if (!ZEND_FCI_INITIALIZED(object->publish_callback)) { 1192 | return; 1193 | } 1194 | 1195 | #ifdef ZEND_ENGINE_3 1196 | ZVAL_LONG(¶ms[0], mid); 1197 | ZVAL_UNDEF(&retval); 1198 | object->publish_callback.retval = &retval; 1199 | #else 1200 | MAKE_STD_ZVAL(mid_zval); 1201 | ZVAL_LONG(mid_zval, mid); 1202 | params[0] = &mid_zval; 1203 | object->publish_callback.retval_ptr_ptr = &retval_ptr; 1204 | #endif 1205 | 1206 | object->publish_callback.params = params; 1207 | object->publish_callback.param_count = 1; 1208 | 1209 | if (zend_call_function(&object->publish_callback, &object->publish_callback_cache TSRMLS_CC) == FAILURE) { 1210 | if (!EG(exception)) { 1211 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke publish callback %s()", mosquitto_finfo_name(&object->publish_callback)); 1212 | } 1213 | } 1214 | 1215 | #ifdef ZEND_ENGINE_3 1216 | zval_ptr_dtor(¶ms[0]); 1217 | zval_ptr_dtor(&retval); 1218 | #else 1219 | zval_ptr_dtor(params[0]); 1220 | 1221 | if (retval_ptr != NULL) { 1222 | zval_ptr_dtor(&retval_ptr); 1223 | } 1224 | #endif 1225 | } 1226 | 1227 | PHP_MOSQUITTO_API void php_mosquitto_subscribe_callback(struct mosquitto *mosq, void *client_obj, int mid, int qos_count, const int *granted_qos) 1228 | { 1229 | mosquitto_client_object *object = (mosquitto_client_object*)client_obj; 1230 | #ifdef ZEND_ENGINE_3 1231 | zval params[3], retval; 1232 | #else 1233 | zval *retval_ptr = NULL, *mid_zval, *qos_count_zval, *granted_qos_zval; 1234 | zval **params[3]; 1235 | #endif 1236 | #ifdef MOSQUITTO_NEED_TSRMLS 1237 | TSRMLS_D = object->TSRMLS_C; 1238 | #endif 1239 | 1240 | if (!ZEND_FCI_INITIALIZED(object->subscribe_callback)) { 1241 | return; 1242 | } 1243 | 1244 | /* Since we can only subscribe to one topic per message, it seems reasonable to 1245 | * take just the first entry from granted_qos as the granted QoS value */ 1246 | #ifdef ZEND_ENGINE_3 1247 | ZVAL_LONG(¶ms[0], mid); 1248 | ZVAL_LONG(¶ms[1], qos_count); 1249 | ZVAL_LONG(¶ms[2], *granted_qos); 1250 | ZVAL_UNDEF(&retval); 1251 | 1252 | object->subscribe_callback.retval = &retval; 1253 | #else 1254 | MAKE_STD_ZVAL(mid_zval); 1255 | MAKE_STD_ZVAL(qos_count_zval); 1256 | MAKE_STD_ZVAL(granted_qos_zval); 1257 | ZVAL_LONG(mid_zval, mid); 1258 | ZVAL_LONG(qos_count_zval, qos_count); 1259 | ZVAL_LONG(granted_qos_zval, *granted_qos); 1260 | params[0] = &mid_zval; 1261 | params[1] = &qos_count_zval; 1262 | params[2] = &granted_qos_zval; 1263 | 1264 | object->subscribe_callback.retval_ptr_ptr = &retval_ptr; 1265 | #endif 1266 | 1267 | object->subscribe_callback.params = params; 1268 | object->subscribe_callback.param_count = 3; 1269 | 1270 | if (zend_call_function(&object->subscribe_callback, &object->subscribe_callback_cache TSRMLS_CC) == FAILURE) { 1271 | if (!EG(exception)) { 1272 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke subscribe callback %s()", mosquitto_finfo_name(&object->subscribe_callback)); 1273 | } 1274 | } 1275 | 1276 | #ifdef ZEND_ENGINE_3 1277 | zval_ptr_dtor(¶ms[0]); 1278 | zval_ptr_dtor(¶ms[1]); 1279 | zval_ptr_dtor(¶ms[2]); 1280 | zval_ptr_dtor(&retval); 1281 | #else 1282 | zval_ptr_dtor(params[0]); 1283 | zval_ptr_dtor(params[1]); 1284 | zval_ptr_dtor(params[2]); 1285 | 1286 | if (retval_ptr != NULL) { 1287 | zval_ptr_dtor(&retval_ptr); 1288 | } 1289 | #endif 1290 | } 1291 | 1292 | PHP_MOSQUITTO_API void php_mosquitto_unsubscribe_callback(struct mosquitto *mosq, void *client_obj, int mid) 1293 | { 1294 | mosquitto_client_object *object = (mosquitto_client_object*)client_obj; 1295 | #ifdef ZEND_ENGINE_3 1296 | zval params[1], retval; 1297 | #else 1298 | zval *retval_ptr = NULL, *mid_zval; 1299 | zval **params[1]; 1300 | #endif 1301 | #ifdef MOSQUITTO_NEED_TSRMLS 1302 | TSRMLS_D = object->TSRMLS_C; 1303 | #endif 1304 | 1305 | if (!ZEND_FCI_INITIALIZED(object->unsubscribe_callback)) { 1306 | return; 1307 | } 1308 | 1309 | #ifdef ZEND_ENGINE_3 1310 | ZVAL_LONG(¶ms[0], mid); 1311 | ZVAL_UNDEF(&retval); 1312 | 1313 | object->unsubscribe_callback.retval = &retval; 1314 | #else 1315 | MAKE_STD_ZVAL(mid_zval); 1316 | ZVAL_LONG(mid_zval, mid); 1317 | params[0] = &mid_zval; 1318 | 1319 | object->unsubscribe_callback.retval_ptr_ptr = &retval_ptr; 1320 | #endif 1321 | 1322 | object->unsubscribe_callback.params = params; 1323 | object->unsubscribe_callback.param_count = 1; 1324 | 1325 | if (zend_call_function(&object->unsubscribe_callback, &object->unsubscribe_callback_cache TSRMLS_CC) == FAILURE) { 1326 | if (!EG(exception)) { 1327 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to invoke unsubscribe callback %s()", mosquitto_finfo_name(&object->unsubscribe_callback)); 1328 | } 1329 | } 1330 | 1331 | #ifdef ZEND_ENGINE_3 1332 | zval_ptr_dtor(¶ms[0]); 1333 | zval_ptr_dtor(&retval); 1334 | #else 1335 | zval_ptr_dtor(params[0]); 1336 | 1337 | if (retval_ptr != NULL) { 1338 | zval_ptr_dtor(&retval_ptr); 1339 | } 1340 | #endif 1341 | } 1342 | 1343 | static int php_mosquitto_pw_callback(char *buf, int size, int rwflag, void *userdata) { 1344 | TSRMLS_FETCH(); 1345 | int key_len; 1346 | 1347 | strncpy(buf, MQTTG(client_key), size); 1348 | key_len = MQTTG(client_key_len); 1349 | efree(MQTTG(client_key)); 1350 | MQTTG(client_key_len) = 0; 1351 | 1352 | return key_len; 1353 | } 1354 | 1355 | /* {{{ mosquitto_client_methods */ 1356 | const zend_function_entry mosquitto_client_methods[] = { 1357 | PHP_ME(Mosquitto_Client, __construct, Mosquitto_Client___construct_args, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 1358 | PHP_ME(Mosquitto_Client, onConnect, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1359 | PHP_ME(Mosquitto_Client, onDisconnect, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1360 | PHP_ME(Mosquitto_Client, onLog, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1361 | PHP_ME(Mosquitto_Client, onSubscribe, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1362 | PHP_ME(Mosquitto_Client, onUnsubscribe, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1363 | PHP_ME(Mosquitto_Client, onMessage, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1364 | PHP_ME(Mosquitto_Client, onPublish, Mosquitto_Client_callback_args, ZEND_ACC_PUBLIC) 1365 | PHP_ME(Mosquitto_Client, getSocket, NULL, ZEND_ACC_PUBLIC) 1366 | PHP_ME(Mosquitto_Client, setTlsCertificates, NULL, ZEND_ACC_PUBLIC) 1367 | PHP_ME(Mosquitto_Client, setTlsInsecure, NULL, ZEND_ACC_PUBLIC) 1368 | PHP_ME(Mosquitto_Client, setTlsOptions, NULL, ZEND_ACC_PUBLIC) 1369 | PHP_ME(Mosquitto_Client, setTlsPSK, NULL, ZEND_ACC_PUBLIC) 1370 | PHP_ME(Mosquitto_Client, setCredentials, Mosquitto_Client_setCredentials_args, ZEND_ACC_PUBLIC) 1371 | PHP_ME(Mosquitto_Client, setWill, Mosquitto_Client_setWill_args, ZEND_ACC_PUBLIC) 1372 | PHP_ME(Mosquitto_Client, clearWill, NULL, ZEND_ACC_PUBLIC) 1373 | PHP_ME(Mosquitto_Client, setReconnectDelay, Mosquitto_Client_setReconnectDelay_args, ZEND_ACC_PUBLIC) 1374 | PHP_ME(Mosquitto_Client, setMessageRetry, Mosquitto_Client_setMessageRetry_args, ZEND_ACC_PUBLIC) 1375 | PHP_ME(Mosquitto_Client, connect, Mosquitto_Client_connect_args, ZEND_ACC_PUBLIC) 1376 | PHP_ME(Mosquitto_Client, disconnect, NULL, ZEND_ACC_PUBLIC) 1377 | PHP_ME(Mosquitto_Client, setMaxInFlightMessages, Mosquitto_Client_setMaxInFlightMessages_args, ZEND_ACC_PUBLIC) 1378 | PHP_ME(Mosquitto_Client, publish, Mosquitto_Client_publish_args, ZEND_ACC_PUBLIC) 1379 | PHP_ME(Mosquitto_Client, subscribe, Mosquitto_Client_subscribe_args, ZEND_ACC_PUBLIC) 1380 | PHP_ME(Mosquitto_Client, unsubscribe, Mosquitto_Client_unsubscribe_args, ZEND_ACC_PUBLIC) 1381 | PHP_ME(Mosquitto_Client, loop, Mosquitto_Client_loop_args, ZEND_ACC_PUBLIC) 1382 | PHP_ME(Mosquitto_Client, loopForever, Mosquitto_Client_loopForever_args, ZEND_ACC_PUBLIC) 1383 | PHP_ME(Mosquitto_Client, exitLoop, NULL, ZEND_ACC_PUBLIC) 1384 | {NULL, NULL, NULL} 1385 | }; 1386 | /* }}} */ 1387 | 1388 | /* {{{ mosquitto_functions[] */ 1389 | const zend_function_entry mosquitto_functions[] = { 1390 | PHP_FE_END /* Must be the last line in mosquitto_functions[] */ 1391 | }; 1392 | /* }}} */ 1393 | 1394 | /* {{{ mosquitto_module_entry */ 1395 | zend_module_entry mosquitto_module_entry = { 1396 | #if ZEND_MODULE_API_NO >= 20010901 1397 | STANDARD_MODULE_HEADER, 1398 | #endif 1399 | "mosquitto", 1400 | NULL, 1401 | PHP_MINIT(mosquitto), 1402 | PHP_MSHUTDOWN(mosquitto), 1403 | NULL, 1404 | NULL, 1405 | PHP_MINFO(mosquitto), 1406 | #if ZEND_MODULE_API_NO >= 20010901 1407 | PHP_MOSQUITTO_VERSION, 1408 | #endif 1409 | PHP_MODULE_GLOBALS(mosquitto), 1410 | NULL, 1411 | NULL, 1412 | NULL, 1413 | STANDARD_MODULE_PROPERTIES_EX 1414 | }; 1415 | /* }}} */ 1416 | 1417 | #ifdef COMPILE_DL_MOSQUITTO 1418 | ZEND_GET_MODULE(mosquitto) 1419 | #endif 1420 | 1421 | /* {{{ PHP_MINIT_FUNCTION */ 1422 | PHP_MINIT_FUNCTION(mosquitto) 1423 | { 1424 | zend_class_entry client_ce, exception_ce; 1425 | 1426 | memcpy(&mosquitto_std_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 1427 | mosquitto_std_object_handlers.clone_obj = NULL; 1428 | #ifdef ZEND_ENGINE_3 1429 | mosquitto_std_object_handlers.offset = XtOffsetOf(mosquitto_client_object, std); 1430 | mosquitto_std_object_handlers.free_obj = mosquitto_client_object_free; 1431 | mosquitto_std_object_handlers.dtor_obj = mosquitto_client_object_destroy; 1432 | #endif 1433 | 1434 | INIT_NS_CLASS_ENTRY(client_ce, "Mosquitto", "Client", mosquitto_client_methods); 1435 | mosquitto_ce_client = zend_register_internal_class(&client_ce TSRMLS_CC); 1436 | mosquitto_ce_client->create_object = mosquitto_client_object_new; 1437 | 1438 | INIT_NS_CLASS_ENTRY(exception_ce, "Mosquitto", "Exception", NULL); 1439 | mosquitto_ce_exception = zend_register_internal_class_ex(&exception_ce, zend_exception_get_default(TSRMLS_C) 1440 | #ifndef ZEND_ENGINE_3 1441 | , "Exception" TSRMLS_CC 1442 | #endif 1443 | ); 1444 | 1445 | #define REGISTER_MOSQUITTO_LONG_CONST(const_name, value) \ 1446 | zend_declare_class_constant_long(mosquitto_ce_client, const_name, sizeof(const_name)-1, (long)value TSRMLS_CC); \ 1447 | REGISTER_LONG_CONSTANT(#value, value, CONST_CS | CONST_PERSISTENT); 1448 | 1449 | REGISTER_MOSQUITTO_LONG_CONST("LOG_INFO", MOSQ_LOG_INFO); 1450 | REGISTER_MOSQUITTO_LONG_CONST("LOG_NOTICE", MOSQ_LOG_NOTICE); 1451 | REGISTER_MOSQUITTO_LONG_CONST("LOG_WARNING", MOSQ_LOG_WARNING); 1452 | REGISTER_MOSQUITTO_LONG_CONST("LOG_ERR", MOSQ_LOG_ERR); 1453 | REGISTER_MOSQUITTO_LONG_CONST("LOG_DEBUG", MOSQ_LOG_DEBUG); 1454 | 1455 | zend_declare_class_constant_long(mosquitto_ce_client, "SSL_VERIFY_NONE", sizeof("SSL_VERIFY_NONE")-1, 0 TSRMLS_CC); 1456 | zend_declare_class_constant_long(mosquitto_ce_client, "SSL_VERIFY_PEER", sizeof("SSL_VERIFY_PEER")-1, 1 TSRMLS_CC); 1457 | 1458 | mosquitto_lib_init(); 1459 | 1460 | PHP_MINIT(mosquitto_message)(INIT_FUNC_ARGS_PASSTHRU); 1461 | 1462 | return SUCCESS; 1463 | } 1464 | /* }}} */ 1465 | 1466 | /* {{{ PHP_MSHUTDOWN_FUNCTION */ 1467 | PHP_MSHUTDOWN_FUNCTION(mosquitto) 1468 | { 1469 | mosquitto_lib_cleanup(); 1470 | return SUCCESS; 1471 | } 1472 | /* }}} */ 1473 | 1474 | /* {{{ PHP_MINFO_FUNCTION */ 1475 | PHP_MINFO_FUNCTION(mosquitto) 1476 | { 1477 | char tmp[16]; 1478 | snprintf(tmp, sizeof(tmp), "%d.%d.%d", LIBMOSQUITTO_MAJOR, LIBMOSQUITTO_MINOR, LIBMOSQUITTO_REVISION); 1479 | 1480 | php_info_print_table_start(); 1481 | php_info_print_table_header(2, "Mosquitto support", "enabled"); 1482 | php_info_print_table_colspan_header(2, 1483 | #ifdef COMPILE_DL_MOSQUITTO 1484 | "Compiled as dynamic module" 1485 | #else 1486 | "Compiled as static module" 1487 | #endif 1488 | ); 1489 | php_info_print_table_row(2, "libmosquitto version", tmp); 1490 | php_info_print_table_row(2, "Extension version", PHP_MOSQUITTO_VERSION); 1491 | php_info_print_table_end(); 1492 | } 1493 | /* }}} */ 1494 | 1495 | /* 1496 | * Local variables: 1497 | * tab-width: 4 1498 | * c-basic-offset: 4 1499 | * End: 1500 | * vim600: noet sw=4 ts=4 fdm=marker 1501 | * vim<600: noet sw=4 ts=4 1502 | */ 1503 | -------------------------------------------------------------------------------- /mosquitto_message.c: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include "php.h" 6 | #include "php_ini.h" 7 | #include "zend_variables.h" 8 | #include "zend_exceptions.h" 9 | #include "zend_API.h" 10 | #include "ext/standard/info.h" 11 | #include "php_mosquitto.h" 12 | 13 | zend_class_entry *mosquitto_ce_message; 14 | static zend_object_handlers mosquitto_message_object_handlers; 15 | static HashTable php_mosquitto_message_properties; 16 | 17 | #ifdef ZEND_ENGINE_3 18 | typedef size_t mosquitto_strlen_type; 19 | #else 20 | # ifndef Z_OBJ_P 21 | # define Z_OBJ_P(pzv) ((zend_object*)zend_object_store_get_object(pzv TSRMLS_CC)) 22 | # endif 23 | typedef int mosquitto_strlen_type; 24 | #endif 25 | 26 | /* {{{ Arginfo */ 27 | 28 | ZEND_BEGIN_ARG_INFO(Mosquitto_Message_topicMatchesSub_args, ZEND_SEND_BY_VAL) 29 | ZEND_ARG_INFO(0, topic) 30 | ZEND_ARG_INFO(0, subscription) 31 | ZEND_END_ARG_INFO() 32 | 33 | ZEND_BEGIN_ARG_INFO(Mosquitto_Message_tokeniseTopic_args, ZEND_SEND_BY_VAL) 34 | ZEND_ARG_INFO(0, topic) 35 | ZEND_END_ARG_INFO() 36 | 37 | /* }}} */ 38 | 39 | PHP_METHOD(Mosquitto_Message, __construct) 40 | { 41 | PHP_MOSQUITTO_ERROR_HANDLING(); 42 | if (zend_parse_parameters_none() == FAILURE) { 43 | PHP_MOSQUITTO_RESTORE_ERRORS(); 44 | return; 45 | } 46 | PHP_MOSQUITTO_RESTORE_ERRORS(); 47 | } 48 | 49 | /* {{{ Mosquitto\Message::topicMatchesSub() */ 50 | PHP_METHOD(Mosquitto_Message, topicMatchesSub) 51 | { 52 | char *topic = NULL, *subscription = NULL; 53 | mosquitto_strlen_type topic_len, subscription_len; 54 | zend_bool result; 55 | 56 | PHP_MOSQUITTO_ERROR_HANDLING(); 57 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", 58 | &topic, &topic_len, &subscription, &subscription_len) == FAILURE) { 59 | PHP_MOSQUITTO_RESTORE_ERRORS(); 60 | return; 61 | } 62 | PHP_MOSQUITTO_RESTORE_ERRORS(); 63 | 64 | mosquitto_topic_matches_sub(subscription, topic, (bool *) &result); 65 | RETURN_BOOL(result); 66 | } 67 | /* }}} */ 68 | 69 | /* {{{ Mosquitto\Message::tokeniseTopic() */ 70 | PHP_METHOD(Mosquitto_Message, tokeniseTopic) 71 | { 72 | char *topic = NULL, **topics = NULL; 73 | mosquitto_strlen_type topic_len = 0, retval = 0, count = 0, i = 0; 74 | 75 | PHP_MOSQUITTO_ERROR_HANDLING(); 76 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &topic, &topic_len) == FAILURE) { 77 | PHP_MOSQUITTO_RESTORE_ERRORS(); 78 | return; 79 | } 80 | PHP_MOSQUITTO_RESTORE_ERRORS(); 81 | 82 | retval = mosquitto_sub_topic_tokenise(topic, &topics, (int*)&count); 83 | 84 | if (retval == MOSQ_ERR_NOMEM) { 85 | zend_throw_exception_ex(mosquitto_ce_exception, 0 TSRMLS_CC, "Failed to tokenise topic"); 86 | return; 87 | } 88 | 89 | array_init(return_value); 90 | for (i = 0; i < count; i++) { 91 | if (topics[i] == NULL) { 92 | add_next_index_null(return_value); 93 | } else { 94 | #ifdef ZEND_ENGINE_3 95 | add_next_index_string(return_value, topics[i]); 96 | #else 97 | add_next_index_string(return_value, topics[i], 1); 98 | #endif 99 | } 100 | } 101 | 102 | mosquitto_sub_topic_tokens_free(&topics, count); 103 | } 104 | /* }}} */ 105 | 106 | PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_READER_FUNCTION(mid); 107 | PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_READER_FUNCTION(qos); 108 | 109 | #ifdef ZEND_ENGINE_3 110 | static int php_mosquitto_message_read_retain(mosquitto_message_object *mosquitto_object, zval *retval) 111 | { 112 | ZVAL_BOOL(retval, mosquitto_object->message.retain); 113 | return SUCCESS; 114 | } 115 | 116 | static int php_mosquitto_message_read_topic(mosquitto_message_object *mosquitto_object, zval *retval) 117 | { 118 | if (mosquitto_object->message.topic != NULL) { 119 | ZVAL_STRING(retval, mosquitto_object->message.topic); 120 | } else { 121 | ZVAL_NULL(retval); 122 | } 123 | 124 | return SUCCESS; 125 | } 126 | 127 | static int php_mosquitto_message_read_payload(mosquitto_message_object *mosquitto_object, zval *retval) 128 | { 129 | ZVAL_STRINGL(retval, mosquitto_object->message.payload, mosquitto_object->message.payloadlen); 130 | return SUCCESS; 131 | } 132 | #else 133 | static int php_mosquitto_message_read_retain(mosquitto_message_object *mosquitto_object, zval **retval TSRMLS_DC) 134 | { 135 | MAKE_STD_ZVAL(*retval); 136 | ZVAL_BOOL(*retval, mosquitto_object->message.retain); 137 | return SUCCESS; 138 | } 139 | 140 | static int php_mosquitto_message_read_topic(mosquitto_message_object *mosquitto_object, zval **retval TSRMLS_DC) 141 | { 142 | MAKE_STD_ZVAL(*retval); 143 | 144 | if (mosquitto_object->message.topic != NULL) { 145 | ZVAL_STRING(*retval, mosquitto_object->message.topic, 1); 146 | } else { 147 | ZVAL_NULL(*retval); 148 | } 149 | 150 | return SUCCESS; 151 | } 152 | 153 | static int php_mosquitto_message_read_payload(mosquitto_message_object *mosquitto_object, zval **retval TSRMLS_DC) 154 | { 155 | MAKE_STD_ZVAL(*retval); 156 | ZVAL_STRINGL(*retval, mosquitto_object->message.payload, mosquitto_object->message.payloadlen, 1); 157 | return SUCCESS; 158 | } 159 | #endif 160 | 161 | PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_WRITER_FUNCTION(mid); 162 | PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_WRITER_FUNCTION(qos); 163 | 164 | static int php_mosquitto_message_write_retain(mosquitto_message_object *mosquitto_object, zval *newval TSRMLS_DC) 165 | { 166 | mosquitto_object->message.retain = zend_is_true(newval); 167 | 168 | return SUCCESS; 169 | } 170 | 171 | static int php_mosquitto_message_write_topic(mosquitto_message_object *mosquitto_object, zval *newval TSRMLS_DC) 172 | { 173 | zval ztmp; 174 | if (Z_TYPE_P(newval) != IS_STRING) { 175 | ztmp = *newval; 176 | zval_copy_ctor(&ztmp); 177 | convert_to_string(&ztmp); 178 | newval = &ztmp; 179 | } 180 | 181 | if (mosquitto_object->message.topic && mosquitto_object->owned_topic) { 182 | efree(mosquitto_object->message.topic); 183 | } 184 | 185 | mosquitto_object->message.topic = estrdup(Z_STRVAL_P(newval)); 186 | mosquitto_object->owned_topic = 1; 187 | 188 | if (newval == &ztmp) { 189 | zval_dtor(newval); 190 | } 191 | 192 | return SUCCESS; 193 | } 194 | 195 | static int php_mosquitto_message_write_payload(mosquitto_message_object *mosquitto_object, zval *newval TSRMLS_DC) 196 | { 197 | zval ztmp; 198 | if (Z_TYPE_P(newval) != IS_STRING) { 199 | ztmp = *newval; 200 | zval_copy_ctor(&ztmp); 201 | convert_to_string(&ztmp); 202 | newval = &ztmp; 203 | } 204 | 205 | if (mosquitto_object->message.payload && mosquitto_object->owned_payload) { 206 | efree(mosquitto_object->message.payload); 207 | mosquitto_object->message.payloadlen = 0; 208 | } 209 | 210 | mosquitto_object->message.payload = estrdup(Z_STRVAL_P(newval)); 211 | mosquitto_object->message.payloadlen = Z_STRLEN_P(newval); 212 | mosquitto_object->owned_payload = 1; 213 | 214 | if (newval == &ztmp) { 215 | zval_dtor(newval); 216 | } 217 | 218 | return SUCCESS; 219 | } 220 | 221 | const php_mosquitto_prop_handler php_mosquitto_message_property_entries[] = { 222 | PHP_MOSQUITTO_MESSAGE_PROPERTY_ENTRY_RECORD(mid), 223 | PHP_MOSQUITTO_MESSAGE_PROPERTY_ENTRY_RECORD(topic), 224 | PHP_MOSQUITTO_MESSAGE_PROPERTY_ENTRY_RECORD(payload), 225 | PHP_MOSQUITTO_MESSAGE_PROPERTY_ENTRY_RECORD(qos), 226 | PHP_MOSQUITTO_MESSAGE_PROPERTY_ENTRY_RECORD(retain), 227 | {NULL, 0, NULL, NULL} 228 | }; 229 | 230 | #ifdef ZEND_ENGINE_3 231 | # define READ_PROPERTY_DC , void **cache_slot, zval *retval 232 | # define READ_PROPERTY_CC , cache_slot, retval 233 | # define WRITE_PROPERTY_DC , void **cache_slot 234 | # define WRITE_PROPERTY_CC , cache_slot 235 | # define HAS_PROPERTY_DC , void **cache_slot 236 | # define HAS_PROPERTY_CC , cache_slot 237 | 238 | static php_mosquitto_prop_handler *mosquitto_get_prop_handler(zval *prop) { 239 | zval *ret = zend_hash_find(&php_mosquitto_message_properties, Z_STR_P(prop)); 240 | if (!ret || Z_TYPE_P(ret) != IS_PTR) { 241 | return NULL; 242 | } 243 | return (php_mosquitto_prop_handler*)Z_PTR_P(ret); 244 | } 245 | #else 246 | # define READ_PROPERTY_DC ZEND_LITERAL_KEY_DC TSRMLS_DC 247 | # define READ_PROPERTY_CC ZEND_LITERAL_KEY_CC TSRMLS_CC 248 | # define WRITE_PROPERTY_DC ZEND_LITERAL_KEY_DC TSRMLS_DC 249 | # define WRITE_PROPERTY_CC ZEND_LITERAL_KEY_CC TSRMLS_CC 250 | # define HAS_PROPERTY_DC ZEND_LITERAL_KEY_DC TSRMLS_DC 251 | # define HAS_PROPERTY_CC ZEND_LITERAL_KEY_CC TSRMLS_CC 252 | 253 | static php_mosquitto_prop_handler *mosquitto_get_prop_handler(zval *prop) { 254 | php_mosquitto_prop_handler *hnd; 255 | if (FAILURE == zend_hash_find(&php_mosquitto_message_properties, Z_STRVAL_P(prop), Z_STRLEN_P(prop)+1, (void**) &hnd)) { 256 | return NULL; 257 | } 258 | return hnd; 259 | } 260 | #endif 261 | 262 | zval *php_mosquitto_message_read_property(zval *object, zval *member, int type READ_PROPERTY_DC) { 263 | zval tmp_member; 264 | #ifndef ZEND_ENGINE_3 265 | zval *retval; 266 | #endif 267 | mosquitto_message_object *message_object = mosquitto_message_object_from_zend_object(Z_OBJ_P(object)); 268 | php_mosquitto_prop_handler *hnd; 269 | 270 | if (Z_TYPE_P(member) != IS_STRING) { 271 | tmp_member = *member; 272 | zval_copy_ctor(&tmp_member); 273 | convert_to_string(&tmp_member); 274 | member = &tmp_member; 275 | } 276 | hnd = mosquitto_get_prop_handler(member); 277 | 278 | if (hnd && hnd->read_func) { 279 | #ifdef ZEND_ENGINE_3 280 | if (FAILURE == hnd->read_func(message_object, retval)) { 281 | ZVAL_NULL(retval); 282 | } 283 | #else 284 | if (SUCCESS == hnd->read_func(message_object, &retval TSRMLS_CC)) { 285 | /* ensure we're creating a temporary variable */ 286 | Z_SET_REFCOUNT_P(retval, 0); 287 | } else { 288 | retval = EG(uninitialized_zval_ptr); 289 | } 290 | #endif 291 | } else { 292 | zend_object_handlers * std_hnd = zend_get_std_object_handlers(); 293 | retval = std_hnd->read_property(object, member, type READ_PROPERTY_CC); 294 | } 295 | 296 | if (member == &tmp_member) { 297 | zval_dtor(member); 298 | } 299 | 300 | return(retval); 301 | } 302 | 303 | void php_mosquitto_message_write_property(zval *object, zval *member, zval *value WRITE_PROPERTY_DC) 304 | { 305 | zval tmp_member; 306 | mosquitto_message_object *obj = mosquitto_message_object_from_zend_object(Z_OBJ_P(object)); 307 | php_mosquitto_prop_handler *hnd; 308 | 309 | if (Z_TYPE_P(member) != IS_STRING) { 310 | tmp_member = *member; 311 | zval_copy_ctor(&tmp_member); 312 | convert_to_string(&tmp_member); 313 | member = &tmp_member; 314 | } 315 | 316 | hnd = mosquitto_get_prop_handler(member); 317 | 318 | if (hnd && hnd->write_func) { 319 | hnd->write_func(obj, value TSRMLS_CC); 320 | #ifdef ZEND_ENGINE_3 321 | if (Z_REFCOUNTED_P(value)) { 322 | Z_ADDREF_P(value); 323 | zval_ptr_dtor(value); 324 | } 325 | #else 326 | if (! PZVAL_IS_REF(value) && Z_REFCOUNT_P(value) == 0) { 327 | Z_ADDREF_P(value); 328 | zval_ptr_dtor(&value); 329 | } 330 | #endif 331 | } else { 332 | zend_object_handlers * std_hnd = zend_get_std_object_handlers(); 333 | std_hnd->write_property(object, member, value WRITE_PROPERTY_CC); 334 | } 335 | 336 | if (member == &tmp_member) { 337 | zval_dtor(member); 338 | } 339 | } 340 | 341 | static int php_mosquitto_message_has_property(zval *object, zval *member, int has_set_exists HAS_PROPERTY_DC) 342 | { 343 | php_mosquitto_prop_handler *hnd = mosquitto_get_prop_handler(member); 344 | int ret = 0; 345 | #ifdef ZEND_ENGINE_3 346 | zval rv; 347 | zval *retval = &rv; 348 | #endif 349 | 350 | if (hnd) { 351 | switch (has_set_exists) { 352 | case 2: 353 | ret = 1; 354 | break; 355 | case 0: { 356 | zval *value = php_mosquitto_message_read_property(object, member, BP_VAR_IS READ_PROPERTY_CC); 357 | #ifdef ZEND_ENGINE_3 358 | if (Z_REFCOUNTED_P(value)) { 359 | Z_ADDREF_P(value); 360 | zval_ptr_dtor(value); 361 | } 362 | #else 363 | if (value != EG(uninitialized_zval_ptr)) { 364 | ret = Z_TYPE_P(value) != IS_NULL? 1:0; 365 | /* refcount is 0 */ 366 | Z_ADDREF_P(value); 367 | zval_ptr_dtor(&value); 368 | } 369 | #endif 370 | break; 371 | } 372 | default: { 373 | zval *value = php_mosquitto_message_read_property(object, member, BP_VAR_IS READ_PROPERTY_CC); 374 | #ifdef ZEND_ENGINE_3 375 | if (Z_REFCOUNTED_P(value)) { 376 | Z_ADDREF_P(value); 377 | zval_ptr_dtor(value); 378 | } 379 | #else 380 | if (value != EG(uninitialized_zval_ptr)) { 381 | convert_to_boolean(value); 382 | ret = Z_BVAL_P(value)? 1:0; 383 | /* refcount is 0 */ 384 | Z_ADDREF_P(value); 385 | zval_ptr_dtor(&value); 386 | } 387 | #endif 388 | break; 389 | } 390 | } 391 | } else { 392 | zend_object_handlers * std_hnd = zend_get_std_object_handlers(); 393 | ret = std_hnd->has_property(object, member, has_set_exists HAS_PROPERTY_CC); 394 | } 395 | return ret; 396 | } 397 | 398 | #ifndef ZEND_ENGINE_3 399 | # ifndef ZEND_HASH_FOREACH_PTR 400 | # define ZEND_HASH_FOREACH_KEY_PTR(ht, idx, key, ptr) \ 401 | { \ 402 | HashPosition pos; \ 403 | for (zend_hash_internal_pointer_reset_ex(ht, &pos); \ 404 | zend_hash_get_current_data_ex(ht, (void**)&ptr, &pos) == SUCCESS; \ 405 | zend_hash_move_forward_ex(ht, &pos)) { \ 406 | key = NULL; \ 407 | zend_hash_get_current_key_ex(ht, &key, &key##_len, &idx, 0, &pos); \ 408 | { 409 | # endif 410 | # ifndef ZEND_HASH_FOREACH_END 411 | # define ZEND_HASH_FOREACH_END() \ 412 | } \ 413 | } \ 414 | } 415 | # endif 416 | #endif 417 | 418 | static HashTable *php_mosquitto_message_get_properties(zval *object TSRMLS_DC) 419 | { 420 | mosquitto_message_object *obj = mosquitto_message_object_from_zend_object(Z_OBJ_P(object)); 421 | php_mosquitto_prop_handler *hnd; 422 | HashTable *props; 423 | #ifdef ZEND_ENGINE_3 424 | zend_string *key; 425 | zend_long num_key; 426 | #else 427 | char *key; 428 | uint key_len; 429 | ulong num_key; 430 | #endif 431 | 432 | props = zend_std_get_properties(object TSRMLS_CC); 433 | 434 | ZEND_HASH_FOREACH_KEY_PTR(&php_mosquitto_message_properties, num_key, key, hnd) { 435 | #ifdef ZEND_ENGINE_3 436 | zval val; 437 | if (!hnd->read_func || (hnd->read_func(obj, &val) != SUCCESS)) { 438 | ZVAL_NULL(&val); 439 | } 440 | if (key) { 441 | zend_hash_update(props, key, &val); 442 | } else { 443 | zend_hash_index_update(props, num_key, &val); 444 | } 445 | #else 446 | zval *val; 447 | if (!hnd->read_func || hnd->read_func(obj, &val TSRMLS_CC) != SUCCESS) { 448 | val = EG(uninitialized_zval_ptr); 449 | Z_ADDREF_P(val); 450 | } 451 | if (key) { 452 | zend_hash_update(props, key, key_len, (void *)&val, sizeof(zval*), NULL); 453 | } else { 454 | zend_hash_index_update(props, num_key, (void *)&val, sizeof(zval*), NULL); 455 | } 456 | #endif 457 | } ZEND_HASH_FOREACH_END(); 458 | 459 | return obj->std.properties; 460 | } 461 | 462 | 463 | void php_mosquitto_message_add_property(HashTable *h, const char *name, size_t name_length, php_mosquitto_read_t read_func, php_mosquitto_write_t write_func TSRMLS_DC) 464 | { 465 | #ifdef ZEND_ENGINE_3 466 | php_mosquitto_prop_handler *p = (php_mosquitto_prop_handler*)pemalloc(sizeof(php_mosquitto_prop_handler), 1); 467 | #else 468 | php_mosquitto_prop_handler val, *p = &val; 469 | #endif 470 | 471 | p->name = (char*) name; 472 | p->name_length = name_length; 473 | p->read_func = read_func; 474 | p->write_func = write_func; 475 | #ifdef ZEND_ENGINE_3 476 | { 477 | zend_string *key = zend_string_init(name, name_length, 1); 478 | zval hnd; 479 | ZVAL_PTR(&hnd, p); 480 | zend_hash_add(h, key, &hnd); 481 | } 482 | #else 483 | zend_hash_add(h, (char *)name, name_length + 1, p, sizeof(php_mosquitto_prop_handler), NULL); 484 | #endif 485 | } 486 | 487 | static void mosquitto_message_object_destroy(zend_object *object TSRMLS_DC) 488 | { 489 | mosquitto_message_object *message = mosquitto_message_object_from_zend_object(object); 490 | #ifdef ZEND_ENGINE_3 491 | zend_object_std_dtor(object); 492 | #else 493 | zend_hash_destroy(message->std.properties); 494 | FREE_HASHTABLE(message->std.properties); 495 | #endif 496 | if (message->owned_topic == 1) { 497 | efree(message->message.topic); 498 | } 499 | 500 | if (message->owned_payload == 1) { 501 | efree(message->message.payload); 502 | } 503 | 504 | #ifndef ZEND_ENGINE_3 505 | efree(object); 506 | #endif 507 | } 508 | 509 | #ifdef ZEND_ENGINE_3 510 | static zend_object *mosquitto_message_object_new(zend_class_entry *ce) { 511 | mosquitto_message_object *msg = ecalloc(1, sizeof(mosquitto_message_object) + zend_object_properties_size(ce)); 512 | zend_object *ret = mosquitto_message_object_to_zend_object(msg); 513 | 514 | #ifdef MOSQUITTO_NEED_TSRMLS 515 | message_obj->TSRMLS_C = TSRMLS_C; 516 | #endif 517 | 518 | zend_object_std_init(ret, ce); 519 | ret->handlers = &mosquitto_message_object_handlers; 520 | 521 | return ret; 522 | } 523 | #else 524 | static zend_object_value mosquitto_message_object_new(zend_class_entry *ce TSRMLS_DC) { 525 | 526 | zend_object_value retval; 527 | mosquitto_message_object *message_obj; 528 | #if PHP_VERSION_ID < 50399 529 | zval *temp; 530 | #endif 531 | 532 | message_obj = ecalloc(1, sizeof(mosquitto_message_object)); 533 | message_obj->std.ce = ce; 534 | 535 | #ifdef MOSQUITTO_NEED_TSRMLS 536 | message_obj->TSRMLS_C = TSRMLS_C; 537 | #endif 538 | 539 | ALLOC_HASHTABLE(message_obj->std.properties); 540 | zend_hash_init(message_obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); 541 | #if PHP_VERSION_ID < 50399 542 | zend_hash_copy(message_obj->std.properties, &mosquitto_ce_message->default_properties, (copy_ctor_func_t) zval_add_ref,(void *) &temp, sizeof(zval *)); 543 | #else 544 | object_properties_init(&message_obj->std, mosquitto_ce_message); 545 | #endif 546 | retval.handle = zend_objects_store_put(message_obj, NULL, (zend_objects_free_object_storage_t) mosquitto_message_object_destroy, NULL TSRMLS_CC); 547 | retval.handlers = &mosquitto_message_object_handlers; 548 | return retval; 549 | } 550 | #endif 551 | 552 | const zend_function_entry mosquitto_message_methods[] = { 553 | PHP_ME(Mosquitto_Message, __construct, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 554 | PHP_ME(Mosquitto_Message, topicMatchesSub, Mosquitto_Message_topicMatchesSub_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) 555 | PHP_ME(Mosquitto_Message, tokeniseTopic, Mosquitto_Message_tokeniseTopic_args, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) 556 | PHP_FE_END 557 | }; 558 | 559 | PHP_MINIT_FUNCTION(mosquitto_message) 560 | { 561 | zend_class_entry message_ce; 562 | memcpy(&mosquitto_message_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 563 | mosquitto_message_object_handlers.read_property = php_mosquitto_message_read_property; 564 | mosquitto_message_object_handlers.write_property = php_mosquitto_message_write_property; 565 | mosquitto_message_object_handlers.has_property = php_mosquitto_message_has_property; 566 | mosquitto_message_object_handlers.get_properties = php_mosquitto_message_get_properties; 567 | #ifdef ZEND_ENGINE_3 568 | mosquitto_message_object_handlers.offset = XtOffsetOf(mosquitto_message_object, std); 569 | mosquitto_message_object_handlers.free_obj = mosquitto_message_object_destroy; 570 | #endif 571 | 572 | INIT_NS_CLASS_ENTRY(message_ce, "Mosquitto", "Message", mosquitto_message_methods); 573 | mosquitto_ce_message = zend_register_internal_class(&message_ce TSRMLS_CC); 574 | mosquitto_ce_message->create_object = mosquitto_message_object_new; 575 | 576 | zend_hash_init(&php_mosquitto_message_properties, 0, NULL, NULL, 1); 577 | PHP_MOSQUITTO_ADD_PROPERTIES(&php_mosquitto_message_properties, php_mosquitto_message_property_entries); 578 | 579 | return SUCCESS; 580 | } 581 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mosquitto 4 | pecl.php.net 5 | Extension for libmosquitto 6 | Mosquitto provides support for the MQTT protocol, including publishing, subscribing, and an event loop. 7 | 8 | Michael Maclean 9 | mgdm 10 | mgdm@php.net 11 | yes 12 | 13 | 14 | 2017-03-13 15 | 0.4.00.4.0 16 | betabeta 17 | BSD 3-Clause License 18 | 19 | * PHP 7 support thanks to Sara Golemon 20 | * Improved docs hosted on ReadTheDocs 21 | * Some memory usage fixes 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 5.3.0 87 | 88 | 89 | 1.4.0 90 | 91 | 92 | 93 | 94 | 95 | mosquitto 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | -------------------------------------------------------------------------------- /php_mosquitto.h: -------------------------------------------------------------------------------- 1 | #ifndef PHP_MOSQUITTO_H 2 | #define PHP_MOSQUITTO_H 3 | 4 | #define PHP_MOSQUITTO_VERSION "0.4.0" 5 | 6 | extern zend_module_entry mosquitto_module_entry; 7 | #define phpext_mosquitto_ptr &mosquitto_module_entry 8 | 9 | #ifdef PHP_WIN32 10 | # define PHP_MOSQUITTO_API __declspec(dllexport) 11 | #elif defined(__GNUC__) && __GNUC__ >= 4 12 | # define PHP_MOSQUITTO_API __attribute__ ((visibility("default"))) 13 | #else 14 | # define PHP_MOSQUITTO_API 15 | #endif 16 | 17 | #ifdef __GLIBC__ 18 | #define POSSIBLY_UNUSED __attribute__((unused)) 19 | #else 20 | #define POSSIBLY_UNUSED 21 | #endif 22 | 23 | #if defined(PHP_VERSION_ID) && (PHP_VERSION_ID >= 50399) 24 | # define ZEND_LITERAL_KEY_DC , const zend_literal *_zend_literal_key 25 | # define ZEND_LITERAL_KEY_CC , _zend_literal_key 26 | # define ZEND_LITERAL_NIL_CC , NULL 27 | #else 28 | # define ZEND_LITERAL_KEY_DC 29 | # define ZEND_LITERAL_KEY_CC 30 | # define ZEND_LITERAL_NIL_CC 31 | #endif 32 | 33 | #ifdef ZTS 34 | #include "TSRM.h" 35 | #endif 36 | 37 | #include 38 | 39 | #if defined(ZEND_ENGINE_2) && defined(ZTS) 40 | # define MOSQUITTO_NEED_TSRMLS 41 | #endif 42 | 43 | typedef struct _mosquitto_client_object { 44 | #ifndef ZEND_ENGINE_3 45 | zend_object std; 46 | #endif 47 | struct mosquitto *client; 48 | 49 | zend_fcall_info connect_callback; 50 | zend_fcall_info_cache connect_callback_cache; 51 | zend_fcall_info subscribe_callback; 52 | zend_fcall_info_cache subscribe_callback_cache; 53 | zend_fcall_info unsubscribe_callback; 54 | zend_fcall_info_cache unsubscribe_callback_cache; 55 | zend_fcall_info message_callback; 56 | zend_fcall_info_cache message_callback_cache; 57 | zend_fcall_info publish_callback; 58 | zend_fcall_info_cache publish_callback_cache; 59 | zend_fcall_info disconnect_callback; 60 | zend_fcall_info_cache disconnect_callback_cache; 61 | zend_fcall_info log_callback; 62 | zend_fcall_info_cache log_callback_cache; 63 | 64 | int looping; 65 | 66 | #ifdef MOSQUITTO_NEED_TSRMLS 67 | TSRMLS_D; 68 | #endif 69 | #ifdef ZEND_ENGINE_3 70 | zend_object std; /* Must be last */ 71 | #endif 72 | } mosquitto_client_object; 73 | 74 | typedef struct _mosquitto_message_object { 75 | #ifndef ZEND_ENGINE_3 76 | zend_object std; 77 | #endif 78 | struct mosquitto_message message; 79 | zend_bool owned_topic; 80 | zend_bool owned_payload; 81 | #ifdef MOSQUITTO_NEED_TSRMLS 82 | TSRMLS_D; 83 | #endif 84 | #ifdef ZEND_ENGINE_3 85 | zend_object std; /* Must be last */ 86 | #endif 87 | } mosquitto_message_object; 88 | 89 | static inline 90 | mosquitto_client_object *mosquitto_client_object_from_zend_object(zend_object* obj) { 91 | return (mosquitto_client_object*)( 92 | ((char*)obj) - XtOffsetOf(mosquitto_client_object, std) 93 | ); 94 | } 95 | 96 | static inline 97 | zend_object *mosquitto_client_object_to_zend_object(mosquitto_client_object* client) { 98 | return &(client->std); 99 | } 100 | 101 | static inline 102 | mosquitto_message_object *mosquitto_message_object_from_zend_object(zend_object* obj) { 103 | return (mosquitto_message_object*)( 104 | ((char*)obj) - XtOffsetOf(mosquitto_message_object, std) 105 | ); 106 | } 107 | 108 | static inline 109 | zend_object *mosquitto_message_object_to_zend_object(mosquitto_message_object* msg) { 110 | return &(msg->std); 111 | } 112 | 113 | #ifdef ZEND_ENGINE_3 114 | typedef int (*php_mosquitto_read_t)(mosquitto_message_object *mosquitto_object, zval *retval); 115 | #else 116 | typedef int (*php_mosquitto_read_t)(mosquitto_message_object *mosquitto_object, zval **retval TSRMLS_DC); 117 | #endif 118 | typedef int (*php_mosquitto_write_t)(mosquitto_message_object *mosquitto_object, zval *newval TSRMLS_DC); 119 | 120 | typedef struct _php_mosquitto_prop_handler { 121 | const char *name; 122 | size_t name_length; 123 | php_mosquitto_read_t read_func; 124 | php_mosquitto_write_t write_func; 125 | } php_mosquitto_prop_handler; 126 | 127 | 128 | #define PHP_MOSQUITTO_ERROR_HANDLING() \ 129 | zend_replace_error_handling(EH_THROW, mosquitto_ce_exception, &MQTTG(mosquitto_original_error_handling) TSRMLS_CC) 130 | 131 | #define PHP_MOSQUITTO_RESTORE_ERRORS() \ 132 | zend_restore_error_handling(&MQTTG(mosquitto_original_error_handling) TSRMLS_CC) 133 | 134 | #ifdef ZEND_ENGINE_3 135 | # define PHP_MOSQUITTO_FREE_CALLBACK(client, CALLBACK) \ 136 | if (ZEND_FCI_INITIALIZED(client->CALLBACK ## _callback)) { \ 137 | zval_ptr_dtor(&client->CALLBACK ## _callback.function_name); \ 138 | } \ 139 | \ 140 | if (client->CALLBACK ## _callback.object != NULL) { \ 141 | zval tmp_; \ 142 | ZVAL_OBJ(&tmp_, client->CALLBACK ## _callback.object); \ 143 | zval_ptr_dtor(&tmp_); \ 144 | } \ 145 | client->CALLBACK ## _callback = empty_fcall_info; \ 146 | client->CALLBACK ## _callback_cache = empty_fcall_info_cache; 147 | #else 148 | # define PHP_MOSQUITTO_FREE_CALLBACK(client, CALLBACK) \ 149 | if (ZEND_FCI_INITIALIZED(client->CALLBACK ## _callback)) { \ 150 | zval_ptr_dtor(&client->CALLBACK ## _callback.function_name); \ 151 | } \ 152 | \ 153 | if (client->CALLBACK ## _callback.object_ptr != NULL) { \ 154 | zval_ptr_dtor(&client->CALLBACK ## _callback.object_ptr); \ 155 | } \ 156 | client->CALLBACK ## _callback = empty_fcall_info; \ 157 | client->CALLBACK ## _callback_cache = empty_fcall_info_cache; 158 | #endif 159 | 160 | #define PHP_MOSQUITTO_MESSAGE_PROPERTY_ENTRY_RECORD(name) \ 161 | { "" #name "", sizeof("" #name "") - 1, php_mosquitto_message_read_##name, php_mosquitto_message_write_##name } 162 | 163 | #define PHP_MOSQUITTO_ADD_PROPERTIES(a, b) \ 164 | { \ 165 | int i = 0; \ 166 | while (b[i].name != NULL) { \ 167 | php_mosquitto_message_add_property((a), (b)[i].name, (b)[i].name_length, \ 168 | (php_mosquitto_read_t)(b)[i].read_func, (php_mosquitto_write_t)(b)[i].write_func TSRMLS_CC); \ 169 | i++; \ 170 | } \ 171 | } 172 | 173 | #ifdef ZEND_ENGINE_3 174 | # define PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_READER_FUNCTION(name) \ 175 | static int php_mosquitto_message_read_##name(mosquitto_message_object *mosquitto_object, zval *retval) { \ 176 | ZVAL_LONG(retval, mosquitto_object->message.name); \ 177 | return SUCCESS; \ 178 | } 179 | #else 180 | # define PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_READER_FUNCTION(name) \ 181 | static int php_mosquitto_message_read_##name(mosquitto_message_object *mosquitto_object, zval **retval TSRMLS_DC) { \ 182 | MAKE_STD_ZVAL(*retval); \ 183 | ZVAL_LONG(*retval, mosquitto_object->message.name); \ 184 | return SUCCESS; \ 185 | } 186 | #endif 187 | 188 | #define PHP_MOSQUITTO_MESSAGE_LONG_PROPERTY_WRITER_FUNCTION(name) \ 189 | static int php_mosquitto_message_write_##name(mosquitto_message_object *mosquitto_object, zval *newval TSRMLS_DC) \ 190 | { \ 191 | zval ztmp; \ 192 | if (Z_TYPE_P(newval) != IS_LONG) { \ 193 | ztmp = *newval; \ 194 | zval_copy_ctor(&ztmp); \ 195 | convert_to_long(&ztmp); \ 196 | newval = &ztmp; \ 197 | } \ 198 | \ 199 | mosquitto_object->message.name = Z_LVAL_P(newval); \ 200 | \ 201 | if (newval == &ztmp) { \ 202 | zval_dtor(newval); \ 203 | } \ 204 | return SUCCESS; \ 205 | } 206 | 207 | ZEND_BEGIN_MODULE_GLOBALS(mosquitto) 208 | char *client_key; 209 | int client_key_len; 210 | zend_object_handlers mosquitto_std_object_handlers; 211 | zend_error_handling mosquitto_original_error_handling; 212 | ZEND_END_MODULE_GLOBALS(mosquitto) 213 | 214 | #ifdef ZTS 215 | # define MQTTG(v) TSRMG(mosquitto_globals_id, zend_mosquitto_globals *, v) 216 | #else 217 | # define MQTTG(v) (mosquitto_globals.v) 218 | #endif 219 | 220 | ZEND_EXTERN_MODULE_GLOBALS(mosquitto) 221 | 222 | extern zend_class_entry *mosquitto_ce_exception; 223 | extern zend_class_entry *mosquitto_ce_client; 224 | extern zend_class_entry *mosquitto_ce_message; 225 | 226 | PHP_MOSQUITTO_API void php_mosquitto_connect_callback(struct mosquitto *mosq, void *obj, int rc); 227 | PHP_MOSQUITTO_API void php_mosquitto_disconnect_callback(struct mosquitto *mosq, void *obj, int rc); 228 | PHP_MOSQUITTO_API void php_mosquitto_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str); 229 | PHP_MOSQUITTO_API void php_mosquitto_message_callback(struct mosquitto *mosq, void *client_obj, const struct mosquitto_message *message); 230 | PHP_MOSQUITTO_API void php_mosquitto_subscribe_callback(struct mosquitto *mosq, void *client_obj, int mid, int qos_count, const int *granted_qos); 231 | PHP_MOSQUITTO_API void php_mosquitto_unsubscribe_callback(struct mosquitto *mosq, void *client_obj, int mid); 232 | PHP_MOSQUITTO_API void php_mosquitto_publish_callback(struct mosquitto *mosq, void *client_obj, int mid); 233 | PHP_MOSQUITTO_API void php_mosquitto_disconnect_callback(struct mosquitto *mosq, void *obj, int rc); 234 | 235 | PHP_MOSQUITTO_API char *php_mosquitto_strerror_wrapper(int err); 236 | void php_mosquitto_handle_errno(int retval, int err TSRMLS_DC); 237 | void php_mosquitto_exit_loop(mosquitto_client_object *object); 238 | 239 | PHP_MINIT_FUNCTION(mosquitto); 240 | PHP_MINIT_FUNCTION(mosquitto_message); 241 | PHP_MSHUTDOWN_FUNCTION(mosquitto); 242 | PHP_MINFO_FUNCTION(mosquitto); 243 | 244 | #endif /* PHP_MOSQUITTO_H */ 245 | 246 | /* __footer_here__ */ 247 | -------------------------------------------------------------------------------- /tests/Client/__construct.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::__construct() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | 33 | --EXPECTF-- 34 | object(Mosquitto\Client)#%d (%d) { 35 | } 36 | object(Mosquitto\Client)#%d (%d) { 37 | } 38 | object(Mosquitto\Client)#%d (%d) { 39 | } 40 | object(Mosquitto\Client)#%d (%d) { 41 | } 42 | object(Mosquitto\Client)#%d (%d) { 43 | } 44 | Caught exception. 45 | -------------------------------------------------------------------------------- /tests/Client/clearWill.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setWill() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | clearWill(); 11 | 12 | try { 13 | $client->clearWill(true); 14 | } catch (Mosquitto\Exception $e) { 15 | writeException($e); 16 | } 17 | 18 | ?> 19 | --EXPECTF-- 20 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::clearWill() expects exactly 0 parameters, 1 given 21 | -------------------------------------------------------------------------------- /tests/Client/connect.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::connect() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | connect(); 13 | } catch (Exception $e) { 14 | echo $e->getMessage(), "\n"; 15 | } 16 | 17 | /* Invalid hostname */ 18 | try { 19 | $client = new Mosquitto\Client(); 20 | $client->connect(false); 21 | } catch (Exception $e) { 22 | echo $e->getMessage(), "\n"; 23 | } 24 | 25 | /* Invalid hostname */ 26 | try { 27 | $client = new Mosquitto\Client(); 28 | $client->connect(":^(%^*:"); 29 | } catch (Exception $e) { 30 | echo $e->getMessage(), "\n"; 31 | } 32 | 33 | /* Invalid port */ 34 | try { 35 | $client = new Mosquitto\Client(); 36 | $client->connect(TEST_MQTT_HOST, 0); 37 | } catch (Exception $e) { 38 | echo $e->getMessage(), "\n"; 39 | } 40 | 41 | /* Invalid port */ 42 | try { 43 | $client = new Mosquitto\Client(); 44 | $client->connect(TEST_MQTT_HOST, new stdClass); 45 | } catch (Exception $e) { 46 | echo $e->getMessage(), "\n"; 47 | } 48 | 49 | /* Invalid keepalive */ 50 | try { 51 | $client = new Mosquitto\Client(); 52 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_PORT, new stdClass); 53 | var_dump($client); 54 | } catch (Exception $e) { 55 | echo $e->getMessage(), "\n"; 56 | } 57 | 58 | /* Invalid bind address */ 59 | try { 60 | $client = new Mosquitto\Client(); 61 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_PORT, 0, '^(%%^&*'); 62 | var_dump($client); 63 | } catch (Exception $e) { 64 | echo $e->getMessage(), "\n"; 65 | } 66 | 67 | /* Zero keepalive (OK) */ 68 | try { 69 | $client = new Mosquitto\Client(); 70 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_PORT, 0); 71 | var_dump($client); 72 | } catch (Exception $e) { 73 | echo $e->getMessage(), "\n"; 74 | } 75 | 76 | /* 10-second keepalive */ 77 | try { 78 | $client = new Mosquitto\Client(); 79 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_PORT, 10); 80 | var_dump($client); 81 | } catch (Exception $e) { 82 | echo $e->getMessage(), "\n"; 83 | } 84 | 85 | /* Bind to 127.0.0.1 - should work if connecting to localhost */ 86 | try { 87 | $client = new Mosquitto\Client(); 88 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_PORT, 0, '127.0.0.1'); 89 | var_dump($client); 90 | } catch (Exception $e) { 91 | echo $e->getMessage(), "\n"; 92 | } 93 | 94 | /* Specify just the host */ 95 | try { 96 | $client = new Mosquitto\Client(); 97 | $client->connect(TEST_MQTT_HOST); 98 | var_dump($client); 99 | } catch (Exception $e) { 100 | echo $e->getMessage(), "\n"; 101 | } 102 | 103 | ?> 104 | --EXPECTF-- 105 | Mosquitto\Client::connect() expects at least 1 parameter, 0 given 106 | %s error. 107 | %s error. 108 | Invalid function arguments provided. 109 | Mosquitto\Client::connect() expects parameter 2 to be %s, object given 110 | Mosquitto\Client::connect() expects parameter 3 to be %s, object given 111 | %s error. 112 | object(Mosquitto\Client)#%d (%d) { 113 | } 114 | object(Mosquitto\Client)#%d (%d) { 115 | } 116 | object(Mosquitto\Client)#%d (%d) { 117 | } 118 | object(Mosquitto\Client)#%d (%d) { 119 | } 120 | 121 | -------------------------------------------------------------------------------- /tests/Client/disconnect.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::disconnect() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | disconnect()); 12 | } catch (Exception $e) { 13 | var_dump($e->getMessage()); 14 | } 15 | 16 | $client = new Mosquitto\Client(); 17 | 18 | $client->onConnect(function() use ($client) { 19 | echo "Connected\n"; 20 | $client->disconnect(); 21 | }); 22 | 23 | $client->onDisconnect(function() { 24 | echo "Disconnected\n"; 25 | }); 26 | 27 | $client->connect(TEST_MQTT_HOST); 28 | $client->loopForever(); 29 | 30 | ?> 31 | --EXPECTF-- 32 | string(38) "The client is not currently connected." 33 | Connected 34 | Disconnected 35 | 36 | -------------------------------------------------------------------------------- /tests/Client/exitLoop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::exitLoop() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | exitLoop(); 12 | 13 | $client->onConnect(function() use ($client) { 14 | $client->exitLoop(); 15 | }); 16 | $client->connect(TEST_MQTT_HOST); 17 | $client->loopForever(); 18 | echo "Made it\n"; 19 | ?> 20 | --EXPECTF-- 21 | Made it 22 | -------------------------------------------------------------------------------- /tests/Client/getSocket.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::getSocket() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | getSocket()); 13 | 14 | $client->connect(TEST_MQTT_HOST); 15 | var_dump($client->getSocket() > 0); 16 | ?> 17 | --EXPECTF-- 18 | int(-1) 19 | bool(true) 20 | -------------------------------------------------------------------------------- /tests/Client/loop.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::loop() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | loop(); 13 | } catch (Mosquitto\Exception $e) { 14 | var_dump($e->getMessage()); 15 | } 16 | 17 | /* No params */ 18 | $client = new Mosquitto\Client; 19 | $client->connect(TEST_MQTT_HOST); 20 | 21 | for ($i = 0; $i < 3; $i++) { 22 | echo "Looping\n"; 23 | $client->loop(); 24 | } 25 | 26 | unset ($client); 27 | 28 | /* Zero param */ 29 | $client = new Mosquitto\Client; 30 | $client->connect(TEST_MQTT_HOST); 31 | 32 | for ($i = 0; $i < 3; $i++) { 33 | echo "Looping\n"; 34 | $client->loop(0); 35 | } 36 | 37 | unset ($client); 38 | 39 | $client = new Mosquitto\Client; 40 | $client->connect(TEST_MQTT_HOST); 41 | 42 | for ($i = 0; $i < 3; $i++) { 43 | echo "Looping\n"; 44 | $client->loop(100); 45 | } 46 | 47 | ?> 48 | --EXPECTF-- 49 | string(38) "The client is not currently connected." 50 | Looping 51 | Looping 52 | Looping 53 | Looping 54 | Looping 55 | Looping 56 | Looping 57 | Looping 58 | Looping 59 | -------------------------------------------------------------------------------- /tests/Client/loopForever.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::loopForever() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | loopForever(); 12 | } catch (Mosquitto\Exception $e) { 13 | var_dump($e->getMessage()); 14 | } 15 | 16 | /* No params */ 17 | $client = new Mosquitto\Client; 18 | $client->onConnect(function() use ($client) { 19 | echo "Exiting loop\n"; 20 | $client->exitLoop(); 21 | }); 22 | 23 | $client->connect(TEST_MQTT_HOST); 24 | $client->loopForever(); 25 | unset ($client); 26 | 27 | /* Zero param */ 28 | $client = new Mosquitto\Client; 29 | $client->onConnect(function() use ($client) { 30 | echo "Exiting loop\n"; 31 | $client->exitLoop(); 32 | }); 33 | 34 | $client->connect(TEST_MQTT_HOST); 35 | $client->loopForever(0); 36 | unset ($client); 37 | 38 | /* Proper param */ 39 | $client = new Mosquitto\Client; 40 | $client->onConnect(function() use ($client) { 41 | echo "Exiting loop\n"; 42 | $client->exitLoop(); 43 | }); 44 | 45 | $client->connect(TEST_MQTT_HOST); 46 | $client->loopForever(100); 47 | unset ($client); 48 | 49 | ?> 50 | --EXPECTF-- 51 | string(38) "The client is not currently connected." 52 | Exiting loop 53 | Exiting loop 54 | Exiting loop 55 | -------------------------------------------------------------------------------- /tests/Client/onConnect.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::onConnect() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | onConnect('foo'); 12 | } catch (TypeError $e) { 13 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 14 | } catch (Mosquitto\Exception $e) { 15 | printf("Caught TypeError with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 16 | } 17 | unset($client); 18 | 19 | $client = new Mosquitto\Client; 20 | $client->onConnect(function() { 21 | var_dump(func_get_args()); 22 | }); 23 | 24 | $client->connect(TEST_MQTT_HOST); 25 | 26 | for ($i = 0; $i < 2; $i++) { 27 | $client->loop(50); 28 | } 29 | ?> 30 | --EXPECTF-- 31 | %ACaught TypeError with code 0 and message: %s 32 | array(2) { 33 | [0]=> 34 | int(0) 35 | [1]=> 36 | string(20) "Connection Accepted." 37 | } 38 | -------------------------------------------------------------------------------- /tests/Client/onDisconnect.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::onDisconnect() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | onDisconnect('foo'); 12 | } catch (TypeError $e) { 13 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 14 | } catch (Mosquitto\Exception $e) { 15 | printf("Caught TypeError with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 16 | } 17 | unset($client); 18 | 19 | 20 | $client = new Mosquitto\Client; 21 | 22 | $client->onConnect(function() use ($client) { 23 | echo "Triggering disconnect\n"; 24 | $client->disconnect(); 25 | }); 26 | 27 | $client->onDisconnect(function() { 28 | echo "Disconnected\n"; 29 | }); 30 | 31 | $client->connect(TEST_MQTT_HOST); 32 | $client->loopForever(50); 33 | unset($client); 34 | 35 | /* onDisconnect called when Client is destroyed */ 36 | $client = new Mosquitto\Client; 37 | $loop = true; 38 | $client->onDisconnect(function() use (&$loop) { 39 | $loop = false; 40 | echo "Disconnected\n"; 41 | }); 42 | 43 | $client->connect(TEST_MQTT_HOST); 44 | 45 | for ($i = 0; $i < 10; $i++) { 46 | if (!$loop) break; 47 | $client->loop(); 48 | } 49 | 50 | ?> 51 | --EXPECTF-- 52 | %ACaught TypeError with code 0 and message: %s 53 | Triggering disconnect 54 | Disconnected 55 | Disconnected 56 | -------------------------------------------------------------------------------- /tests/Client/onLog.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::onLog() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | onLog('foo'); 16 | } catch (TypeError $e) { 17 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 18 | } catch (Mosquitto\Exception $e) { 19 | printf("Caught TypeError with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 20 | } 21 | 22 | $client = new Mosquitto\Client; 23 | $client->onLog('logger'); 24 | var_dump($client); 25 | 26 | $client->connect(TEST_MQTT_HOST); 27 | $client->loop(50); 28 | $client->loop(50); 29 | ?> 30 | --EXPECTF-- 31 | %ACaught TypeError with code 0 and message: %s 32 | object(Mosquitto\Client)#%d (%d) { 33 | } 34 | array(2) { 35 | [0]=> 36 | int(16) 37 | [1]=> 38 | string(%d) "Client %s sending CONNECT" 39 | } 40 | array(2) { 41 | [0]=> 42 | int(16) 43 | [1]=> 44 | string(%d) "Client %s received CONNACK%S" 45 | } 46 | array(2) { 47 | [0]=> 48 | int(16) 49 | [1]=> 50 | string(%d) "Client %s sending DISCONNECT" 51 | } 52 | -------------------------------------------------------------------------------- /tests/Client/onMessage.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::onMessage() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | onMessage('foo'); 12 | } catch (TypeError $e) { 13 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 14 | } catch (Mosquitto\Exception $e) { 15 | printf("Caught TypeError with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 16 | } 17 | unset($client); 18 | 19 | $client = new Mosquitto\Client; 20 | var_dump($client); 21 | 22 | $client->onMessage(function($m) { 23 | var_dump($m); 24 | }); 25 | 26 | $client->connect(TEST_MQTT_HOST); 27 | $client->subscribe('#', 1); 28 | 29 | $client2 = new Mosquitto\Client; 30 | $client2->connect(TEST_MQTT_HOST); 31 | $client2->publish('test', 'test', 1); 32 | 33 | for ($i = 0; $i < 30; $i++) { 34 | $client->loop(); 35 | $client2->loop(); 36 | } 37 | 38 | ?> 39 | --EXPECTF-- 40 | %ACaught TypeError with code 0 and message: %s 41 | object(Mosquitto\Client)#%d (%d) { 42 | } 43 | object(Mosquitto\Message)#%d (%d) { 44 | ["mid"]=> 45 | int(%d) 46 | ["topic"]=> 47 | string(4) "test" 48 | ["payload"]=> 49 | string(4) "test" 50 | ["qos"]=> 51 | int(1) 52 | ["retain"]=> 53 | bool(false) 54 | } 55 | -------------------------------------------------------------------------------- /tests/Client/onSubscribe.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::onSubscribe() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | onSubscribe('foo'); 12 | } catch (TypeError $e) { 13 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 14 | } catch (Mosquitto\Exception $e) { 15 | printf("Caught TypeError with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 16 | } 17 | unset($client); 18 | 19 | $client = new Mosquitto\Client; 20 | $client->onSubscribe(function() use ($client) { 21 | var_dump(func_get_args()); 22 | $client->disconnect(); 23 | }); 24 | 25 | $client->onConnect(function() use ($client) { 26 | $client->subscribe('#', 0); 27 | }); 28 | 29 | $client->connect(TEST_MQTT_HOST); 30 | $client->loopForever(); 31 | 32 | ?> 33 | --EXPECTF-- 34 | %ACaught TypeError with code 0 and message: %s 35 | array(3) { 36 | [0]=> 37 | int(1) 38 | [1]=> 39 | int(1) 40 | [2]=> 41 | int(0) 42 | } 43 | -------------------------------------------------------------------------------- /tests/Client/onUnsubscribe.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::onUnsubscribe() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | onUnsubscribe('foo'); 12 | } catch (TypeError $e) { 13 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 14 | } catch (Mosquitto\Exception $e) { 15 | printf("Caught TypeError with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 16 | } 17 | unset($client); 18 | 19 | $client = new Mosquitto\Client; 20 | 21 | $client->onUnsubscribe(function() use ($client) { 22 | $client->disconnect(); 23 | }); 24 | 25 | $client->onConnect(function() use ($client) { 26 | $client->subscribe('#', 0); 27 | }); 28 | 29 | $client->onSubscribe(function() use ($client) { 30 | var_dump(func_get_args()); 31 | $client->unsubscribe('#'); 32 | }); 33 | 34 | $client->connect(TEST_MQTT_HOST); 35 | $client->loopForever(); 36 | 37 | ?> 38 | --EXPECTF-- 39 | %ACaught TypeError with code 0 and message: %s 40 | array(3) { 41 | [0]=> 42 | int(1) 43 | [1]=> 44 | int(1) 45 | [2]=> 46 | int(0) 47 | } 48 | -------------------------------------------------------------------------------- /tests/Client/publish.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::publish() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | publish(); 13 | echo "Done\n"; 14 | } catch (Mosquitto\Exception $e) { 15 | writeException($e); 16 | } 17 | 18 | try { 19 | $client->publish(new stdClass); 20 | echo "Done\n"; 21 | } catch (Mosquitto\Exception $e) { 22 | writeException($e); 23 | } 24 | 25 | try { 26 | $client->publish(new stdClass, new stdClass); 27 | echo "Done\n"; 28 | } catch (Mosquitto\Exception $e) { 29 | writeException($e); 30 | } 31 | 32 | try { 33 | $client->publish('#'); 34 | echo "Done\n"; 35 | } catch (Mosquitto\Exception $e) { 36 | writeException($e); 37 | } 38 | 39 | try { 40 | $client->publish('topic'); 41 | echo "Done\n"; 42 | } catch (Mosquitto\Exception $e) { 43 | writeException($e); 44 | } 45 | 46 | try { 47 | $client->publish('topic', 'payload', new stdClass); 48 | echo "Done\n"; 49 | } catch (Mosquitto\Exception $e) { 50 | writeException($e); 51 | } 52 | 53 | try { 54 | $client->publish('topic', 'payload', 1, new stdClass); 55 | echo "Done\n"; 56 | } catch (Mosquitto\Exception $e) { 57 | writeException($e); 58 | } 59 | 60 | try { 61 | $client->publish('topic', 'payload', 1); 62 | echo "Done\n"; 63 | } catch (Mosquitto\Exception $e) { 64 | writeException($e); 65 | } 66 | 67 | try { 68 | $client->publish('topic', 'payload', 1, true); 69 | echo "Done\n"; 70 | } catch (Mosquitto\Exception $e) { 71 | writeException($e); 72 | } 73 | 74 | try { 75 | $client->publish('topic', 'payload', 1, false); 76 | echo "Done\n"; 77 | } catch (Mosquitto\Exception $e) { 78 | writeException($e); 79 | } 80 | 81 | 82 | try { 83 | $client->publish('topic', 'payload'); 84 | echo "Done\n"; 85 | } catch (Mosquitto\Exception $e) { 86 | writeException($e); 87 | } 88 | 89 | $client2 = new Mosquitto\Client(); 90 | $looping = true; 91 | 92 | $client2->onConnect(function() use ($client2) { 93 | $client2->subscribe('publish', 0); 94 | $client2->publish('publish', 'hello', 0); 95 | }); 96 | 97 | $client2->onMessage(function($m) use ($client2, &$looping) { 98 | var_dump($m); 99 | $client2->disconnect(); 100 | $looping = false; 101 | }); 102 | 103 | $client2->connect(TEST_MQTT_HOST); 104 | 105 | for ($i = 0; $i < 10; $i++) { 106 | if (!$looping) break; 107 | $client2->loop(50); 108 | } 109 | 110 | ?> 111 | --EXPECTF-- 112 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects at least 2 parameters, 0 given 113 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects at least 2 parameters, 1 given 114 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects parameter 1 to be string, object given 115 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects at least 2 parameters, 1 given 116 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects at least 2 parameters, 1 given 117 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects parameter 3 to be %s, object given 118 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::publish() expects parameter 4 to be boolean, object given 119 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 120 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 121 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 122 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 123 | object(Mosquitto\Message)#%d (5) { 124 | ["mid"]=> 125 | int(%d) 126 | ["topic"]=> 127 | string(7) "publish" 128 | ["payload"]=> 129 | string(5) "hello" 130 | ["qos"]=> 131 | int(0) 132 | ["retain"]=> 133 | bool(false) 134 | } 135 | -------------------------------------------------------------------------------- /tests/Client/setCredentials.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setCredentials() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setCredentials('foo', 'bar'); 12 | var_dump($client); 13 | 14 | $client->setCredentials(null, null); 15 | var_dump($client); 16 | 17 | $client->setCredentials('foo', null); 18 | var_dump($client); 19 | 20 | $client->setCredentials(null, 'foo'); 21 | var_dump($client); 22 | 23 | try { 24 | $client->setCredentials(new stdClass, 'foo'); 25 | var_dump($client); 26 | } catch (Exception $e) { 27 | var_dump($e->getMessage()); 28 | } 29 | 30 | try { 31 | $client->setCredentials('foo', new stdClass); 32 | var_dump($client); 33 | } catch (Exception $e) { 34 | var_dump($e->getMessage()); 35 | } 36 | 37 | ?> 38 | --EXPECTF-- 39 | object(Mosquitto\Client)#%d (0) { 40 | } 41 | object(Mosquitto\Client)#%d (0) { 42 | } 43 | object(Mosquitto\Client)#%d (0) { 44 | } 45 | object(Mosquitto\Client)#%d (0) { 46 | } 47 | string(81) "Mosquitto\Client::setCredentials() expects parameter 1 to be string, object given" 48 | string(81) "Mosquitto\Client::setCredentials() expects parameter 2 to be string, object given" 49 | -------------------------------------------------------------------------------- /tests/Client/setMaxInFlightMessages.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setMaxInFlightMessages() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setMaxInFlightMessages(10); 11 | var_dump($client); 12 | 13 | $client->setMaxInFlightMessages(0); 14 | var_dump($client); 15 | 16 | $client->setMaxInFlightMessages("100"); 17 | var_dump($client); 18 | 19 | try { 20 | $client->setMaxInFlightMessages("foo"); 21 | var_dump($client); 22 | } catch (Exception $e) { 23 | var_dump($e->getMessage()); 24 | } 25 | 26 | try { 27 | $client->setMaxInFlightMessages(new stdClass); 28 | var_dump($client); 29 | } catch (Exception $e) { 30 | var_dump($e->getMessage()); 31 | } 32 | ?> 33 | --EXPECTF-- 34 | object(Mosquitto\Client)#%d (0) { 35 | } 36 | object(Mosquitto\Client)#%d (0) { 37 | } 38 | object(Mosquitto\Client)#%d (0) { 39 | } 40 | string(%d) "Mosquitto\Client::setMaxInFlightMessages() expects parameter 1 to be %s, string given" 41 | string(%d) "Mosquitto\Client::setMaxInFlightMessages() expects parameter 1 to be %s, object given" 42 | -------------------------------------------------------------------------------- /tests/Client/setMessageRetry.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setMessageRetry() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setMessageRetry(10); 11 | var_dump($client); 12 | 13 | $client->setMessageRetry(0); 14 | var_dump($client); 15 | 16 | $client->setMessageRetry("100"); 17 | var_dump($client); 18 | 19 | try { 20 | $client->setMessageRetry("foo"); 21 | var_dump($client); 22 | } catch (Exception $e) { 23 | var_dump($e->getMessage()); 24 | } 25 | 26 | try { 27 | $client->setMessageRetry(new stdClass); 28 | var_dump($client); 29 | } catch (Exception $e) { 30 | var_dump($e->getMessage()); 31 | } 32 | ?> 33 | --EXPECTF-- 34 | object(Mosquitto\Client)#%d (0) { 35 | } 36 | object(Mosquitto\Client)#%d (0) { 37 | } 38 | object(Mosquitto\Client)#%d (0) { 39 | } 40 | string(%d) "Mosquitto\Client::setMessageRetry() expects parameter 1 to be %s, string given" 41 | string(%d) "Mosquitto\Client::setMessageRetry() expects parameter 1 to be %s, object given" 42 | -------------------------------------------------------------------------------- /tests/Client/setReconnectDelay.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setReconnectDelay() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setReconnectDelay(10); 11 | var_dump($client); 12 | 13 | $client->setReconnectDelay(0); 14 | var_dump($client); 15 | 16 | $client->setReconnectDelay("100"); 17 | var_dump($client); 18 | 19 | try { 20 | $client->setReconnectDelay("foo"); 21 | var_dump($client); 22 | } catch (Exception $e) { 23 | var_dump($e->getMessage()); 24 | } 25 | 26 | try { 27 | $client->setReconnectDelay(new stdClass); 28 | var_dump($client); 29 | } catch (Exception $e) { 30 | var_dump($e->getMessage()); 31 | } 32 | ?> 33 | --EXPECTF-- 34 | object(Mosquitto\Client)#%d (0) { 35 | } 36 | object(Mosquitto\Client)#%d (0) { 37 | } 38 | object(Mosquitto\Client)#%d (0) { 39 | } 40 | string(%d) "Mosquitto\Client::setReconnectDelay() expects parameter 1 to be %s, string given" 41 | string(%d) "Mosquitto\Client::setReconnectDelay() expects parameter 1 to be %s, object given" 42 | -------------------------------------------------------------------------------- /tests/Client/setTlsCertificates.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setTlsCertificates() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setTlsCertificates(); 12 | } catch (Exception $e) { 13 | writeException($e); 14 | } 15 | 16 | try { 17 | $client->setTlsCertificates('/does/not/exist'); 18 | } catch (Exception $e) { 19 | writeException($e); 20 | } 21 | 22 | try { 23 | $client->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt', '/does/not/exist'); 24 | } catch (Exception $e) { 25 | writeException($e); 26 | } 27 | 28 | try { 29 | $client->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt', __FILE__); 30 | } catch (Exception $e) { 31 | writeException($e); 32 | } 33 | 34 | try { 35 | $client->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt', CERTIFICATE_DIR . 'client.crt'); 36 | } catch (Exception $e) { 37 | writeException($e); 38 | } 39 | 40 | try { 41 | $client->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt', CERTIFICATE_DIR . 'client.crt', CERTIFICATE_DIR . 'client.key'); 42 | } catch (Exception $e) { 43 | writeException($e); 44 | } 45 | 46 | $client->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt'); 47 | //$client->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt', CERTIFICATE_DIR . 'client.crt', CERTIFICATE_DIR . 'client-enc.key', 'Mosquitto-PHP'); 48 | 49 | $client->onConnect(function() use ($client) { 50 | echo "Connected successfully\n"; 51 | $client->disconnect(); 52 | }); 53 | 54 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_TLS_PORT); 55 | $client->loopForever(); 56 | unset($client); 57 | 58 | $client2 = new Mosquitto\Client; 59 | $client2->onConnect(function() use ($client2) { 60 | echo "Connected successfully\n"; 61 | $client2->disconnect(); 62 | }); 63 | 64 | $client2->setTlsCertificates(CERTIFICATE_DIR . 'ca.crt', CERTIFICATE_DIR . 'client.crt', CERTIFICATE_DIR . 'client-enc.key', 'Mosquitto-PHP'); 65 | 66 | $client2->connect(TEST_MQTT_HOST, TEST_MQTT_TLS_CERT_PORT); 67 | $client2->loopForever(); 68 | 69 | ?> 70 | --EXPECTF-- 71 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsCertificates() expects at least 1 parameter, 0 given 72 | Caught Mosquitto\Exception with code 0 and message: Invalid function arguments provided. 73 | Caught Mosquitto\Exception with code 0 and message: Invalid function arguments provided. 74 | Caught Mosquitto\Exception with code 0 and message: Invalid function arguments provided. 75 | Caught Mosquitto\Exception with code 0 and message: Invalid function arguments provided. 76 | Connected successfully 77 | Connected successfully 78 | 79 | -------------------------------------------------------------------------------- /tests/Client/setTlsInsecure.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setTlsInsecure() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setTlsInsecure(true); 11 | var_dump($client); 12 | 13 | $client->setTlsInsecure(false); 14 | var_dump($client); 15 | 16 | try { 17 | $client->setTlsInsecure(new stdClass); 18 | var_dump($client); 19 | } catch (Mosquitto\Exception $e) { 20 | var_dump($e->getMessage()); 21 | } 22 | 23 | ?> 24 | --EXPECTF-- 25 | object(Mosquitto\Client)#%d (0) { 26 | } 27 | object(Mosquitto\Client)#%d (0) { 28 | } 29 | string(82) "Mosquitto\Client::setTlsInsecure() expects parameter 1 to be boolean, object given" 30 | -------------------------------------------------------------------------------- /tests/Client/setTlsOptions.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setTlsOptions() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setTlsOptions(); 12 | } catch (Exception $e) { 13 | writeException($e); 14 | } 15 | 16 | try { 17 | $client->setTlsOptions(new stdClass); 18 | } catch (Exception $e) { 19 | writeException($e); 20 | } 21 | 22 | try { 23 | $client->setTlsOptions(Mosquitto\Client::SSL_VERIFY_PEER, new stdClass); 24 | } catch (Exception $e) { 25 | writeException($e); 26 | } 27 | 28 | try { 29 | $client->setTlsOptions(Mosquitto\Client::SSL_VERIFY_PEER, 'tlsv1.2', new stdClass); 30 | } catch (Exception $e) { 31 | writeException($e); 32 | } 33 | 34 | $client->setTlsOptions(Mosquitto\Client::SSL_VERIFY_PEER); 35 | $client->setTlsOptions(Mosquitto\Client::SSL_VERIFY_PEER, 'tlsv1.2'); 36 | $client->setTlsOptions(Mosquitto\Client::SSL_VERIFY_PEER, 'tlsv1.2', 'DEFAULT'); 37 | 38 | ?> 39 | --EXPECTF-- 40 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsOptions() expects at least 1 parameter, 0 given 41 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsOptions() expects parameter 1 to be %s, object given 42 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsOptions() expects parameter 2 to be string, object given 43 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsOptions() expects parameter 3 to be string, object given 44 | 45 | -------------------------------------------------------------------------------- /tests/Client/setTlsPSK.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setTlsPSK() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setTlsPSK(); 13 | } catch (Exception $e) { 14 | writeException($e); 15 | } 16 | 17 | try { 18 | $client->setTlsPsk('1234567890abcdef'); 19 | } catch (Exception $e) { 20 | writeException($e); 21 | } 22 | 23 | try { 24 | $client->setTlsPsk("This is invalid hex", "Username"); 25 | } catch (Exception $e) { 26 | writeException($e); 27 | } 28 | 29 | /* This actually doesn't fail */ 30 | try { 31 | $client->setTlsPsk('1234567890abcdef', 'username', "This is not a cipher string"); 32 | } catch (Exception $e) { 33 | writeException($e); 34 | } 35 | 36 | $client->setTlsPsk('1234567890abcdef', 'testuser'); 37 | var_dump($client); 38 | 39 | $client->setTlsPsk('1234567890abcdef', 'testuser', 'DEFAULT'); 40 | var_dump($client); 41 | 42 | $client->onConnect(function() use ($client) { 43 | echo "Connected successfully\n"; 44 | $client->disconnect(); 45 | }); 46 | 47 | $client->connect(TEST_MQTT_HOST, TEST_MQTT_TLS_PSK_PORT); 48 | $client->loopForever(); 49 | 50 | ?> 51 | --EXPECTF-- 52 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsPSK() expects at least 2 parameters, 0 given 53 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setTlsPSK() expects at least 2 parameters, 1 given 54 | Caught Mosquitto\Exception with code 0 and message: Invalid function arguments provided. 55 | object(Mosquitto\Client)#%d (0) { 56 | } 57 | object(Mosquitto\Client)#%d (0) { 58 | } 59 | Connected successfully 60 | 61 | -------------------------------------------------------------------------------- /tests/Client/setWill.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::setWill() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | setWill(); 13 | echo "Done\n"; 14 | } catch (Mosquitto\Exception $e) { 15 | writeException($e); 16 | } 17 | 18 | try { 19 | $client->setWill(new stdClass); 20 | echo "Done\n"; 21 | } catch (Mosquitto\Exception $e) { 22 | writeException($e); 23 | } 24 | 25 | try { 26 | $client->setWill(new stdClass, new stdClass); 27 | echo "Done\n"; 28 | } catch (Mosquitto\Exception $e) { 29 | writeException($e); 30 | } 31 | 32 | try { 33 | $client->setWill('#'); 34 | echo "Done\n"; 35 | } catch (Mosquitto\Exception $e) { 36 | writeException($e); 37 | } 38 | 39 | try { 40 | $client->setWill('topic'); 41 | echo "Done\n"; 42 | } catch (Mosquitto\Exception $e) { 43 | writeException($e); 44 | } 45 | 46 | try { 47 | $client->setWill('topic', 'payload'); 48 | echo "Done\n"; 49 | } catch (Mosquitto\Exception $e) { 50 | writeException($e); 51 | } 52 | 53 | try { 54 | $client->setWill('topic', 'payload', new stdClass); 55 | echo "Done\n"; 56 | } catch (Mosquitto\Exception $e) { 57 | writeException($e); 58 | } 59 | 60 | try { 61 | $client->setWill('topic', 'payload', 1); 62 | echo "Done\n"; 63 | } catch (Mosquitto\Exception $e) { 64 | writeException($e); 65 | } 66 | 67 | try { 68 | $client->setWill('topic', 'payload', 1, new stdClass); 69 | echo "Done\n"; 70 | } catch (Mosquitto\Exception $e) { 71 | writeException($e); 72 | } 73 | 74 | try { 75 | $client->setWill('topic', 'payload', 1, false); 76 | echo "Done\n"; 77 | } catch (Mosquitto\Exception $e) { 78 | writeException($e); 79 | } 80 | 81 | try { 82 | $client->setWill('topic', 'payload', 1, true); 83 | echo "Done\n"; 84 | } catch (Mosquitto\Exception $e) { 85 | writeException($e); 86 | } 87 | 88 | ?> 89 | --EXPECTF-- 90 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 0 given 91 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 1 given 92 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 2 given 93 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 1 given 94 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 1 given 95 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 2 given 96 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 3 given 97 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects exactly 4 parameters, 3 given 98 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::setWill() expects parameter 4 to be boolean, object given 99 | Done 100 | Done 101 | -------------------------------------------------------------------------------- /tests/Client/subscribe.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::subscribe() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | subscribe(); 13 | } catch (Exception $e) { 14 | writeException($e); 15 | } 16 | 17 | /* Null param */ 18 | try { 19 | $client = new Mosquitto\Client; 20 | $client->subscribe(null); 21 | } catch (Exception $e) { 22 | writeException($e); 23 | } 24 | 25 | /* Only one param */ 26 | try { 27 | $client = new Mosquitto\Client; 28 | $client->subscribe('#'); 29 | } catch (Exception $e) { 30 | writeException($e); 31 | } 32 | 33 | /* Not connected */ 34 | try { 35 | $client = new Mosquitto\Client; 36 | $client->subscribe('#', 0); 37 | } catch (Exception $e) { 38 | writeException($e); 39 | } 40 | 41 | /* Daft params */ 42 | try { 43 | $client = new Mosquitto\Client; 44 | $client->subscribe(new stdClass, 0); 45 | } catch (Exception $e) { 46 | writeException($e); 47 | } 48 | 49 | try { 50 | $client = new Mosquitto\Client; 51 | $client->subscribe('#', new stdClass); 52 | } catch (Exception $e) { 53 | writeException($e); 54 | } 55 | 56 | $client = new Mosquitto\Client; 57 | 58 | $client->onConnect(function() use ($client) { 59 | $client->subscribe('#', 0); 60 | }); 61 | 62 | $client->onSubscribe(function() use ($client) { 63 | var_dump(func_get_args()); 64 | $client->disconnect(); 65 | }); 66 | 67 | $client->connect(TEST_MQTT_HOST); 68 | $client->loopForever(); 69 | ?> 70 | --EXPECTF-- 71 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::subscribe() expects exactly 2 parameters, 0 given 72 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::subscribe() expects exactly 2 parameters, 1 given 73 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::subscribe() expects exactly 2 parameters, 1 given 74 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 75 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::subscribe() expects parameter 1 to be string, object given 76 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::subscribe() expects parameter 2 to be %s, object given 77 | array(3) { 78 | [0]=> 79 | int(%d) 80 | [1]=> 81 | int(1) 82 | [2]=> 83 | int(0) 84 | } 85 | -------------------------------------------------------------------------------- /tests/Client/unsubscribe.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Client::unsubscribe() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | unsubscribe(); 13 | } catch (Exception $e) { 14 | writeException($e); 15 | } 16 | 17 | /* Null param */ 18 | try { 19 | $client = new Mosquitto\Client; 20 | $client->unsubscribe(null); 21 | } catch (Exception $e) { 22 | writeException($e); 23 | } 24 | 25 | /* One param */ 26 | try { 27 | $client = new Mosquitto\Client; 28 | $client->unsubscribe('#'); 29 | } catch (Exception $e) { 30 | writeException($e); 31 | } 32 | 33 | /* Daft params */ 34 | try { 35 | $client = new Mosquitto\Client; 36 | $client->unsubscribe(new stdClass); 37 | } catch (Exception $e) { 38 | writeException($e); 39 | } 40 | 41 | $client = new Mosquitto\Client; 42 | 43 | $client->onConnect(function() use ($client) { 44 | $client->subscribe('#', 0); 45 | }); 46 | 47 | $client->onSubscribe(function() use ($client) { 48 | $client->unsubscribe('#'); 49 | }); 50 | 51 | $client->onUnsubscribe(function() use ($client) { 52 | var_dump(func_get_args()); 53 | $client->disconnect(); 54 | }); 55 | 56 | $client->connect(TEST_MQTT_HOST); 57 | $client->loopForever(); 58 | ?> 59 | --EXPECTF-- 60 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::unsubscribe() expects exactly 1 parameter, 0 given 61 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 62 | Caught Mosquitto\Exception with code 0 and message: The client is not currently connected. 63 | Caught Mosquitto\Exception with code 0 and message: Mosquitto\Client::unsubscribe() expects parameter 1 to be string, object given 64 | array(1) { 65 | [0]=> 66 | int(2) 67 | } 68 | -------------------------------------------------------------------------------- /tests/Message/__construct.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Message::__construct() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | topic = "Hello"; 13 | var_dump($message); 14 | 15 | $message->payload = "Hello world"; 16 | var_dump($message); 17 | 18 | $message->topic = false; 19 | var_dump($message); 20 | 21 | $message->topic = new stdClass; 22 | var_dump($message); 23 | 24 | $message->payload = false; 25 | var_dump($message); 26 | 27 | $message->payload = new stdClass; 28 | var_dump($message); 29 | ?> 30 | --EXPECTF-- 31 | object(Mosquitto\Message)#1 (5) { 32 | ["mid"]=> 33 | int(%d) 34 | ["topic"]=> 35 | NULL 36 | ["payload"]=> 37 | string(0) "" 38 | ["qos"]=> 39 | int(0) 40 | ["retain"]=> 41 | bool(false) 42 | } 43 | object(Mosquitto\Message)#1 (5) { 44 | ["mid"]=> 45 | int(%d) 46 | ["topic"]=> 47 | string(5) "Hello" 48 | ["payload"]=> 49 | string(0) "" 50 | ["qos"]=> 51 | int(0) 52 | ["retain"]=> 53 | bool(false) 54 | } 55 | object(Mosquitto\Message)#1 (5) { 56 | ["mid"]=> 57 | int(%d) 58 | ["topic"]=> 59 | string(5) "Hello" 60 | ["payload"]=> 61 | string(11) "Hello world" 62 | ["qos"]=> 63 | int(0) 64 | ["retain"]=> 65 | bool(false) 66 | } 67 | object(Mosquitto\Message)#1 (5) { 68 | ["mid"]=> 69 | int(%d) 70 | ["topic"]=> 71 | string(0) "" 72 | ["payload"]=> 73 | string(11) "Hello world" 74 | ["qos"]=> 75 | int(0) 76 | ["retain"]=> 77 | bool(false) 78 | } 79 | Caught error 4096 (Object of class stdClass could not be converted to string) in %s on line 16 80 | %Aobject(Mosquitto\Message)#1 (5) { 81 | ["mid"]=> 82 | int(%d) 83 | ["topic"]=> 84 | string(6) "Object" 85 | ["payload"]=> 86 | string(11) "Hello world" 87 | ["qos"]=> 88 | int(0) 89 | ["retain"]=> 90 | bool(false) 91 | } 92 | object(Mosquitto\Message)#1 (5) { 93 | ["mid"]=> 94 | int(%d) 95 | ["topic"]=> 96 | string(6) "Object" 97 | ["payload"]=> 98 | string(0) "" 99 | ["qos"]=> 100 | int(0) 101 | ["retain"]=> 102 | bool(false) 103 | } 104 | Caught error 4096 (Object of class stdClass could not be converted to string) in %s on line 22 105 | %Aobject(Mosquitto\Message)#1 (5) { 106 | ["mid"]=> 107 | int(%d) 108 | ["topic"]=> 109 | string(6) "Object" 110 | ["payload"]=> 111 | string(6) "Object" 112 | ["qos"]=> 113 | int(0) 114 | ["retain"]=> 115 | bool(false) 116 | } 117 | -------------------------------------------------------------------------------- /tests/Message/tokeniseTopic.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Message::tokeniseTopic() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | getMessage()); 20 | } 21 | ?> 22 | --EXPECTF-- 23 | array(2) { 24 | [0]=> 25 | string(3) "foo" 26 | [1]=> 27 | string(3) "bar" 28 | } 29 | array(3) { 30 | [0]=> 31 | NULL 32 | [1]=> 33 | string(3) "foo" 34 | [2]=> 35 | string(3) "bar" 36 | } 37 | array(1) { 38 | [0]=> 39 | string(1) "#" 40 | } 41 | array(3) { 42 | [0]=> 43 | string(3) "foo" 44 | [1]=> 45 | string(1) "+" 46 | [2]=> 47 | string(3) "bar" 48 | } 49 | array(1) { 50 | [0]=> 51 | NULL 52 | } 53 | string(%d) "%s expects parameter 1 to be string, object given" 54 | -------------------------------------------------------------------------------- /tests/Message/topicMatchesSub.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Mosquitto\Message::topicMatchesSub() 3 | --SKIPIF-- 4 | if (!extension_loaded('mosquitto')) die('skip - Mosquitto extension not available'); 5 | --FILE-- 6 | getMessage()); 21 | } 22 | 23 | try { 24 | var_dump(Message::topicMatchesSub("foo", new stdClass)); 25 | } catch (Exception $e) { 26 | var_dump($e->getMessage()); 27 | } 28 | ?> 29 | --EXPECTF-- 30 | bool(true) 31 | bool(true) 32 | bool(false) 33 | bool(false) 34 | bool(false) 35 | bool(false) 36 | string(83) "Mosquitto\Message::topicMatchesSub() expects parameter 1 to be string, object given" 37 | string(83) "Mosquitto\Message::topicMatchesSub() expects parameter 2 to be string, object given" 38 | -------------------------------------------------------------------------------- /tests/makeTestCerts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | dest_dir=certs 4 | 5 | if [ ! -d "$dest_dir" ]; then 6 | mkdir "$dest_dir" 7 | fi 8 | 9 | if [ ! -d "$dest_dir" ]; then 10 | echo "Unable to create directory: $dest_dir" >&2 11 | exit 3 12 | fi 13 | 14 | cd "$dest_dir" 15 | 16 | echo "Making CA key and certificate" 17 | openssl req \ 18 | -new \ 19 | -x509 \ 20 | -days 3650 \ 21 | -extensions v3_ca \ 22 | -subj "/C=GB/ST=Scotland/L=Glasgow/O=Mosquitto-PHP/CN=Mosquitto-PHP Test CA" \ 23 | -keyout ca.key \ 24 | -out ca.crt \ 25 | -nodes 26 | 27 | echo "Making server key" 28 | 29 | openssl genrsa \ 30 | -out server.key \ 31 | 2048 32 | 33 | echo "Making server signing request" 34 | openssl req \ 35 | -subj "/C=GB/ST=Scotland/L=Glasgow/O=Mosquitto-PHP/CN=localhost" \ 36 | -out server.csr \ 37 | -key server.key \ 38 | -new 39 | 40 | echo "Signing server certificate" 41 | openssl x509 \ 42 | -req \ 43 | -in server.csr \ 44 | -CA ca.crt \ 45 | -CAkey ca.key \ 46 | -CAcreateserial \ 47 | -out server.crt \ 48 | -days 3650 49 | 50 | echo "Making encrypted client key" 51 | openssl genrsa \ 52 | -out client-enc.key \ 53 | -des3 \ 54 | -passout pass:Mosquitto-PHP \ 55 | 2048 56 | 57 | echo "Making clear-text client key" 58 | openssl rsa \ 59 | -in client-enc.key \ 60 | -passin pass:Mosquitto-PHP \ 61 | -out client.key 62 | 63 | echo "Making client signing request" 64 | openssl req \ 65 | -subj "/C=GB/ST=Scotland/L=Glasgow/O=Mosquitto-PHP/CN=Mosquitto-PHP Client" \ 66 | -out client.csr \ 67 | -key client.key \ 68 | -new 69 | 70 | echo "Signing client certificate" 71 | openssl x509 \ 72 | -req \ 73 | -in client.csr \ 74 | -CA ca.crt \ 75 | -CAkey ca.key \ 76 | -CAcreateserial \ 77 | -out client.crt \ 78 | -days 3650 79 | 80 | echo "Created all certificates. Now run mosquitto with the following command line, from the current directory:" 81 | echo "mosquitto -c mosquitto.conf -d" 82 | -------------------------------------------------------------------------------- /tests/mosquitto.conf: -------------------------------------------------------------------------------- 1 | bind_address 0.0.0.0 2 | port 1883 3 | 4 | listener 8883 5 | cafile certs/ca.crt 6 | certfile certs/server.crt 7 | keyfile certs/server.key 8 | 9 | listener 8884 10 | cafile certs/ca.crt 11 | certfile certs/server.crt 12 | keyfile certs/server.key 13 | tls_version tlsv1.2 14 | require_certificate true 15 | use_identity_as_username true 16 | 17 | listener 8885 18 | psk_hint Test PSK server 19 | psk_file psk.db 20 | -------------------------------------------------------------------------------- /tests/psk.db: -------------------------------------------------------------------------------- 1 | testuser:1234567890abcdef 2 | -------------------------------------------------------------------------------- /tests/setup.php: -------------------------------------------------------------------------------- 1 | 'localhost', 12 | 'TEST_MQTT_PORT' => 1883, 13 | 'TEST_MQTT_TLS_PORT' => 8883, 14 | 'TEST_MQTT_TLS_CERT_PORT' => 8884, 15 | 'TEST_MQTT_TLS_PSK_PORT' => 8885, 16 | ); 17 | 18 | foreach ($defaults as $index => $default) { 19 | if (getenv($index)) { 20 | define($index, getenv($index)); 21 | } else { 22 | define($index, $default); 23 | } 24 | } 25 | 26 | function errorHandler($errno, $errstr, $errfile, $errline) { 27 | printf("Caught error %d (%s) in %s on line %d\n", $errno, $errstr, $errfile, $errline); 28 | } 29 | set_error_handler('errorHandler'); 30 | 31 | function writeException(Exception $e) { 32 | printf("Caught %s with code %d and message: %s\n", get_class($e), $e->getCode(), $e->getMessage()); 33 | } 34 | -------------------------------------------------------------------------------- /travis/compile-ext.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | set -e 3 | $HOME/bin/phpize 4 | ./configure --with-php-config=$HOME/bin/php-config 5 | make -j2 --quiet 6 | make install 7 | echo "extension=mosquitto.so" > $HOME/php.d/mosquitto.ini 8 | -------------------------------------------------------------------------------- /travis/compile-mosquitto.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wget http://mosquitto.org/files/source/mosquitto-1.4.tar.gz 4 | tar xvzf mosquitto-1.4.tar.gz 5 | cd mosquitto-1.4 6 | make 7 | sudo make install 8 | 9 | -------------------------------------------------------------------------------- /travis/compile-php.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | set -e 3 | mkdir -p $HOME/php 4 | git clone https://github.com/php/php-src $HOME/php/src 5 | cd $HOME/php/src 6 | git checkout PHP-7.0 7 | ./buildconf --force 8 | ./configure \ 9 | --prefix=$HOME \ 10 | --disable-all \ 11 | --enable-debug \ 12 | --with-config-file-path=$HOME \ 13 | --with-config-file-scan-dir=$HOME/php.d \ 14 | --enable-json \ 15 | --enable-phar \ 16 | --with-openssl 17 | 18 | make -j2 --quiet install 19 | cp php.ini-development $HOME 20 | mkdir $HOME/php.d 21 | 22 | -------------------------------------------------------------------------------- /travis/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | cd $TRAVIS_BUILD_DIR 6 | 7 | cd tests 8 | ./makeTestCerts.sh 9 | mosquitto -c mosquitto.conf -d 10 | cd .. 11 | 12 | USE_ZEND_ALLOC=1 REPORT_EXIT_STATUS=1 TEST_PHP_ARGS="-q --show-diff" make test 13 | --------------------------------------------------------------------------------