├── .gitignore ├── .travis.yml ├── CREDITS ├── LICENSE ├── README ├── TODO ├── config.m4 ├── config.w32 ├── doc ├── classes.php └── functions.php ├── examples ├── oop.php └── procedural.php ├── package.xml ├── php_stomp.c ├── php_stomp.h ├── stomp.c ├── stomp.h └── tests ├── 001-stomp.phpt ├── 002-version.phpt ├── 003-connect ├── 001.phpt ├── 002.phpt └── 003.phpt ├── 004-getSessionId └── 001.phpt ├── 005-close ├── 001.phpt └── 002.phpt ├── 006-send ├── 001.phpt ├── 002.phpt └── 003.phpt ├── 007-subscribe └── 001.phpt ├── 008-unsubscribe └── 001.phpt ├── 009-readFrame ├── 001.phpt ├── 002.phpt ├── 003.phpt ├── 004.phpt ├── 005.phpt └── 006.phpt ├── 010-timeout ├── 001.phpt └── 002.phpt ├── 011-commit └── 001.phpt ├── bug_16930.phpt ├── bug_16936.phpt ├── config.inc └── skipif.inc /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | .libs/ 3 | Makefile 4 | Makefile.fragments 5 | Makefile.global 6 | Makefile.objects 7 | acinclude.m4 8 | aclocal.m4 9 | autom4te.cache/ 10 | build/ 11 | config.guess 12 | config.h 13 | config.h.in 14 | config.log 15 | config.nice 16 | config.status 17 | config.sub 18 | configure 19 | configure.in 20 | install-sh 21 | libtool 22 | ltmain.sh 23 | missing 24 | mkinstalldirs 25 | modules/ 26 | php_stomp.lo 27 | run-tests.php 28 | stomp.la 29 | stomp.lo 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | dist: xenial 4 | language: c 5 | os: linux 6 | addons: 7 | apt: 8 | packages: 9 | - rabbitmq-server 10 | 11 | services: 12 | - rabbitmq 13 | 14 | php: 15 | - 7.0 16 | - 7.1 17 | - 7.2 18 | - 7.3 19 | - 7.4 20 | - 8.0 21 | 22 | env: 23 | - STOMP_PROVIDER=rabbitmq 24 | 25 | before_script: 26 | - sh -c " if [ '$STOMP_PROVIDER' = 'rabbitmq' ]; then 27 | sudo service rabbitmq-server start; 28 | sudo rabbitmq-plugins enable rabbitmq_stomp; 29 | sudo rabbitmq-plugins list; 30 | fi" 31 | - phpize 32 | 33 | script: ./configure && make && NO_INTERACTION=1 REPORT_EXIT_STATUS=1 make test TEST_PHP_ARGS='--show-diff' 34 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | stomp 2 | Pierrick Charron 3 | Xinchen Hui 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2014 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | stomp 2 | ===== 3 | 4 | This extension allows php applications to communicate with any Stomp compliant Message Broker(s) through easy object 5 | oriented and procedural interfaces. 6 | 7 | This extension currently implements STOMP 1.0 protocol: https://stomp.github.io/stomp-specification-1.0.html 8 | 9 | [The stomp extension at the PECL Repository website](http://pecl.php.net/package/stomp) 10 | 11 | Documentation 12 | ============= 13 | 14 | For documentation of the functions that this extension provides can be found here: http://www.php.net/stomp 15 | 16 | Contribute 17 | ========== 18 | 19 | Your contributions and bugreports are highly appreciated. To contribute, fork and create a pull request. To report a 20 | bug use the [PHP Bug Tracking System](https://bugs.php.net/) 21 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Add the ability to connect to a network of brokers 2 | (Cf: http://activemq.apache.org/networks-of-brokers.html) 3 | - Add Stomp 1.1 full support 4 | - Verify if we need to call recv with MSG_PEEK for the newline char 5 | or if the current implementation is enough (cf bug #59217) 6 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension stomp 2 | 3 | PHP_ARG_ENABLE(stomp, whether to enable stomp support, 4 | [ --enable-stomp Enable stomp support]) 5 | 6 | PHP_ARG_WITH(openssl-dir,OpenSSL dir for stomp, 7 | [ --with-openssl-dir[=DIR] stomp: openssl install prefix], no, no) 8 | 9 | if test "$PHP_STOMP" != "no"; then 10 | PHP_NEW_EXTENSION(stomp, stomp.c php_stomp.c, $ext_shared) 11 | 12 | test -z "$PHP_OPENSSL" && PHP_OPENSSL=no 13 | 14 | if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then 15 | PHP_SETUP_OPENSSL(STOMP_SHARED_LIBADD, 16 | [ 17 | AC_DEFINE(HAVE_STOMP_SSL,1,[ ]) 18 | ], [ 19 | AC_MSG_ERROR([OpenSSL libraries not found. 20 | 21 | Check the path given to --with-openssl-dir and output in config.log) 22 | ]) 23 | ]) 24 | 25 | PHP_SUBST(STOMP_SHARED_LIBADD) 26 | fi 27 | fi 28 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | ARG_ENABLE("stomp", "enable stomp support", "no"); 4 | 5 | if (PHP_STOMP != "no") { 6 | if (CHECK_LIB("ssleay32.lib", "stomp", PHP_STOMP) && CHECK_LIB("libeay32.lib", "stomp", PHP_STOMP)) { 7 | ADD_FLAG("CFLAGS_STOMP", "/DHAVE_STOMP_SSL=1"); 8 | } else if (CHECK_LIB("libssl.lib", "stomp", PHP_STOMP) && CHECK_LIB("libcrypto.lib", "stomp", PHP_STOMP)) { 9 | ADD_FLAG("CFLAGS_STOMP", "/DHAVE_STOMP_SSL=1"); 10 | } 11 | 12 | EXTENSION("stomp", "stomp.c php_stomp.c"); 13 | } 14 | -------------------------------------------------------------------------------- /doc/classes.php: -------------------------------------------------------------------------------- 1 | send($queue, $msg); 10 | 11 | $stomp->subscribe($queue); 12 | $frame = $stomp->readFrame(); 13 | if ($frame->body === $msg) { 14 | echo "Worked\n"; 15 | $stomp->ack($frame, array('receipt' => 'message-12345')); 16 | } else { 17 | echo "Failed\n"; 18 | } 19 | 20 | $stomp->disconnect(); 21 | } catch(StompException $e) { 22 | echo $e->getMessage(); 23 | } 24 | -------------------------------------------------------------------------------- /examples/procedural.php: -------------------------------------------------------------------------------- 1 | 't1')); 11 | stomp_commit($stomp, 't1'); 12 | 13 | stomp_subscribe($stomp, $queue); 14 | $frame = stomp_read_frame($stomp); 15 | if ($frame['body'] === $msg) { 16 | echo "Worked\n"; 17 | stomp_ack($stomp, $frame['headers']['message-id']); 18 | } else { 19 | echo "Failed\n"; 20 | } 21 | 22 | stomp_close($stomp); 23 | } 24 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | stomp 4 | pecl.php.net 5 | Stomp client extension 6 | 7 | This extension allows php applications to communicate with any Stomp compliant Message Brokers through easy object oriented and procedural interfaces. 8 | 9 | 10 | Pierrick Charron 11 | pierrick 12 | pierrick@php.net 13 | yes 14 | 15 | 16 | Gennady Feldman 17 | gena01 18 | gena01@php.net 19 | yes 20 | 21 | 22 | Xinchen Hui 23 | laruence 24 | laruence@php.net 25 | yes 26 | 27 | 28 | Remi Collet 29 | remi 30 | remi@php.net 31 | yes 32 | 33 | 2022-05-31 34 | 2.0.4dev2.0.1 35 | stablestable 36 | PHP License 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 | 87 | 7.0.0 88 | 89 | 90 | 1.4.0 91 | 92 | 93 | 94 | 95 | openssl 96 | 97 | 98 | 99 | 100 | stomp 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 2022-05-31 109 | 2.0.32.0.1 110 | stablestable 111 | PHP License 112 | 113 | - Fix compatibility with PHP 8 114 | 115 | 116 | 117 | 2018-06-29 118 | 2.0.22.0.1 119 | stablestable 120 | PHP License 121 | 122 | - Fix compatibility with PHP 7.3 123 | - Fix -Wformat issues 124 | 125 | 126 | 127 | 2.0.12.0.1 128 | stablestable 129 | PHP License 130 | 2017-05-04 131 | 132 | - PHP7.1 Support 133 | 134 | 135 | 136 | 2.0.02.0.0 137 | betabeta 138 | PHP License 139 | 2016-05-24 140 | 141 | - PHP7 Support 142 | 143 | 144 | 145 | 1.0.91.0.9 146 | stablestable 147 | PHP License 148 | 2016-01-04 149 | 150 | - Adding .travis.yml to enable Travis CI test runner. (Gennady) 151 | - Adding a basic README file. (Gennady) 152 | - Check stomp connection status after _stomp_recv. (Gennady) 153 | - Commenting out activemq.prefetchSize hardcoded header for SUBSCRIBE frame. (Gennady) 154 | - Make login and passcode headers optional (if no creds provided). (Gennady) 155 | - Fixing type cast warning, introduced a new int variable. (Gennady) 156 | - Fixing stomp_writable() check so we can catch when connect failed. (Gennady) 157 | 158 | 159 | 160 | 1.0.81.0.8 161 | stablestable 162 | PHP License 163 | 2015-05-18 164 | 165 | - Fix perm on source files. (Remi) 166 | - Fixing PHP_STOMP_VERSION constant, per Remi's request. (Gennady) 167 | 168 | 169 | 170 | 1.0.71.0.7 171 | stablestable 172 | PHP License 173 | 2015-05-15 174 | 175 | - add LICENSE file as documentation (Remi) 176 | - Fixed Windows compilation regression due to new TCP_NODELAY code. (Gennady Feldman) 177 | - Fixed bug where error checking was missing after stomp_send(). (Gennady Feldman) 178 | 179 | 180 | 181 | 1.0.61.0.6 182 | stablestable 183 | PHP License 184 | 2014-12-07 185 | 186 | - Add two new ini options stomp.default_username and stomp.default_passowrd (Pierrick) 187 | - General performance improvements (Pierrick) 188 | - Fix stomp_read_frame when buffered (Pierrick) 189 | - Fixed bug #59217 (Connections to RabbitMQ via CLI). (Pierrick). 190 | - Fixed bug #59970 (acking a message makes rabbitmq disconnect the server). (Pierrick) 191 | - Fixed bug #67170 (Disable Nagle's Algorithm with TCP_NODELAY, it delays sending small messages). (Yarek Tyshchenko) 192 | - Fixed bug #68497 (Stomp client doesn't parse ERROR response on CONNECT). (Lorenzo Fontana) 193 | - Fixed bug #64671 (Add stomp_nack and Stomp::nack functions). (Pierrick) 194 | 195 | 196 | 197 | 1.0.51.0.5 198 | stablestable 199 | PHP License 200 | 2012-11-18 201 | 202 | - Fix memory leak when Stomp can't write the message on the queue. (Pierrick) 203 | - Add a buffer for receipts. (Pierrick) 204 | - Fixed bug #62831 (Stomp module seems not initializing SSL library first). 205 | (Patch by lwhsu at lwhsu dot org) 206 | - Fixed bug #59972 (Message body are not binary safe). (Pierrick) 207 | 208 | 209 | 210 | 1.0.41.0.4 211 | stablestable 212 | PHP License 213 | 2012-09-17 214 | 215 | - Fix compatibility with 5.4 216 | 217 | 218 | 219 | 1.0.31.0.3 220 | stablestable 221 | PHP License 222 | 2010-10-12 223 | 224 | - Fixed bug #18772 (setTimeout usecs not honored) 225 | 226 | 227 | 228 | 1.0.21.0.2 229 | stablestable 230 | PHP License 231 | 2010-08-13 232 | 233 | - Fixed SSL connection bug introduced in 1.0.1 234 | 235 | 236 | 237 | 1.0.11.0.1 238 | stablestable 239 | PHP License 240 | 2010-08-03 241 | 242 | - Add new parameter to the constructor to allow client to send extra informations 243 | - Add zend engine runtime cache support (introduced into trunk) 244 | - Add new details property in the StompException class 245 | - Add new StompException::getDetails() method 246 | - Add the frame body content in the Stomp::Error() method 247 | - Fixed bug #17262 (Server is not responding on win32) 248 | 249 | 250 | 251 | 1.0.01.0.0 252 | stablestable 253 | PHP License 254 | 2010-02-11 255 | 256 | - Bump to stable 257 | 258 | 259 | 260 | 0.4.10.4.1 261 | betabeta 262 | PHP License 263 | 2010-01-19 264 | 265 | - Fix compilation issue on PHP5.2 branch 266 | 267 | 268 | 269 | 0.4.00.4.0 270 | betabeta 271 | PHP License 272 | 2010-01-17 273 | 274 | - Adds the ability to specify an alternative class for readFrame 275 | 276 | 277 | 278 | 0.3.20.3.2 279 | betabeta 280 | PHP License 281 | 2009-11-22 282 | 283 | - Adds alt class 284 | - Fixed bug #16936 (Module segfaults on readFrame if Frame > STOMP_BUFSIZE) 285 | - Fixed bug #16933 (readFrame does not notice when server shuts down) 286 | - Fixed bug #16930 (readFrame reports error-frames as "timeout") 287 | 288 | 289 | 290 | 0.3.10.3.1 291 | betabeta 292 | PHP License 293 | 2009-11-08 294 | 295 | - Fix memory leak in stomp_send and in stomp_ack 296 | - Reduced minimum php version to 5.2.2 297 | 298 | 299 | 300 | 0.3.00.3.0 301 | betabeta 302 | PHP License 303 | 2009-11-06 304 | 305 | - new stomp_connect_error() function (pierrick) 306 | - stomp_begin, stomp_abort and stomp_commit now accept additional headers (pierrick) 307 | - new connection timeout and read timeout ini configuration (pierrick) 308 | - Fix a memory leak in stomp_read_line (pierrick) 309 | - Better set of test (Pierrick and Anis) 310 | 311 | 312 | 313 | 0.2.00.2.0 314 | betabeta 315 | PHP License 316 | 2009-11-01 317 | 318 | - Windows build fix (kalle) 319 | - Add SSL support (pierrick) 320 | 321 | 322 | 323 | 0.1.00.1.0 324 | alphaalpha 325 | PHP License 326 | 2009-10-30 327 | 328 | - Initial PECL release. (pierrick) 329 | 330 | 331 | 332 | 333 | -------------------------------------------------------------------------------- /php_stomp.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Pierrick Charron | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifdef HAVE_CONFIG_H 18 | #include "config.h" 19 | #endif 20 | 21 | #include "php.h" 22 | #include "php_ini.h" 23 | #include "zend_exceptions.h" 24 | #include "ext/standard/info.h" 25 | #include "ext/standard/url.h" 26 | #include "php_stomp.h" 27 | #include "Zend/zend_smart_str.h" 28 | 29 | #define GET_STOMP_OBJECT() ((stomp_object_t*) ((char *)Z_OBJ_P(getThis()) - XtOffsetOf(stomp_object_t, std))) 30 | #define FETCH_STOMP_RSRC(result, rsrc) do { \ 31 | result = zend_fetch_resource_ex(rsrc, PHP_STOMP_RES_NAME, le_stomp); \ 32 | } while (0) 33 | 34 | #define FETCH_STOMP_OBJECT do { \ 35 | stomp_object_t *i_obj = GET_STOMP_OBJECT(); \ 36 | if (!(stomp = i_obj->stomp)) { \ 37 | php_error_docref(NULL , E_WARNING, PHP_STOMP_ERR_NO_CTR); \ 38 | RETURN_FALSE; \ 39 | } \ 40 | } while (0) 41 | 42 | #define INIT_FRAME_HEADERS \ 43 | zend_hash_init(frame.headers, 0, NULL, ZVAL_PTR_DTOR, 0); 44 | 45 | #define INIT_FRAME_L(frame, cmd, l) \ 46 | frame.command = cmd; \ 47 | frame.command_length = l; \ 48 | ALLOC_HASHTABLE(frame.headers); \ 49 | INIT_FRAME_HEADERS 50 | 51 | #define INIT_FRAME(frame, cmd) INIT_FRAME_L(frame, cmd, sizeof(cmd)-1) 52 | 53 | #define FRAME_HEADER_FROM_HASHTABLE(h, p) do { \ 54 | zval *value, _zv; \ 55 | zend_string *key; \ 56 | ZEND_HASH_FOREACH_STR_KEY_VAL((p), key, value) { \ 57 | if (key == NULL) { \ 58 | php_error_docref(NULL , E_WARNING, "Invalid argument or parameter array"); \ 59 | break; \ 60 | } else { \ 61 | if (strncmp(ZSTR_VAL(key), "content-length", sizeof("content-length")) != 0) { \ 62 | ZVAL_STR(&_zv, zval_get_string(value)); \ 63 | zend_hash_add((h), key, &_zv); \ 64 | } \ 65 | } \ 66 | } ZEND_HASH_FOREACH_END(); \ 67 | } while (0) 68 | 69 | #define CLEAR_FRAME(frame) \ 70 | zend_hash_destroy(frame.headers); \ 71 | efree(frame.headers); 72 | 73 | #define THROW_STOMP_EXCEPTION(excobj, errnum, error) \ 74 | ZVAL_OBJ(excobj, zend_throw_exception_ex(stomp_ce_exception, errnum, "%s", error)); 75 | 76 | #define STOMP_ERROR(errno, msg) \ 77 | STOMP_G(error_no) = errno; \ 78 | if (STOMP_G(error_msg)) { \ 79 | efree(STOMP_G(error_msg)); \ 80 | } \ 81 | STOMP_G(error_msg) = estrdup(msg); \ 82 | if (stomp_object) { \ 83 | zend_throw_exception_ex(stomp_ce_exception, errno , msg); \ 84 | } 85 | 86 | #define STOMP_ERROR_DETAILS(errno, msg, details) \ 87 | STOMP_G(error_no) = errno; \ 88 | if (STOMP_G(error_msg)) { \ 89 | efree(STOMP_G(error_msg)); \ 90 | } \ 91 | STOMP_G(error_msg) = estrdup(msg); \ 92 | if (stomp_object) { \ 93 | zval _object, *object = &_object; \ 94 | THROW_STOMP_EXCEPTION(object, errno, msg) \ 95 | if (details) { \ 96 | zend_update_property_string(stomp_ce_exception, OBJ_FOR_PROP(object), "details", sizeof("details")-1, (char *) details ); \ 97 | } \ 98 | } 99 | 100 | #if PHP_VERSION_ID < 70300 101 | #define STOMP_URL_STR(a) (a) 102 | #else 103 | #define STOMP_URL_STR(a) ZSTR_VAL(a) 104 | #endif 105 | 106 | #if PHP_VERSION_ID < 80000 107 | #define OBJ_FOR_PROP(zv) (zv) 108 | #else 109 | #define OBJ_FOR_PROP(zv) Z_OBJ_P(zv) 110 | #endif 111 | 112 | static int le_stomp; 113 | static zend_object_handlers stomp_obj_handlers; 114 | 115 | ZEND_DECLARE_MODULE_GLOBALS(stomp) 116 | static PHP_GINIT_FUNCTION(stomp); 117 | 118 | /* {{{ stomp_class_entry */ 119 | zend_class_entry *stomp_ce_stomp; 120 | zend_class_entry *stomp_ce_exception; 121 | zend_class_entry *stomp_ce_frame; 122 | /* }}} */ 123 | 124 | /* {{{ arg_info */ 125 | ZEND_BEGIN_ARG_INFO_EX(stomp_no_args, 0, 0, 0) 126 | ZEND_END_ARG_INFO() 127 | 128 | ZEND_BEGIN_ARG_INFO_EX(stomp_connect_args, 0, 0, 0) 129 | ZEND_ARG_INFO(0, broker) 130 | ZEND_ARG_INFO(0, username) 131 | ZEND_ARG_INFO(0, password) 132 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 133 | ZEND_END_ARG_INFO() 134 | 135 | ZEND_BEGIN_ARG_INFO_EX(stomp_link_only, 0, 0, 1) 136 | ZEND_ARG_INFO(0, link) 137 | ZEND_END_ARG_INFO() 138 | 139 | ZEND_BEGIN_ARG_INFO_EX(stomp_send_args, 0, 0, 3) 140 | ZEND_ARG_INFO(0, link) 141 | ZEND_ARG_INFO(0, destination) 142 | ZEND_ARG_INFO(0, msg) 143 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 144 | ZEND_END_ARG_INFO() 145 | 146 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_send_args, 0, 0, 2) 147 | ZEND_ARG_INFO(0, destination) 148 | ZEND_ARG_INFO(0, msg) 149 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 150 | ZEND_END_ARG_INFO() 151 | 152 | ZEND_BEGIN_ARG_INFO_EX(stomp_subscribe_args, 0, 0, 2) 153 | ZEND_ARG_INFO(0, link) 154 | ZEND_ARG_INFO(0, destination) 155 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 156 | ZEND_END_ARG_INFO() 157 | 158 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_subscribe_args, 0, 0, 1) 159 | ZEND_ARG_INFO(0, destination) 160 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 161 | ZEND_END_ARG_INFO() 162 | 163 | ZEND_BEGIN_ARG_INFO_EX(stomp_readframe_args, 0, 0, 1) 164 | ZEND_ARG_INFO(0, link) 165 | ZEND_ARG_INFO(0, class_name) 166 | ZEND_END_ARG_INFO() 167 | 168 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_readframe_args, 0, 0, 0) 169 | ZEND_ARG_INFO(0, class_name) 170 | ZEND_END_ARG_INFO() 171 | 172 | ZEND_BEGIN_ARG_INFO_EX(stomp_transaction_args, 0, 0, 2) 173 | ZEND_ARG_INFO(0, link) 174 | ZEND_ARG_INFO(0, transaction_id) 175 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 176 | ZEND_END_ARG_INFO() 177 | 178 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_transaction_args, 0, 0, 1) 179 | ZEND_ARG_INFO(0, transaction_id) 180 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 181 | ZEND_END_ARG_INFO() 182 | 183 | ZEND_BEGIN_ARG_INFO_EX(stomp_ack_args, 0, 0, 2) 184 | ZEND_ARG_INFO(0, link) 185 | ZEND_ARG_INFO(0, msg) 186 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 187 | ZEND_END_ARG_INFO() 188 | 189 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_ack_args, 0, 0, 1) 190 | ZEND_ARG_INFO(0, msg) 191 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 192 | ZEND_END_ARG_INFO() 193 | 194 | ZEND_BEGIN_ARG_INFO_EX(stomp_nack_args, 0, 0, 2) 195 | ZEND_ARG_INFO(0, link) 196 | ZEND_ARG_INFO(0, msg) 197 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 198 | ZEND_END_ARG_INFO() 199 | 200 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_nack_args, 0, 0, 1) 201 | ZEND_ARG_INFO(0, msg) 202 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 203 | ZEND_END_ARG_INFO() 204 | 205 | ZEND_BEGIN_ARG_INFO_EX(stomp_set_read_timeout_args, 0, 0, 2) 206 | ZEND_ARG_INFO(0, link) 207 | ZEND_ARG_INFO(0, seconds) 208 | ZEND_ARG_INFO(0, microseconds) 209 | ZEND_END_ARG_INFO() 210 | 211 | ZEND_BEGIN_ARG_INFO_EX(stomp_oop_set_read_timeout_args, 0, 0, 1) 212 | ZEND_ARG_INFO(0, seconds) 213 | ZEND_ARG_INFO(0, microseconds) 214 | ZEND_END_ARG_INFO() 215 | 216 | ZEND_BEGIN_ARG_INFO_EX(stomp_frame_construct_args, 0, 0, 0) 217 | ZEND_ARG_INFO(0, command) 218 | ZEND_ARG_ARRAY_INFO(0, headers, 1) 219 | ZEND_ARG_INFO(0, body) 220 | ZEND_END_ARG_INFO() 221 | /* }}} */ 222 | 223 | /* {{{ stomp_functions */ 224 | zend_function_entry stomp_functions[] = { 225 | PHP_FE(stomp_version, stomp_no_args) 226 | PHP_FE(stomp_connect, stomp_connect_args) 227 | PHP_FE(stomp_connect_error, stomp_no_args) 228 | PHP_FE(stomp_get_session_id, stomp_link_only) 229 | PHP_FE(stomp_close, stomp_link_only) 230 | PHP_FE(stomp_send, stomp_send_args) 231 | PHP_FE(stomp_subscribe, stomp_subscribe_args) 232 | PHP_FE(stomp_has_frame, stomp_link_only) 233 | PHP_FE(stomp_read_frame, stomp_readframe_args) 234 | PHP_FE(stomp_unsubscribe, stomp_subscribe_args) 235 | PHP_FE(stomp_begin, stomp_transaction_args) 236 | PHP_FE(stomp_commit, stomp_transaction_args) 237 | PHP_FE(stomp_abort, stomp_transaction_args) 238 | PHP_FE(stomp_ack, stomp_ack_args) 239 | PHP_FE(stomp_nack, stomp_nack_args) 240 | PHP_FE(stomp_error, stomp_link_only) 241 | PHP_FE(stomp_set_read_timeout, stomp_set_read_timeout_args) 242 | PHP_FE(stomp_get_read_timeout, stomp_link_only) 243 | {NULL, NULL, NULL} 244 | }; 245 | /* }}} */ 246 | 247 | /* {{{ stomp_methods[] */ 248 | static zend_function_entry stomp_methods[] = { 249 | PHP_FALIAS(__construct, stomp_connect, stomp_connect_args) 250 | PHP_FALIAS(getSessionId, stomp_get_session_id, stomp_no_args) 251 | PHP_FALIAS(__destruct, stomp_close, stomp_no_args) 252 | PHP_FALIAS(send, stomp_send, stomp_oop_send_args) 253 | PHP_FALIAS(subscribe, stomp_subscribe, stomp_oop_subscribe_args) 254 | PHP_FALIAS(hasFrame, stomp_has_frame, stomp_no_args) 255 | PHP_FALIAS(readFrame, stomp_read_frame, stomp_oop_readframe_args) 256 | PHP_FALIAS(unsubscribe, stomp_unsubscribe, stomp_oop_subscribe_args) 257 | PHP_FALIAS(begin, stomp_begin, stomp_oop_transaction_args) 258 | PHP_FALIAS(commit, stomp_commit, stomp_oop_transaction_args) 259 | PHP_FALIAS(abort, stomp_abort, stomp_oop_transaction_args) 260 | PHP_FALIAS(ack, stomp_ack, stomp_oop_ack_args) 261 | PHP_FALIAS(nack, stomp_nack, stomp_oop_nack_args) 262 | PHP_FALIAS(error, stomp_error, stomp_no_args) 263 | PHP_FALIAS(setReadTimeout, stomp_set_read_timeout, stomp_oop_set_read_timeout_args) 264 | PHP_FALIAS(getReadTimeout, stomp_get_read_timeout, stomp_no_args) 265 | {NULL, NULL, NULL} 266 | }; 267 | /* }}} */ 268 | 269 | /* {{{ stomp_frame_methods[] */ 270 | static zend_function_entry stomp_frame_methods[] = { 271 | PHP_ME(stompframe, __construct, stomp_frame_construct_args, ZEND_ACC_PUBLIC) 272 | {NULL, NULL, NULL} 273 | }; 274 | /* }}} */ 275 | 276 | /* {{{ stomp_exception_methods[] */ 277 | static zend_function_entry stomp_exception_methods[] = { 278 | PHP_ME(stompexception, getDetails, stomp_no_args, ZEND_ACC_PUBLIC) 279 | {NULL, NULL, NULL} 280 | }; 281 | /* }}} */ 282 | 283 | /* {{{ stomp_module_entry */ 284 | zend_module_entry stomp_module_entry = { 285 | STANDARD_MODULE_HEADER, 286 | PHP_STOMP_EXTNAME, 287 | stomp_functions, 288 | PHP_MINIT(stomp), 289 | PHP_MSHUTDOWN(stomp), 290 | PHP_RINIT(stomp), 291 | PHP_RSHUTDOWN(stomp), 292 | PHP_MINFO(stomp), 293 | PHP_STOMP_VERSION, 294 | PHP_MODULE_GLOBALS(stomp), 295 | PHP_GINIT(stomp), 296 | NULL, 297 | NULL, 298 | STANDARD_MODULE_PROPERTIES_EX 299 | }; 300 | /* }}} */ 301 | 302 | PHP_INI_BEGIN() 303 | STD_PHP_INI_ENTRY("stomp.default_broker", "tcp://localhost:61613", PHP_INI_ALL, OnUpdateString, default_broker, zend_stomp_globals, stomp_globals) 304 | STD_PHP_INI_ENTRY("stomp.default_username", "", PHP_INI_ALL, OnUpdateString, default_username, zend_stomp_globals, stomp_globals) 305 | STD_PHP_INI_ENTRY("stomp.default_password", "", PHP_INI_ALL, OnUpdateString, default_password, zend_stomp_globals, stomp_globals) 306 | STD_PHP_INI_ENTRY("stomp.default_read_timeout_sec", "2", PHP_INI_ALL, OnUpdateLong, read_timeout_sec, zend_stomp_globals, stomp_globals) 307 | STD_PHP_INI_ENTRY("stomp.default_read_timeout_usec", "0", PHP_INI_ALL, OnUpdateLong, read_timeout_usec, zend_stomp_globals, stomp_globals) 308 | STD_PHP_INI_ENTRY("stomp.default_connection_timeout_sec", "2", PHP_INI_ALL, OnUpdateLong, connection_timeout_sec, zend_stomp_globals, stomp_globals) 309 | STD_PHP_INI_ENTRY("stomp.default_connection_timeout_usec", "0", PHP_INI_ALL, OnUpdateLong, connection_timeout_usec, zend_stomp_globals, stomp_globals) 310 | PHP_INI_END() 311 | 312 | /* {{{ PHP_GINIT_FUNCTION */ 313 | static PHP_GINIT_FUNCTION(stomp) 314 | { 315 | stomp_globals->default_broker = NULL; 316 | stomp_globals->default_username = NULL; 317 | stomp_globals->default_password = NULL; 318 | stomp_globals->read_timeout_sec = 2; 319 | stomp_globals->read_timeout_usec = 0; 320 | stomp_globals->connection_timeout_sec = 2; 321 | stomp_globals->connection_timeout_usec = 0; 322 | #if HAVE_STOMP_SSL 323 | SSL_library_init(); 324 | #endif 325 | } 326 | /* }}} */ 327 | 328 | ZEND_DECLARE_MODULE_GLOBALS(stomp) 329 | 330 | #ifdef COMPILE_DL_STOMP 331 | ZEND_GET_MODULE(stomp) 332 | #endif 333 | 334 | /* {{{ constructor/destructor */ 335 | static void stomp_send_disconnect(stomp_t *stomp) 336 | { 337 | stomp_frame_t frame = {0}; 338 | INIT_FRAME(frame, "DISCONNECT"); 339 | 340 | stomp_send(stomp, &frame ); 341 | CLEAR_FRAME(frame); 342 | } 343 | 344 | static void php_destroy_stomp_res(zend_resource *rsrc) 345 | { 346 | stomp_t *stomp = (stomp_t *) rsrc->ptr; 347 | stomp_send_disconnect(stomp ); 348 | stomp_close(stomp); 349 | } 350 | 351 | static zend_object *php_stomp_new(zend_class_entry *ce) 352 | { 353 | stomp_object_t *intern; 354 | 355 | intern = (stomp_object_t *) ecalloc(1, sizeof(stomp_object_t) + zend_object_properties_size(ce)); 356 | intern->stomp = NULL; 357 | 358 | zend_object_std_init(&intern->std, ce ); 359 | 360 | intern->std.handlers = &stomp_obj_handlers; 361 | 362 | return &intern->std; 363 | } 364 | /* }}} */ 365 | 366 | /* {{{ PHP_MINIT_FUNCTION */ 367 | PHP_MINIT_FUNCTION(stomp) 368 | { 369 | zend_class_entry ce; 370 | 371 | /* Ressource */ 372 | le_stomp = zend_register_list_destructors_ex(php_destroy_stomp_res, NULL, PHP_STOMP_RES_NAME, module_number); 373 | 374 | /* Register Stomp class */ 375 | INIT_CLASS_ENTRY(ce, PHP_STOMP_CLASSNAME, stomp_methods); 376 | stomp_ce_stomp = zend_register_internal_class(&ce ); 377 | stomp_ce_stomp->create_object = php_stomp_new; 378 | memcpy(&stomp_obj_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 379 | stomp_obj_handlers.offset = XtOffsetOf(stomp_object_t, std); 380 | 381 | /* Register StompFrame class */ 382 | INIT_CLASS_ENTRY(ce, PHP_STOMP_FRAME_CLASSNAME, stomp_frame_methods); 383 | stomp_ce_frame = zend_register_internal_class(&ce ); 384 | 385 | /* Properties */ 386 | zend_declare_property_null(stomp_ce_frame, "command", sizeof("command")-1, ZEND_ACC_PUBLIC ); 387 | zend_declare_property_null(stomp_ce_frame, "headers", sizeof("headers")-1, ZEND_ACC_PUBLIC ); 388 | zend_declare_property_null(stomp_ce_frame, "body", sizeof("body")-1, ZEND_ACC_PUBLIC ); 389 | 390 | /* Register StompException class */ 391 | INIT_CLASS_ENTRY(ce, PHP_STOMP_EXCEPTION_CLASSNAME, stomp_exception_methods); 392 | stomp_ce_exception = zend_register_internal_class_ex(&ce, zend_exception_get_default()); 393 | 394 | /* Properties */ 395 | zend_declare_property_null(stomp_ce_exception, "details", sizeof("details")-1, ZEND_ACC_PRIVATE ); 396 | 397 | /** Register INI entries **/ 398 | REGISTER_INI_ENTRIES(); 399 | 400 | return SUCCESS; 401 | } 402 | /* }}} */ 403 | 404 | /* {{{ PHP_MSHUTDOWN_FUNCTION */ 405 | PHP_MSHUTDOWN_FUNCTION(stomp) 406 | { 407 | /* Unregister INI entries */ 408 | UNREGISTER_INI_ENTRIES(); 409 | return SUCCESS; 410 | } 411 | /* }}} */ 412 | 413 | /* {{{ PHP_RINIT_FUNCTION */ 414 | PHP_RINIT_FUNCTION(stomp) 415 | { 416 | STOMP_G(error_msg) = NULL; 417 | STOMP_G(error_no) = 0; 418 | 419 | return SUCCESS; 420 | } 421 | /* }}} */ 422 | 423 | /* {{{ PHP_RSHUTDOWN_FUNCTION */ 424 | PHP_RSHUTDOWN_FUNCTION(stomp) 425 | { 426 | if (STOMP_G(error_msg)) { 427 | efree(STOMP_G(error_msg)); 428 | } 429 | 430 | return SUCCESS; 431 | } 432 | /* }}} */ 433 | 434 | /* {{{ PHP_MINFO_FUNCTION */ 435 | PHP_MINFO_FUNCTION(stomp) 436 | { 437 | php_info_print_table_start(); 438 | php_info_print_table_header(2, PHP_STOMP_EXTNAME, "enabled"); 439 | php_info_print_table_row(2, "API version", PHP_STOMP_VERSION); 440 | #if HAVE_STOMP_SSL 441 | php_info_print_table_row(2, "SSL Support", "enabled"); 442 | #else 443 | php_info_print_table_row(2, "SSL Support", "disabled"); 444 | #endif 445 | php_info_print_table_end(); 446 | DISPLAY_INI_ENTRIES(); 447 | } 448 | /* }}} */ 449 | 450 | /* {{{ proto string stomp_version() 451 | Get stomp extension version */ 452 | PHP_FUNCTION(stomp_version) 453 | { 454 | RETURN_STRINGL(PHP_STOMP_VERSION, sizeof(PHP_STOMP_VERSION)-1); 455 | } 456 | /* }}} */ 457 | 458 | /* {{{ proto Stomp::__construct([string broker [, string username [, string password [, array headers]]]]) 459 | Connect to server */ 460 | PHP_FUNCTION(stomp_connect) 461 | { 462 | zval *stomp_object = getThis(); 463 | zval *headers = NULL; 464 | stomp_t *stomp = NULL; 465 | zend_string *broker = NULL, *username = NULL, *password = NULL; 466 | php_url *url_parts; 467 | 468 | #ifdef HAVE_STOMP_SSL 469 | int use_ssl = 0; 470 | #endif 471 | 472 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "|SSSa!", &broker, &username, &password, &headers) == FAILURE) { 473 | return; 474 | } 475 | 476 | /* Verify that broker URI */ 477 | if (!broker) { 478 | broker = STOMP_G(default_broker)? 479 | zend_string_init(STOMP_G(default_broker), strlen(STOMP_G(default_broker)), 0) : NULL; 480 | } else { 481 | zend_string_copy(broker); 482 | } 483 | 484 | url_parts = php_url_parse_ex(ZSTR_VAL(broker), ZSTR_LEN(broker)); 485 | 486 | if (!url_parts || !url_parts->host) { 487 | STOMP_ERROR(0, PHP_STOMP_ERR_INVALID_BROKER_URI); 488 | zend_string_release(broker); 489 | php_url_free(url_parts); 490 | return; 491 | } 492 | zend_string_release(broker); 493 | 494 | if (url_parts->scheme) { 495 | if (strcmp(STOMP_URL_STR(url_parts->scheme), "ssl") == 0) { 496 | #if HAVE_STOMP_SSL 497 | use_ssl = 1; 498 | #else 499 | STOMP_ERROR(0, "SSL DISABLED"); 500 | php_url_free(url_parts); 501 | return; 502 | #endif 503 | } else if (strcmp(STOMP_URL_STR(url_parts->scheme), "tcp") != 0) { 504 | STOMP_ERROR(0, PHP_STOMP_ERR_INVALID_BROKER_URI_SCHEME); 505 | php_url_free(url_parts); 506 | return; 507 | } 508 | } 509 | 510 | stomp = stomp_init(); 511 | 512 | #if HAVE_STOMP_SSL 513 | stomp->options.use_ssl = use_ssl; 514 | #endif 515 | 516 | stomp->options.read_timeout_sec = STOMP_G(read_timeout_sec); 517 | stomp->options.read_timeout_usec = STOMP_G(read_timeout_usec); 518 | stomp->options.connect_timeout_sec = STOMP_G(connection_timeout_sec); 519 | stomp->options.connect_timeout_usec = STOMP_G(connection_timeout_usec); 520 | 521 | stomp->status = stomp_connect(stomp, STOMP_URL_STR(url_parts->host), url_parts->port ? url_parts->port : 61613 ); 522 | php_url_free(url_parts); 523 | 524 | if (stomp->status) { 525 | zval rv; 526 | stomp_frame_t *res; 527 | stomp_frame_t frame = {0}; 528 | int send_status; 529 | 530 | INIT_FRAME(frame, "CONNECT"); 531 | if (!username) { 532 | username = zend_string_init(STOMP_G(default_username), strlen(STOMP_G(default_username)), 0); 533 | } else { 534 | zend_string_copy(username); 535 | } 536 | 537 | if (!password) { 538 | password = zend_string_init(STOMP_G(default_password), strlen(STOMP_G(default_password)), 0); 539 | } else { 540 | zend_string_copy(password); 541 | } 542 | 543 | /* 544 | * Per Stomp 1.1 "login" and "passcode" are optional. (Also this fix makes test pass against RabbitMQ) 545 | */ 546 | if (ZSTR_LEN(username) > 0) { 547 | ZVAL_STR(&rv, zend_string_copy(username)); 548 | zend_hash_str_add(frame.headers, "login", sizeof("login") - 1, &rv); 549 | } 550 | 551 | if (ZSTR_LEN(password)) { 552 | ZVAL_STR(&rv, zend_string_copy(password)); 553 | zend_hash_str_add(frame.headers, "passcode", sizeof("passcode"), &rv); 554 | } 555 | 556 | zend_string_release(username); 557 | zend_string_release(password); 558 | 559 | if (NULL != headers) { 560 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(headers)); 561 | } 562 | 563 | send_status = stomp_send(stomp, &frame ); 564 | CLEAR_FRAME(frame); 565 | if (0 == send_status) { 566 | zval excobj; 567 | THROW_STOMP_EXCEPTION(&excobj, stomp->errnum, stomp->error); 568 | if (stomp->error_details) { 569 | zend_update_property_string(stomp_ce_exception, OBJ_FOR_PROP(&excobj), "details", sizeof("details")-1, stomp->error_details ); 570 | } 571 | return; 572 | } 573 | 574 | /* Retreive Response */ 575 | res = stomp_read_frame_ex(stomp, 0); 576 | if (NULL == res) { 577 | STOMP_ERROR(0, PHP_STOMP_ERR_SERVER_NOT_RESPONDING); 578 | } else if (0 == strncmp("ERROR", res->command, sizeof("ERROR") - 1)) { 579 | zval *error_msg, excobj; 580 | if ((error_msg = zend_hash_str_find(res->headers, ZEND_STRL("message"))) != NULL) { 581 | THROW_STOMP_EXCEPTION(&excobj, 0, ZSTR_VAL(Z_STR_P(error_msg))); 582 | if (res->body) { 583 | zend_update_property_string(stomp_ce_exception, OBJ_FOR_PROP(&excobj), "details", sizeof("details")-1, (char *) res->body ); 584 | } 585 | } 586 | stomp_free_frame(res); 587 | } else if (0 != strncmp("CONNECTED", res->command, sizeof("CONNECTED")-1)) { 588 | if (stomp->error) { 589 | STOMP_ERROR_DETAILS(stomp->errnum, stomp->error, stomp->error_details); 590 | } else { 591 | STOMP_ERROR(0, PHP_STOMP_ERR_UNKNOWN); 592 | } 593 | stomp_free_frame(res); 594 | } else { 595 | zval *key; 596 | if ((key = zend_hash_str_find(res->headers, ZEND_STRL("session"))) != NULL) { 597 | if (stomp->session) { 598 | efree(stomp->session); 599 | } 600 | ZEND_ASSERT(Z_TYPE_P(key) == IS_STRING); 601 | stomp->session = estrdup(Z_STRVAL_P(key)); 602 | } 603 | stomp_free_frame(res); 604 | if (!stomp_object) { 605 | RETURN_RES(zend_register_resource(stomp, le_stomp)); 606 | } else { 607 | stomp_object_t *i_obj = GET_STOMP_OBJECT(); 608 | if (i_obj->stomp) { 609 | stomp_close(i_obj->stomp); 610 | } 611 | i_obj->stomp = stomp; 612 | RETURN_TRUE; 613 | } 614 | } 615 | } else { 616 | STOMP_ERROR_DETAILS(0, stomp->error, stomp->error_details); 617 | } 618 | 619 | stomp_close(stomp); 620 | RETURN_FALSE; 621 | } 622 | /* }}} */ 623 | 624 | /* {{{ proto string stomp_connect_error() 625 | Get the last connection error */ 626 | PHP_FUNCTION(stomp_connect_error) 627 | { 628 | if (STOMP_G(error_msg)) { 629 | RETURN_STRING(STOMP_G(error_msg)); 630 | } else { 631 | RETURN_NULL(); 632 | } 633 | } 634 | /* }}} */ 635 | 636 | /* {{{ proto string Stomp::getSessionId() 637 | Get the current stomp session ID */ 638 | PHP_FUNCTION(stomp_get_session_id) 639 | { 640 | zval *stomp_object = getThis(); 641 | stomp_t *stomp = NULL; 642 | if (stomp_object) { 643 | FETCH_STOMP_OBJECT; 644 | } else { 645 | zval *arg; 646 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "r", &arg) == FAILURE) { 647 | return; 648 | } 649 | FETCH_STOMP_RSRC(stomp, arg); 650 | } 651 | 652 | if (!stomp) { 653 | php_error_docref(NULL , E_WARNING, PHP_STOMP_ERR_NO_CTR); 654 | RETURN_FALSE; 655 | } 656 | 657 | if (stomp->session) { 658 | RETURN_STRING(stomp->session); 659 | } else { 660 | RETURN_FALSE; 661 | } 662 | } 663 | /* }}} */ 664 | 665 | /* {{{ proto boolean Stomp::__destruct() 666 | Close stomp connection */ 667 | PHP_FUNCTION(stomp_close) 668 | { 669 | zval *stomp_object = getThis(); 670 | stomp_t *stomp = NULL; 671 | 672 | if (stomp_object) { 673 | stomp_object_t *i_obj = GET_STOMP_OBJECT(); 674 | if (!(stomp = i_obj->stomp)) { 675 | php_error_docref(NULL , E_WARNING, PHP_STOMP_ERR_NO_CTR); 676 | RETURN_FALSE; 677 | } 678 | stomp_send_disconnect(stomp ); 679 | stomp_close(stomp); 680 | i_obj->stomp = NULL; 681 | } else { 682 | zval *arg; 683 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "r", &arg) == FAILURE) { 684 | return; 685 | } 686 | FETCH_STOMP_RSRC(stomp, arg); 687 | zend_list_close(Z_RES_P(arg)); 688 | } 689 | 690 | RETURN_TRUE; 691 | } 692 | /* }}} */ 693 | 694 | /* {{{ proto boolean Stomp::send(string destination, mixed msg [, array headers]) 695 | Sends a message to a destination in the messaging system */ 696 | PHP_FUNCTION(stomp_send) 697 | { 698 | zval *stomp_object = getThis(); 699 | stomp_t *stomp = NULL; 700 | zend_string *destination; 701 | zval *msg, *headers = NULL, rv; 702 | stomp_frame_t frame = {0}; 703 | int success = 0; 704 | 705 | if (stomp_object) { 706 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "Sz|a!", &destination, &msg, &headers) == FAILURE) { 707 | return; 708 | } 709 | FETCH_STOMP_OBJECT; 710 | } else { 711 | zval *arg; 712 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "rSz|a!", &arg, &destination, &msg, &headers) == FAILURE) { 713 | return; 714 | } 715 | FETCH_STOMP_RSRC(stomp, arg); 716 | } 717 | 718 | /* Verify destination */ 719 | if (0 == ZSTR_LEN(destination)) { 720 | php_error_docref(NULL , E_WARNING, PHP_STOMP_ERR_EMPTY_DESTINATION); 721 | RETURN_FALSE; 722 | } 723 | 724 | INIT_FRAME(frame, "SEND"); 725 | 726 | /* Translate a PHP array to a stomp_header array */ 727 | if (NULL != headers) { 728 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(headers)); 729 | } 730 | 731 | /* Add the destination */ 732 | ZVAL_STR(&rv, zend_string_copy(destination)); 733 | zend_hash_str_add(frame.headers, "destination", sizeof("destination") - 1, &rv); 734 | 735 | if (Z_TYPE_P(msg) == IS_STRING) { 736 | frame.body = Z_STRVAL_P(msg); 737 | frame.body_length = Z_STRLEN_P(msg); 738 | } else if (Z_TYPE_P(msg) == IS_OBJECT && instanceof_function(Z_OBJCE_P(msg), stomp_ce_frame )) { 739 | zval *frame_obj_prop = NULL; 740 | frame_obj_prop = zend_read_property(stomp_ce_frame, OBJ_FOR_PROP(msg), "command", sizeof("command")-1, 1, &rv); 741 | if (Z_TYPE_P(frame_obj_prop) == IS_STRING) { 742 | frame.command = Z_STRVAL_P(frame_obj_prop); 743 | frame.command_length = Z_STRLEN_P(frame_obj_prop); 744 | } 745 | frame_obj_prop = zend_read_property(stomp_ce_frame, OBJ_FOR_PROP(msg), "body", sizeof("body")-1, 1, &rv); 746 | if (Z_TYPE_P(frame_obj_prop) == IS_STRING) { 747 | frame.body = Z_STRVAL_P(frame_obj_prop); 748 | frame.body_length = Z_STRLEN_P(frame_obj_prop); 749 | } 750 | frame_obj_prop = zend_read_property(stomp_ce_frame, OBJ_FOR_PROP(msg), "headers", sizeof("headers")-1, 1, &rv); 751 | if (Z_TYPE_P(frame_obj_prop) == IS_ARRAY) { 752 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(frame_obj_prop)); 753 | } 754 | } else { 755 | php_error_docref(NULL , E_WARNING, "Expects parameter %d to be a string or a StompFrame object.", stomp_object?2:3); 756 | CLEAR_FRAME(frame); 757 | RETURN_FALSE; 758 | } 759 | 760 | if (stomp_send(stomp, &frame ) > 0) { 761 | success = stomp_valid_receipt(stomp, &frame); 762 | } 763 | 764 | CLEAR_FRAME(frame); 765 | RETURN_BOOL(success); 766 | } 767 | /* }}} */ 768 | 769 | /* {{{ proto boolean Stomp::subscribe(string destination [, array headers]) 770 | Register to listen to a given destination */ 771 | PHP_FUNCTION(stomp_subscribe) 772 | { 773 | zval *stomp_object = getThis(); 774 | stomp_t *stomp = NULL; 775 | zend_string *destination; 776 | zval *headers = NULL, rv; 777 | stomp_frame_t frame = {0}; 778 | int success = 0; 779 | 780 | if (stomp_object) { 781 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "S|a!", &destination, &headers) == FAILURE) { 782 | return; 783 | } 784 | FETCH_STOMP_OBJECT; 785 | } else { 786 | zval *arg = NULL; 787 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "rS|a!", &arg, &destination, &headers) == FAILURE) { 788 | return; 789 | } 790 | FETCH_STOMP_RSRC(stomp, arg); 791 | } 792 | 793 | /* Verify destination */ 794 | if (ZSTR_LEN(destination) == 0) { 795 | php_error_docref(NULL , E_WARNING, PHP_STOMP_ERR_EMPTY_DESTINATION); 796 | RETURN_FALSE; 797 | } 798 | 799 | INIT_FRAME(frame, "SUBSCRIBE"); 800 | 801 | /* Translate a PHP array to a stomp_header array */ 802 | if (NULL != headers) { 803 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(headers)); 804 | } 805 | 806 | /* Add the ack if not already in the headers */ 807 | if (!zend_hash_str_find(frame.headers, ZEND_STRL("ack"))) { 808 | ZVAL_STRINGL(&rv, "client", sizeof("client") - 1); 809 | zend_hash_str_update(frame.headers, "ack", sizeof("ack") - 1, &rv); 810 | } 811 | 812 | /* Add the destination */ 813 | ZVAL_STR(&rv, zend_string_copy(destination)); 814 | zend_hash_str_update(frame.headers, "destination", sizeof("destination") - 1, &rv); 815 | /* zend_hash_str_add_ptr(frame.headers, ZEND_STRL("activemq.prefetchSize"), estrdup("1")); */ 816 | 817 | if (stomp_send(stomp, &frame ) > 0) { 818 | success = stomp_valid_receipt(stomp, &frame); 819 | } 820 | 821 | CLEAR_FRAME(frame); 822 | RETURN_BOOL(success); 823 | } 824 | /* }}} */ 825 | 826 | /* {{{ proto boolean Stomp::unsubscribe(string destination [, array headers]) 827 | Remove an existing subscription */ 828 | PHP_FUNCTION(stomp_unsubscribe) 829 | { 830 | zval *stomp_object = getThis(); 831 | stomp_t *stomp = NULL; 832 | zend_string *destination; 833 | zval *headers = NULL, rv; 834 | stomp_frame_t frame = {0}; 835 | int success = 0; 836 | 837 | if (stomp_object) { 838 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "S|a!", &destination, &headers) == FAILURE) { 839 | return; 840 | } 841 | FETCH_STOMP_OBJECT; 842 | } else { 843 | zval *arg = NULL; 844 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "rS|a!", &arg, &destination, &headers) == FAILURE) { 845 | return; 846 | } 847 | FETCH_STOMP_RSRC(stomp, arg); 848 | } 849 | 850 | /* Verify destination */ 851 | if (0 == ZSTR_LEN(destination)) { 852 | php_error_docref(NULL , E_WARNING, PHP_STOMP_ERR_EMPTY_DESTINATION); 853 | RETURN_FALSE; 854 | } 855 | 856 | INIT_FRAME(frame, "UNSUBSCRIBE"); 857 | 858 | /* Translate a PHP array to a stomp_header array */ 859 | if (NULL != headers) { 860 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(headers)); 861 | } 862 | 863 | /* Add the destination */ 864 | ZVAL_STR(&rv, zend_string_copy(destination)); 865 | zend_hash_str_add(frame.headers, "destination", sizeof("destination") - 1, &rv); 866 | 867 | if (stomp_send(stomp, &frame ) > 0) { 868 | success = stomp_valid_receipt(stomp, &frame); 869 | } 870 | 871 | CLEAR_FRAME(frame); 872 | RETURN_BOOL(success); 873 | } 874 | /* }}} */ 875 | 876 | /* {{{ proto boolean Stomp::hasFrame() 877 | Indicate whether or not there is a frame ready to read */ 878 | PHP_FUNCTION(stomp_has_frame) 879 | { 880 | zval *stomp_object = getThis(); 881 | stomp_t *stomp = NULL; 882 | if (stomp_object) { 883 | FETCH_STOMP_OBJECT; 884 | } else { 885 | zval *arg; 886 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "r", &arg) == FAILURE) { 887 | return; 888 | } 889 | FETCH_STOMP_RSRC(stomp, arg); 890 | } 891 | 892 | RETURN_BOOL(stomp_select(stomp) > 0); 893 | } 894 | /* }}} */ 895 | 896 | /* {{{ proto StompFrame Stomp::readFrame() 897 | Read the next frame */ 898 | PHP_FUNCTION(stomp_read_frame) 899 | { 900 | zval *stomp_object = getThis(); 901 | stomp_t *stomp = NULL; 902 | stomp_frame_t *res = NULL; 903 | zend_string *class_name = NULL; 904 | zend_class_entry *ce = NULL; 905 | 906 | if (stomp_object) { 907 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "|S", &class_name) == FAILURE) { 908 | return; 909 | } 910 | FETCH_STOMP_OBJECT; 911 | } else { 912 | zval *arg; 913 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "r|S", &arg, &class_name) == FAILURE) { 914 | return; 915 | } 916 | FETCH_STOMP_RSRC(stomp, arg); 917 | } 918 | 919 | if (class_name && ZSTR_LEN(class_name)) { 920 | ce = zend_fetch_class(class_name, ZEND_FETCH_CLASS_AUTO); 921 | if (!ce) { 922 | php_error_docref(NULL , E_WARNING, "Could not find class '%s'", ZSTR_VAL(class_name)); 923 | ce = stomp_ce_frame; 924 | } 925 | } else if (stomp_object) { 926 | ce = stomp_ce_frame; 927 | } 928 | 929 | if ((res = stomp_read_frame(stomp))) { 930 | zval headers; 931 | 932 | if (0 == strncmp("ERROR", res->command, sizeof("ERROR") - 1)) { 933 | zval *error_msg; 934 | if ((error_msg = zend_hash_str_find(res->headers, "message", sizeof("message") - 1)) != NULL) { 935 | zval excobj; 936 | THROW_STOMP_EXCEPTION(&excobj, 0, Z_STRVAL_P(error_msg)); 937 | if (res->body) { 938 | zend_update_property_string(stomp_ce_exception, OBJ_FOR_PROP(&excobj), ZEND_STRL("details"), (char *)res->body ); 939 | } 940 | stomp_free_frame(res); 941 | RETURN_FALSE; 942 | } 943 | } 944 | 945 | array_init(&headers); 946 | if (res->headers) { 947 | zend_string *key; 948 | zval *val; 949 | ZEND_HASH_FOREACH_STR_KEY_VAL(res->headers, key, val) { 950 | if (!key) { 951 | break; 952 | } 953 | Z_TRY_ADDREF_P(val); 954 | zend_hash_update(Z_ARRVAL(headers), key, val); 955 | } ZEND_HASH_FOREACH_END(); 956 | } 957 | 958 | if (ce) { 959 | zend_fcall_info fci; 960 | zend_fcall_info_cache fcc; 961 | zval retval; 962 | 963 | object_init_ex(return_value, ce); 964 | 965 | if (ce->constructor) { 966 | zval cmd, body; 967 | 968 | ZVAL_STRINGL(&cmd, res->command, res->command_length); 969 | 970 | if (res->body) { 971 | ZVAL_STRINGL(&body, res->body, res->body_length); 972 | } else { 973 | ZVAL_NULL(&body); 974 | } 975 | 976 | memset(&fci, 0, sizeof(fci)); 977 | memset(&fcc, 0, sizeof(fcc)); 978 | fci.size = sizeof(fci); 979 | #if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0) 980 | fci.function_table = &ce->function_table; 981 | #endif 982 | /* PARAMS */ 983 | fci.param_count = 3; 984 | fci.params = (zval*) safe_emalloc(sizeof(zval), 3, 0); 985 | ZVAL_COPY_VALUE(&fci.params[0], &cmd); 986 | ZVAL_COPY_VALUE(&fci.params[1], &headers); 987 | ZVAL_COPY_VALUE(&fci.params[2], &body); 988 | 989 | ZVAL_UNDEF(&fci.function_name); 990 | fci.object = Z_OBJ_P(return_value); 991 | fci.retval = &retval; 992 | #if PHP_VERSION_ID < 80000 993 | fci.no_separation = 1; 994 | #endif 995 | #if PHP_VERSION_ID < 70300 996 | fcc.initialized = 1; 997 | #endif 998 | fcc.function_handler = ce->constructor; 999 | #if (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION == 0) 1000 | fcc.calling_scope = EG(scope); 1001 | #else 1002 | fcc.calling_scope = zend_get_executed_scope(); 1003 | #endif 1004 | fcc.object = Z_OBJ_P(return_value); 1005 | 1006 | if (zend_call_function(&fci, &fcc ) == FAILURE) { 1007 | zend_throw_exception_ex(zend_exception_get_default(), 0 , "Could not execute %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name)); 1008 | } else { 1009 | zval_ptr_dtor(&retval); 1010 | } 1011 | if (fci.params) { 1012 | efree(fci.params); 1013 | } 1014 | 1015 | zval_ptr_dtor(&cmd); 1016 | zval_ptr_dtor(&body); 1017 | } 1018 | 1019 | zval_ptr_dtor(&headers); 1020 | } else { 1021 | array_init(return_value); 1022 | add_assoc_string_ex(return_value, "command", sizeof("command") - 1, res->command); 1023 | if (res->body) { 1024 | add_assoc_stringl_ex(return_value, "body", sizeof("body") - 1, res->body, res->body_length); 1025 | } 1026 | add_assoc_zval_ex(return_value, "headers", sizeof("headers") - 1, &headers); 1027 | } 1028 | 1029 | stomp_free_frame(res); 1030 | } else { 1031 | RETURN_FALSE; 1032 | } 1033 | } 1034 | /* }}} */ 1035 | 1036 | /* {{{ _php_stomp_transaction */ 1037 | static void _php_stomp_transaction(INTERNAL_FUNCTION_PARAMETERS, char *cmd, size_t cmd_len) { 1038 | zval *stomp_object = getThis(); 1039 | stomp_t *stomp = NULL; 1040 | zend_string *transaction_id; 1041 | stomp_frame_t frame = {0}; 1042 | int success = 0; 1043 | zval *headers = NULL, rv; 1044 | 1045 | if (stomp_object) { 1046 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "S!|a", &transaction_id, &headers) == FAILURE) { 1047 | return; 1048 | } 1049 | FETCH_STOMP_OBJECT; 1050 | } else { 1051 | zval *arg; 1052 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "rS!|a", &arg, &transaction_id, &headers) == FAILURE) { 1053 | return; 1054 | } 1055 | FETCH_STOMP_RSRC(stomp, arg); 1056 | } 1057 | 1058 | INIT_FRAME_L(frame, cmd, cmd_len); 1059 | 1060 | if (transaction_id && ZSTR_LEN(transaction_id)) { 1061 | ZVAL_STR(&rv, zend_string_copy(transaction_id)); 1062 | zend_hash_str_add(frame.headers, "transaction", sizeof("transaction") - 1, &rv); 1063 | } 1064 | 1065 | /* Translate a PHP array to a stomp_header array */ 1066 | if (NULL != headers) { 1067 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(headers)); 1068 | } 1069 | 1070 | if (stomp_send(stomp, &frame ) > 0) { 1071 | success = stomp_valid_receipt(stomp, &frame); 1072 | } 1073 | 1074 | CLEAR_FRAME(frame); 1075 | RETURN_BOOL(success); 1076 | } 1077 | /* }}} */ 1078 | 1079 | /* {{{ proto boolean Stomp::begin(string transactionId [, array headers ]) 1080 | Start a transaction */ 1081 | PHP_FUNCTION(stomp_begin) 1082 | { 1083 | _php_stomp_transaction(INTERNAL_FUNCTION_PARAM_PASSTHRU, "BEGIN", sizeof("BEGIN") - 1); 1084 | } 1085 | /* }}} */ 1086 | 1087 | /* {{{ proto boolean Stomp::commit(string transactionId [, array headers ]) 1088 | Commit a transaction in progress */ 1089 | PHP_FUNCTION(stomp_commit) 1090 | { 1091 | _php_stomp_transaction(INTERNAL_FUNCTION_PARAM_PASSTHRU, "COMMIT", sizeof("COMMIT") - 1); 1092 | } 1093 | /* }}} */ 1094 | 1095 | /* {{{ proto boolean Stomp::abort(string transactionId [, array headers ]) 1096 | Rollback a transaction in progress */ 1097 | PHP_FUNCTION(stomp_abort) 1098 | { 1099 | _php_stomp_transaction(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ABORT", sizeof("ABORT") - 1); 1100 | } 1101 | /* }}} */ 1102 | 1103 | /* {{{ _php_stomp_acknowledgment 1104 | */ 1105 | static void _php_stomp_acknowledgment(INTERNAL_FUNCTION_PARAMETERS, char *cmd) { 1106 | zval *stomp_object = getThis(); 1107 | zval *msg, *headers = NULL; 1108 | stomp_t *stomp = NULL; 1109 | stomp_frame_t frame = {0}; 1110 | int success = 0; 1111 | 1112 | if (stomp_object) { 1113 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "z|a!", &msg, &headers) == FAILURE) { 1114 | return; 1115 | } 1116 | FETCH_STOMP_OBJECT; 1117 | } else { 1118 | zval *arg; 1119 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "rz|a!", &arg, &msg, &headers) == FAILURE) { 1120 | return; 1121 | } 1122 | FETCH_STOMP_RSRC(stomp, arg); 1123 | } 1124 | 1125 | INIT_FRAME(frame, cmd); 1126 | 1127 | if (NULL != headers) { 1128 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(headers)); 1129 | } 1130 | 1131 | if (Z_TYPE_P(msg) == IS_STRING) { 1132 | Z_TRY_ADDREF_P(msg); 1133 | zend_hash_str_add(frame.headers, "message-id", sizeof("message-id") - 1, msg); 1134 | } else if (Z_TYPE_P(msg) == IS_OBJECT && instanceof_function(Z_OBJCE_P(msg), stomp_ce_frame )) { 1135 | zval *frame_obj_prop, rv; 1136 | 1137 | frame_obj_prop = zend_read_property(stomp_ce_frame, OBJ_FOR_PROP(msg), "headers", sizeof("headers")-1, 1, &rv); 1138 | if (Z_TYPE_P(frame_obj_prop) == IS_ARRAY) { 1139 | FRAME_HEADER_FROM_HASHTABLE(frame.headers, Z_ARRVAL_P(frame_obj_prop)); 1140 | } 1141 | } else { 1142 | php_error_docref(NULL, E_WARNING, 1143 | "Expects parameter %d to be a string or a StompFrame object.", stomp_object? 2 : 3); 1144 | CLEAR_FRAME(frame); 1145 | RETURN_FALSE; 1146 | } 1147 | 1148 | if (stomp_send(stomp, &frame ) > 0) { 1149 | success = stomp_valid_receipt(stomp, &frame); 1150 | } 1151 | 1152 | CLEAR_FRAME(frame); 1153 | RETURN_BOOL(success); 1154 | } 1155 | /* }}} */ 1156 | 1157 | /* {{{ proto boolean Stomp::ack(mixed msg [, array headers]) 1158 | Acknowledge consumption of a message from a subscription using client acknowledgment */ 1159 | PHP_FUNCTION(stomp_ack) 1160 | { 1161 | _php_stomp_acknowledgment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ACK"); 1162 | } 1163 | /* }}} */ 1164 | 1165 | /* {{{ proto boolean Stomp::nack(mixed msg [, array headers]) 1166 | Negative Acknowledgment of a message from a subscription */ 1167 | PHP_FUNCTION(stomp_nack) 1168 | { 1169 | _php_stomp_acknowledgment(INTERNAL_FUNCTION_PARAM_PASSTHRU, "NACK"); 1170 | } 1171 | /* }}} */ 1172 | 1173 | /* {{{ proto string Stomp::error() 1174 | Get the last error message */ 1175 | PHP_FUNCTION(stomp_error) 1176 | { 1177 | zval *stomp_object = getThis(); 1178 | stomp_t *stomp = NULL; 1179 | if (stomp_object) { 1180 | FETCH_STOMP_OBJECT; 1181 | } else { 1182 | zval *arg; 1183 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "r", &arg) == FAILURE) { 1184 | return; 1185 | } 1186 | FETCH_STOMP_RSRC(stomp, arg); 1187 | } 1188 | 1189 | if (stomp->error) { 1190 | if (stomp->error_details) { 1191 | char *error_msg = (char *) emalloc(strlen(stomp->error) + strlen(stomp->error_details) + 10); 1192 | strcpy(error_msg, stomp->error); 1193 | strcat(error_msg, "\n\n"); 1194 | strcat(error_msg, stomp->error_details); 1195 | RETVAL_STRING(error_msg); 1196 | efree(error_msg); 1197 | } else { 1198 | RETURN_STRING(stomp->error); 1199 | } 1200 | } else { 1201 | RETURN_FALSE; 1202 | } 1203 | } 1204 | /* }}} */ 1205 | 1206 | /* {{{ proto void Stomp::setTimeout(int seconds [, int microseconds]) 1207 | Set the timeout */ 1208 | PHP_FUNCTION(stomp_set_read_timeout) 1209 | { 1210 | zval *stomp_object = getThis(); 1211 | stomp_t *stomp = NULL; 1212 | zend_long sec = 0, usec = 0; 1213 | if (stomp_object) { 1214 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "l|l", &sec, &usec) == FAILURE) { 1215 | return; 1216 | } 1217 | FETCH_STOMP_OBJECT; 1218 | } else { 1219 | zval *arg; 1220 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "rl|l", &arg, &sec, &usec) == FAILURE) { 1221 | return; 1222 | } 1223 | FETCH_STOMP_RSRC(stomp, arg); 1224 | } 1225 | 1226 | stomp->options.read_timeout_sec = sec; 1227 | stomp->options.read_timeout_usec = usec; 1228 | } 1229 | /* }}} */ 1230 | 1231 | /* {{{ proto array Stomp::getTimeout() 1232 | Get the timeout */ 1233 | PHP_FUNCTION(stomp_get_read_timeout) 1234 | { 1235 | zval *stomp_object = getThis(); 1236 | stomp_t *stomp = NULL; 1237 | if (stomp_object) { 1238 | FETCH_STOMP_OBJECT; 1239 | } else { 1240 | zval *arg; 1241 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "r", &arg) == FAILURE) { 1242 | return; 1243 | } 1244 | FETCH_STOMP_RSRC(stomp, arg); 1245 | } 1246 | 1247 | array_init(return_value); 1248 | add_assoc_long_ex(return_value, "sec", sizeof("sec") - 1, stomp->options.read_timeout_sec); 1249 | add_assoc_long_ex(return_value, "usec", sizeof("usec") - 1, stomp->options.read_timeout_usec); 1250 | } 1251 | /* }}} */ 1252 | 1253 | /* {{{ proto void StompFrame::__construct([string command [, array headers [, string body]]]) 1254 | Create StompFrame object */ 1255 | PHP_METHOD(stompframe, __construct) 1256 | { 1257 | zval *object = getThis(); 1258 | char *command = NULL, *body = NULL; 1259 | zend_long command_length = 0, body_length = -1; 1260 | zval *headers = NULL; 1261 | 1262 | if (zend_parse_parameters(ZEND_NUM_ARGS() , "|sa!s", &command, &command_length, &headers, &body, &body_length) == FAILURE) { 1263 | return; 1264 | } 1265 | 1266 | if (command_length > 0) { 1267 | zend_update_property_stringl(stomp_ce_frame, OBJ_FOR_PROP(object), "command", sizeof("command")-1, command, command_length ); 1268 | } 1269 | if (headers) { 1270 | zend_update_property(stomp_ce_frame, OBJ_FOR_PROP(object), "headers", sizeof("headers")-1, headers ); 1271 | } 1272 | if (body_length > 0) { 1273 | zend_update_property_stringl(stomp_ce_frame, OBJ_FOR_PROP(object), "body", sizeof("body")-1, body, body_length ); 1274 | } 1275 | } 1276 | /* }}} */ 1277 | 1278 | /* {{{ proto string StompException::getDetails() 1279 | Get error details */ 1280 | PHP_METHOD(stompexception, getDetails) 1281 | { 1282 | zval *object = getThis(); 1283 | zval rv, *details = zend_read_property(stomp_ce_exception, OBJ_FOR_PROP(object), "details", sizeof("details")-1, 1, &rv); 1284 | RETURN_STR(zval_get_string(details)); 1285 | } 1286 | /* }}} */ 1287 | 1288 | /* 1289 | * Local variables: 1290 | * tab-width: 4 1291 | * c-basic-offset: 4 1292 | * End: 1293 | * vim600: noet sw=4 ts=4 fdm=marker 1294 | * vim<600: noet sw=4 ts=4 1295 | */ 1296 | -------------------------------------------------------------------------------- /php_stomp.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Pierrick Charron | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef PHP_STOMP_H 18 | #define PHP_STOMP_H 19 | 20 | #include "stomp.h" 21 | 22 | typedef struct _stomp_object { 23 | stomp_t *stomp; 24 | zend_object std; 25 | } stomp_object_t; 26 | 27 | #define PHP_STOMP_EXTNAME "Stomp" 28 | #define PHP_STOMP_VERSION "2.0.4-dev" 29 | 30 | #define PHP_STOMP_RES_NAME "stomp connection" 31 | 32 | #define PHP_STOMP_CLASSNAME "Stomp" 33 | #define PHP_STOMP_FRAME_CLASSNAME "StompFrame" 34 | #define PHP_STOMP_EXCEPTION_CLASSNAME "StompException" 35 | 36 | #define PHP_STOMP_ERR_UNKNOWN "Stomp unknown error" 37 | #define PHP_STOMP_ERR_INVALID_BROKER_URI "Invalid Broker URI" 38 | #define PHP_STOMP_ERR_INVALID_BROKER_URI_SCHEME "Invalid Broker URI scheme" 39 | #define PHP_STOMP_ERR_SERVER_NOT_RESPONDING "Server is not responding" 40 | #define PHP_STOMP_ERR_EMPTY_DESTINATION "Destination can not be empty" 41 | #define PHP_STOMP_ERR_NO_CTR "Stomp constructor was not called" 42 | 43 | extern zend_module_entry stomp_module_entry; 44 | #define phpext_stomp_ptr &stomp_module_entry 45 | 46 | #ifdef PHP_WIN32 47 | #define PHP_STOMP_API __declspec(dllexport) 48 | #else 49 | #define PHP_STOMP_API 50 | #endif 51 | 52 | #ifdef ZTS 53 | #include "TSRM.h" 54 | #endif 55 | 56 | PHP_MINIT_FUNCTION(stomp); 57 | PHP_MSHUTDOWN_FUNCTION(stomp); 58 | PHP_RINIT_FUNCTION(stomp); 59 | PHP_RSHUTDOWN_FUNCTION(stomp); 60 | PHP_MINFO_FUNCTION(stomp); 61 | 62 | /* Methods declarations */ 63 | PHP_FUNCTION(stomp_version); 64 | PHP_FUNCTION(stomp_connect); 65 | PHP_FUNCTION(stomp_connect_error); 66 | PHP_FUNCTION(stomp_get_session_id); 67 | PHP_FUNCTION(stomp_close); 68 | PHP_FUNCTION(stomp_send); 69 | PHP_FUNCTION(stomp_subscribe); 70 | PHP_FUNCTION(stomp_has_frame); 71 | PHP_FUNCTION(stomp_read_frame); 72 | PHP_FUNCTION(stomp_unsubscribe); 73 | PHP_FUNCTION(stomp_begin); 74 | PHP_FUNCTION(stomp_commit); 75 | PHP_FUNCTION(stomp_abort); 76 | PHP_FUNCTION(stomp_ack); 77 | PHP_FUNCTION(stomp_nack); 78 | PHP_FUNCTION(stomp_error); 79 | PHP_FUNCTION(stomp_set_read_timeout); 80 | PHP_FUNCTION(stomp_get_read_timeout); 81 | 82 | PHP_METHOD(stompframe, __construct); 83 | 84 | PHP_METHOD(stompexception, getDetails); 85 | 86 | ZEND_BEGIN_MODULE_GLOBALS(stomp) 87 | /* INI */ 88 | char *default_broker; 89 | long read_timeout_sec; 90 | long read_timeout_usec; 91 | long connection_timeout_sec; 92 | long connection_timeout_usec; 93 | char *default_username; 94 | char *default_password; 95 | 96 | /* Others */ 97 | long error_no; 98 | char *error_msg; 99 | ZEND_END_MODULE_GLOBALS(stomp) 100 | 101 | #ifdef ZTS 102 | #define STOMP_G(v) TSRMG(stomp_globals_id, zend_stomp_globals *, v) 103 | #else 104 | #define STOMP_G(v) (stomp_globals.v) 105 | #endif 106 | 107 | #endif /* PHP_STOMP_H */ 108 | 109 | 110 | /* 111 | * Local variables: 112 | * tab-width: 4 113 | * c-basic-offset: 4 114 | * End: 115 | * vim600: noet sw=4 ts=4 fdm=marker 116 | * vim<600: noet sw=4 ts=4 117 | */ 118 | -------------------------------------------------------------------------------- /stomp.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Pierrick Charron | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifdef HAVE_CONFIG_H 18 | #include "config.h" 19 | #endif 20 | 21 | #include "php.h" 22 | #include "Zend/zend_smart_str.h" 23 | #include "stomp.h" 24 | #include "php_stomp.h" 25 | #ifdef HAVE_NETINET_IN_H 26 | #include 27 | #endif 28 | #define RETURN_READ_FRAME_FAIL { stomp_free_frame(f); return NULL; } 29 | 30 | ZEND_EXTERN_MODULE_GLOBALS(stomp); 31 | extern zend_class_entry *stomp_ce_exception; 32 | 33 | /* {{{ DEBUG */ 34 | #if PHP_DEBUG 35 | static void print_stomp_frame(stomp_frame_t *frame) { 36 | php_printf("------ START FRAME ------\n"); 37 | php_printf("%s\n", frame->command); 38 | /* Headers */ 39 | if (frame->headers) { 40 | zend_string *key; 41 | zval *value; 42 | ZEND_HASH_FOREACH_STR_KEY_VAL(frame->headers, key, value) { 43 | if (!key) { 44 | break; 45 | } 46 | php_printf("%s:%s\n", ZSTR_VAL(key), Z_STRVAL_P(value)); 47 | } ZEND_HASH_FOREACH_END(); 48 | } 49 | php_printf("\n%s\n", frame->body); 50 | php_printf("------ END FRAME ------\n"); 51 | } 52 | #endif 53 | /* }}} */ 54 | 55 | /* {{{ stomp_init 56 | */ 57 | stomp_t *stomp_init() 58 | { 59 | /* Memory allocation */ 60 | stomp_t *stomp = (stomp_t *) emalloc(sizeof(stomp_t)); 61 | memset(stomp, 0, sizeof(*stomp)); 62 | 63 | /* Define all values */ 64 | stomp->host = NULL; 65 | stomp->port = 0; 66 | stomp->status = 0; 67 | stomp->error = NULL; 68 | stomp->error_details = NULL; 69 | stomp->errnum = 0; 70 | stomp->session = NULL; 71 | stomp->options.connect_timeout_sec = 2; 72 | stomp->options.connect_timeout_usec = 0; 73 | stomp->options.read_timeout_sec = 2; 74 | stomp->options.read_timeout_usec = 0; 75 | 76 | #if HAVE_STOMP_SSL 77 | stomp->options.use_ssl = 0; 78 | stomp->ssl_handle = NULL; 79 | #endif 80 | 81 | stomp->frame_stack = NULL; 82 | stomp->read_buffer.size = 0; 83 | return stomp; 84 | } 85 | /* }}} */ 86 | 87 | /* {{{ stomp_frame_stack_push 88 | */ 89 | static void stomp_frame_stack_push(stomp_frame_stack_t **stack, stomp_frame_t *frame) 90 | { 91 | stomp_frame_stack_t *cell = (stomp_frame_stack_t *) emalloc(sizeof(stomp_frame_stack_t)); 92 | cell->frame = frame; 93 | cell->next = NULL; 94 | 95 | if (!*stack) { 96 | *stack = cell; 97 | } else { 98 | stomp_frame_stack_t *cursor = *stack; 99 | while (cursor->next != NULL) cursor = cursor->next; 100 | cursor->next = cell; 101 | } 102 | } 103 | /* }}} */ 104 | 105 | /* {{{ stomp_frame_stack_shift 106 | */ 107 | static stomp_frame_t *stomp_frame_stack_shift(stomp_frame_stack_t **stack) { 108 | stomp_frame_t *frame = NULL; 109 | if (*stack) { 110 | stomp_frame_stack_t *cell = *stack; 111 | *stack = cell->next; 112 | frame = cell->frame; 113 | efree(cell); 114 | } 115 | return frame; 116 | } 117 | /* }}} */ 118 | 119 | /* {{{ stomp_frame_stack_clear 120 | */ 121 | static void stomp_frame_stack_clear(stomp_frame_stack_t **stack) { 122 | stomp_frame_t *frame = NULL; 123 | while ((frame = stomp_frame_stack_shift(stack))) efree(frame); 124 | } 125 | /* }}} */ 126 | 127 | /* {{{ stomp_set_error 128 | */ 129 | void stomp_set_error(stomp_t *stomp, const char *error, int errnum, const char *fmt, ...) 130 | { 131 | va_list ap; 132 | int len; 133 | 134 | if (stomp->error != NULL) { 135 | efree(stomp->error); 136 | stomp->error = NULL; 137 | } 138 | if (stomp->error_details != NULL) { 139 | efree(stomp->error_details); 140 | stomp->error_details = NULL; 141 | } 142 | stomp->errnum = errnum; 143 | if (error != NULL) { 144 | stomp->error = estrdup(error); 145 | } 146 | if (fmt != NULL) { 147 | stomp->error_details = emalloc(STOMP_BUFSIZE); 148 | if (stomp->error_details == NULL) { 149 | return; /* Nothing else can be done */ 150 | } 151 | va_start(ap, fmt); 152 | /* 153 | * Would've been better to call vasprintf(), but that 154 | * function is missing on some platforms... 155 | */ 156 | len = vsnprintf(stomp->error_details, STOMP_BUFSIZE, fmt, ap); 157 | va_end(ap); 158 | if (len < STOMP_BUFSIZE) { 159 | stomp->error_details = erealloc(stomp->error_details, len+1); 160 | } 161 | } 162 | } 163 | /* }}} */ 164 | 165 | /* {{{ stomp_writable 166 | */ 167 | int stomp_writable(stomp_t *stomp) 168 | { 169 | int n; 170 | 171 | n = php_pollfd_for_ms(stomp->fd, POLLOUT, 1000); 172 | if (n != POLLOUT) { 173 | #ifndef PHP_WIN32 174 | if (n == 0) { 175 | errno = ETIMEDOUT; 176 | } 177 | #endif 178 | return 0; 179 | } 180 | 181 | return 1; 182 | } 183 | /* }}} */ 184 | 185 | /* {{{ stomp_connect 186 | */ 187 | int stomp_connect(stomp_t *stomp, const char *host, unsigned short port) 188 | { 189 | char error[1024]; 190 | socklen_t size; 191 | struct timeval tv; 192 | int flag = 1; 193 | 194 | if (stomp->host != NULL) { 195 | efree(stomp->host); 196 | } 197 | stomp->host = (char *) emalloc(strlen(host) + 1); 198 | memcpy(stomp->host, host, strlen(host)); 199 | stomp->host[strlen(host)] = '\0'; 200 | 201 | stomp->port = port; 202 | 203 | tv.tv_sec = stomp->options.connect_timeout_sec; 204 | tv.tv_usec = stomp->options.connect_timeout_usec; 205 | 206 | stomp->fd = php_network_connect_socket_to_host(stomp->host, stomp->port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0, 0); 207 | if (stomp->fd == -1) { 208 | snprintf(error, sizeof(error), "Unable to connect to %s:%d", stomp->host, stomp->port); 209 | stomp_set_error(stomp, error, errno, "%s", strerror(errno)); 210 | return 0; 211 | } 212 | 213 | #ifdef HAVE_NETINET_IN_H 214 | setsockopt(stomp->fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); 215 | #endif 216 | 217 | size = sizeof(stomp->localaddr); 218 | memset(&stomp->localaddr, 0, size); 219 | if (getsockname(stomp->fd, (struct sockaddr*) &stomp->localaddr, &size) == -1) { 220 | snprintf(error, sizeof(error), "getsockname failed: %s (%d)", strerror(errno), errno); 221 | stomp_set_error(stomp, error, errno, NULL); 222 | return 0; 223 | } 224 | 225 | if (stomp_writable(stomp)) { 226 | #if HAVE_STOMP_SSL 227 | if (stomp->options.use_ssl) { 228 | SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method()); 229 | int ret; 230 | 231 | if (NULL == ctx) { 232 | stomp_set_error(stomp, "failed to create the SSL context", 0, NULL); 233 | return 0; 234 | } 235 | 236 | SSL_CTX_set_options(ctx, SSL_OP_ALL); 237 | 238 | stomp->ssl_handle = SSL_new(ctx); 239 | if (stomp->ssl_handle == NULL) { 240 | stomp_set_error(stomp, "failed to create the SSL handle", 0, NULL); 241 | SSL_CTX_free(ctx); 242 | return 0; 243 | } 244 | 245 | SSL_set_fd(stomp->ssl_handle, stomp->fd); 246 | 247 | if ((ret = SSL_connect(stomp->ssl_handle)) <= 0) { 248 | stomp_set_error(stomp, "SSL/TLS handshake failed", 0, "SSL error %d", SSL_get_error(stomp->ssl_handle, ret)); 249 | SSL_shutdown(stomp->ssl_handle); 250 | return 0; 251 | } 252 | } 253 | #endif 254 | return 1; 255 | } else { 256 | snprintf(error, sizeof(error), "Unable to connect to %s:%d", stomp->host, stomp->port); 257 | stomp_set_error(stomp, error, errno, "%s", strerror(errno)); 258 | return 0; 259 | } 260 | } 261 | /* }}} */ 262 | 263 | /* {{{ stomp_close 264 | */ 265 | void stomp_close(stomp_t *stomp) 266 | { 267 | if (NULL == stomp) { 268 | return; 269 | } 270 | 271 | if (stomp->fd != -1) { 272 | #if HAVE_STOMP_SSL 273 | if(stomp->ssl_handle) { 274 | SSL_shutdown(stomp->ssl_handle); 275 | } 276 | #endif 277 | closesocket(stomp->fd); 278 | } 279 | if (stomp->host) { 280 | efree(stomp->host); 281 | } 282 | if (stomp->session) { 283 | efree(stomp->session); 284 | } 285 | if (stomp->error) { 286 | efree(stomp->error); 287 | } 288 | if (stomp->error_details) { 289 | efree(stomp->error_details); 290 | } 291 | stomp_frame_stack_clear(&stomp->frame_stack); 292 | efree(stomp); 293 | } 294 | /* }}} */ 295 | 296 | /* {{{ stomp_send 297 | */ 298 | int stomp_send(stomp_t *stomp, stomp_frame_t *frame) 299 | { 300 | smart_str buf = {0}; 301 | 302 | /* Command */ 303 | smart_str_appends(&buf, frame->command); 304 | smart_str_appendc(&buf, '\n'); 305 | 306 | /* Headers */ 307 | if (frame->headers) { 308 | zend_string *key; 309 | zval *value; 310 | ZEND_HASH_FOREACH_STR_KEY_VAL(frame->headers, key, value) { 311 | smart_str_appends(&buf, ZSTR_VAL(key)); 312 | smart_str_appendc(&buf, ':'); 313 | smart_str_appends(&buf, Z_STRVAL_P(value)); 314 | smart_str_appendc(&buf, '\n'); 315 | } ZEND_HASH_FOREACH_END(); 316 | } 317 | 318 | if (frame->body_length > 0) { 319 | smart_str_appendl(&buf, "content-length:", sizeof("content-length:") - 1); 320 | smart_str_append_long(&buf, frame->body_length); 321 | smart_str_appendc(&buf, '\n'); 322 | } 323 | 324 | smart_str_appendc(&buf, '\n'); 325 | 326 | if (frame->body > 0) { 327 | smart_str_appendl(&buf, frame->body, frame->body_length); 328 | } 329 | 330 | smart_str_appendl(&buf, "\0", sizeof("\0")-1); 331 | 332 | if (!stomp_writable(stomp)) { 333 | smart_str_free(&buf); 334 | stomp_set_error(stomp, "Unable to send data", errno, "%s", strerror(errno)); 335 | return 0; 336 | } 337 | 338 | #ifdef HAVE_STOMP_SSL 339 | if (stomp->options.use_ssl) { 340 | int ret; 341 | if (-1 == (ret = SSL_write(stomp->ssl_handle, ZSTR_VAL(buf.s), ZSTR_LEN(buf.s)))) { 342 | smart_str_free(&buf); 343 | stomp_set_error(stomp, "Unable to send data", errno, "SSL error %d", SSL_get_error(stomp->ssl_handle, ret)); 344 | return 0; 345 | } 346 | } else { 347 | #endif 348 | if (-1 == send(stomp->fd, ZSTR_VAL(buf.s), ZSTR_LEN(buf.s), 0)) { 349 | smart_str_free(&buf); 350 | stomp_set_error(stomp, "Unable to send data", errno, "%s", strerror(errno)); 351 | return 0; 352 | } 353 | #ifdef HAVE_STOMP_SSL 354 | } 355 | #endif 356 | 357 | smart_str_free(&buf); 358 | 359 | return 1; 360 | } 361 | /* }}} */ 362 | 363 | /* {{{ stomp_recv 364 | */ 365 | static int _stomp_recv(stomp_t *stomp, char *msg, const size_t length) 366 | { 367 | int len; 368 | 369 | stomp_select(stomp); 370 | 371 | #if HAVE_STOMP_SSL 372 | if(stomp->options.use_ssl) { 373 | len = SSL_read(stomp->ssl_handle, msg, length); 374 | } else { 375 | #endif 376 | len = recv(stomp->fd, msg, length, 0); 377 | #if HAVE_STOMP_SSL 378 | } 379 | #endif 380 | 381 | if (len == -1) { 382 | #if HAVE_STOMP_SSL 383 | if (stomp->options.use_ssl) { 384 | stomp_set_error(stomp, "Error reading from socket", errno, "%s. (SSL in use)", strerror(errno)); 385 | } else { 386 | #endif 387 | stomp_set_error(stomp, "Error reading from socket", errno, "%s. (SSL not in use)", strerror(errno)); 388 | #if HAVE_STOMP_SSL 389 | } 390 | #endif 391 | stomp->status = -1; 392 | } else if (len == 0) { 393 | stomp_set_error(stomp, "Sender closed connection unexpectedly", 0, NULL); 394 | stomp->status = -1; 395 | } 396 | 397 | return len; 398 | } 399 | 400 | int stomp_recv(stomp_t *stomp, char *msg, const size_t length) 401 | { 402 | if (stomp->read_buffer.size == 0) { 403 | if (length >= STOMP_BUFSIZE) { 404 | return _stomp_recv(stomp, msg, length); 405 | } else { 406 | size_t recv_size = _stomp_recv(stomp, stomp->read_buffer.buf, STOMP_BUFSIZE); 407 | if (recv_size <= length) { 408 | memcpy(msg, stomp->read_buffer.buf, recv_size); 409 | return recv_size; 410 | } else { 411 | memcpy(msg, stomp->read_buffer.buf, length); 412 | stomp->read_buffer.pos = stomp->read_buffer.buf + length; 413 | stomp->read_buffer.size = recv_size - length; 414 | return length; 415 | } 416 | } 417 | } else if (stomp->read_buffer.size >= length) { 418 | memcpy(msg, stomp->read_buffer.pos, length); 419 | stomp->read_buffer.pos += length; 420 | stomp->read_buffer.size -= length; 421 | return length; 422 | } else { 423 | int len = stomp->read_buffer.size; 424 | memcpy(msg, stomp->read_buffer.pos, stomp->read_buffer.size); 425 | stomp->read_buffer.size = 0; 426 | if (stomp_select_ex(stomp, 0, 0)) { 427 | return len + stomp_recv(stomp, msg + len, length - len); 428 | } else { 429 | return len; 430 | } 431 | } 432 | } 433 | /* }}} */ 434 | 435 | /* {{{ _stomp_read_until 436 | */ 437 | static size_t _stomp_read_until(stomp_t *stomp, char **data, const char delimiter) 438 | { 439 | size_t length = 0; 440 | size_t bufsize = STOMP_BUFSIZE; 441 | char *buffer = (char *) emalloc(STOMP_BUFSIZE); 442 | 443 | while (1) { 444 | unsigned int i, found; 445 | char *c; 446 | found = 0; 447 | 448 | // First populate the buffer 449 | if (stomp->read_buffer.size == 0) { 450 | stomp->read_buffer.size = _stomp_recv(stomp, stomp->read_buffer.buf, STOMP_BUFSIZE); 451 | 452 | if (stomp->status == -1) { 453 | length = 0; 454 | break; 455 | } 456 | 457 | stomp->read_buffer.pos = stomp->read_buffer.buf; 458 | } 459 | 460 | // Then search the delimiter 461 | c = stomp->read_buffer.pos; 462 | for (i = 1; i <= stomp->read_buffer.size ; i++) { 463 | if (*c == delimiter) { 464 | found = 1; 465 | break; 466 | } else { 467 | c++; 468 | } 469 | } 470 | if (!found) i--; 471 | 472 | // Make sure we have enough place in the buffer 473 | if ((i+length) >= bufsize) { 474 | buffer = (char *) erealloc(buffer, bufsize + STOMP_BUFSIZE); 475 | bufsize += STOMP_BUFSIZE; 476 | } 477 | 478 | // Copy and update the buffer 479 | memcpy(buffer + length, stomp->read_buffer.pos, i); 480 | length += i; 481 | stomp->read_buffer.pos += i; 482 | stomp->read_buffer.size -= i; 483 | 484 | if (found) { 485 | break; 486 | } 487 | } 488 | 489 | if (length) { 490 | *data = buffer; 491 | } else { 492 | efree(buffer); 493 | *data = NULL; 494 | } 495 | 496 | return length; 497 | } 498 | /* }}} */ 499 | 500 | /* {{{ stomp_read_buffer 501 | */ 502 | static size_t stomp_read_buffer(stomp_t *stomp, char **data) 503 | { 504 | size_t length = _stomp_read_until(stomp, data, 0); 505 | if (stomp_select_ex(stomp, 0, 0)) { 506 | char endline[1]; 507 | if (1 != stomp_recv(stomp, endline, 1) && '\n' != endline[0]) { 508 | if (*data) { 509 | efree(*data); 510 | *data = NULL; 511 | } 512 | return 0; 513 | } 514 | } 515 | if (length > 1) { 516 | length --; 517 | } else if (length) { 518 | efree(*data); 519 | *data = NULL; 520 | length = 0; 521 | } 522 | return length; 523 | } 524 | /* }}} */ 525 | 526 | /* {{{ stomp_read_line 527 | */ 528 | static int stomp_read_line(stomp_t *stomp, char **data) 529 | { 530 | size_t length = _stomp_read_until(stomp, data, '\n'); 531 | if (length > 1) { 532 | (*data)[length - 1] = 0; 533 | length--; 534 | } else if (length) { 535 | efree(*data); 536 | *data = NULL; 537 | length = 0; 538 | } 539 | return length; 540 | } 541 | /* }}} */ 542 | 543 | /* {{{ stomp_free_frame 544 | */ 545 | void stomp_free_frame(stomp_frame_t *frame) 546 | { 547 | if (frame) { 548 | if (frame->command) { 549 | efree(frame->command); 550 | } 551 | if (frame->body) { 552 | efree(frame->body); 553 | } 554 | if (frame->headers) { 555 | zend_hash_destroy(frame->headers); 556 | FREE_HASHTABLE(frame->headers); 557 | } 558 | efree(frame); 559 | } 560 | } 561 | /* }}} */ 562 | 563 | /* {{{ stomp_read_frame 564 | */ 565 | stomp_frame_t *stomp_read_frame_ex(stomp_t *stomp, int use_stack) 566 | { 567 | stomp_frame_t *f = NULL; 568 | char *cmd = NULL; 569 | zval *length_str = NULL; 570 | int length = 0; 571 | 572 | if (use_stack && stomp->frame_stack) { 573 | return stomp_frame_stack_shift(&stomp->frame_stack); 574 | } 575 | 576 | if (!stomp_select(stomp)) { 577 | return NULL; 578 | } 579 | 580 | INIT_STOMP_FRAME(f); 581 | 582 | if (NULL == f) { 583 | return NULL; 584 | } 585 | 586 | /* Parse the command */ 587 | length = stomp_read_line(stomp, &cmd); 588 | if (length < 1) { 589 | RETURN_READ_FRAME_FAIL; 590 | } 591 | 592 | f->command = cmd; 593 | f->command_length = length; 594 | 595 | /* Parse the header */ 596 | while (1) { 597 | char *p = NULL; 598 | length = stomp_read_line(stomp, &p); 599 | 600 | if (length < 0) { 601 | RETURN_READ_FRAME_FAIL; 602 | } 603 | 604 | if (0 == length) { 605 | break; 606 | } else { 607 | char *p2 = NULL; 608 | char *key; 609 | zval value; 610 | 611 | p2 = strstr(p,":"); 612 | 613 | if (p2 == NULL) { 614 | efree(p); 615 | RETURN_READ_FRAME_FAIL; 616 | } 617 | 618 | /* Null terminate the key */ 619 | *p2 = 0; 620 | key = p; 621 | 622 | /* The rest is the value. */ 623 | ZVAL_STRING(&value, p2 + 1); 624 | 625 | /* Insert key/value into hash table. */ 626 | zend_hash_str_add(f->headers, key, p2 - key, &value); 627 | efree(p); 628 | } 629 | } 630 | 631 | /* Check for the content length */ 632 | if ((length_str = zend_hash_str_find(f->headers, ZEND_STRL("content-length"))) != NULL) { 633 | int recv_size = 0; 634 | char endbuffer[2]; 635 | 636 | f->body_length = atoi(Z_STRVAL_P(length_str)); 637 | f->body = (char *) emalloc(f->body_length); 638 | 639 | while (recv_size != f->body_length) { 640 | int l = stomp_recv(stomp, f->body + recv_size, f->body_length - recv_size); 641 | if (-1 == l) { 642 | RETURN_READ_FRAME_FAIL; 643 | } else { 644 | recv_size += l; 645 | } 646 | } 647 | 648 | length = stomp_recv(stomp, endbuffer, 2); 649 | if (endbuffer[0] != '\0' || ((2 == length) && (endbuffer[1] != '\n'))) { 650 | RETURN_READ_FRAME_FAIL; 651 | } 652 | 653 | } else { 654 | f->body_length = stomp_read_buffer(stomp, &f->body); 655 | } 656 | 657 | return f; 658 | } 659 | /* }}} */ 660 | 661 | /* {{{ stomp_valid_receipt 662 | */ 663 | int stomp_valid_receipt(stomp_t *stomp, stomp_frame_t *frame) { 664 | int success = 1; 665 | zval *receipt; 666 | 667 | if ((receipt = zend_hash_str_find(frame->headers, ZEND_STRL("receipt"))) != NULL) { 668 | success = 0; 669 | while (1) { 670 | stomp_frame_t *res = stomp_read_frame_ex(stomp, 0); 671 | if (res) { 672 | if (0 == strncmp("RECEIPT", res->command, sizeof("RECEIPT") - 1)) { 673 | zval *receipt_id; 674 | if ((receipt_id = zend_hash_str_find(res->headers, ZEND_STRL("receipt-id"))) != NULL 675 | && zend_string_equals(Z_STR_P(receipt), Z_STR_P(receipt_id))) { 676 | success = 1; 677 | } else { 678 | stomp_set_error(stomp, "Invalid receipt", 0, "%s", receipt_id); 679 | } 680 | stomp_free_frame(res); 681 | return success; 682 | } else if (0 == strncmp("ERROR", res->command, sizeof("ERROR") - 1)) { 683 | zval *error_msg; 684 | if ((error_msg = zend_hash_str_find(res->headers, ZEND_STRL("message"))) != NULL) { 685 | stomp_set_error(stomp, Z_STRVAL_P(error_msg), 0, "%s", res->body); 686 | } 687 | stomp_free_frame(res); 688 | return success; 689 | } else { 690 | stomp_frame_stack_push(&stomp->frame_stack, res); 691 | } 692 | } else { 693 | return success; 694 | } 695 | } 696 | } 697 | return success; 698 | } 699 | /* }}} */ 700 | 701 | /* {{{ stomp_select 702 | */ 703 | int stomp_select_ex(stomp_t *stomp, const long int sec, const long int usec) 704 | { 705 | int n; 706 | struct timeval tv; 707 | 708 | if (stomp->read_buffer.size || stomp->frame_stack) { 709 | return 1; 710 | } 711 | tv.tv_sec = sec; 712 | tv.tv_usec = usec; 713 | 714 | n = php_pollfd_for(stomp->fd, PHP_POLLREADABLE, &tv); 715 | if (n < 1) { 716 | #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK)) 717 | if (n == 0) { 718 | errno = ETIMEDOUT; 719 | } 720 | #endif 721 | return 0; 722 | } 723 | return 1; 724 | } 725 | /* }}} */ 726 | 727 | /* 728 | * Local variables: 729 | * tab-width: 4 730 | * c-basic-offset: 4 731 | * End: 732 | * vim600: noet sw=4 ts=4 fdm=marker 733 | * vim<600: noet sw=4 ts=4 734 | */ 735 | -------------------------------------------------------------------------------- /stomp.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) The PHP Group | 4 | +----------------------------------------------------------------------+ 5 | | This source file is subject to version 3.01 of the PHP license, | 6 | | that is bundled with this package in the file LICENSE, and is | 7 | | available through the world-wide-web at the following url: | 8 | | http://www.php.net/license/3_01.txt | 9 | | If you did not receive a copy of the PHP license and are unable to | 10 | | obtain it through the world-wide-web, please send a note to | 11 | | license@php.net so we can mail you a copy immediately. | 12 | +----------------------------------------------------------------------+ 13 | | Author: Pierrick Charron | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef _STOMP_H_ 18 | #define _STOMP_H_ 19 | 20 | #include "php_network.h" 21 | 22 | #if HAVE_STOMP_SSL 23 | #include 24 | #endif 25 | 26 | #define STOMP_BUFSIZE 4096 27 | 28 | #define INIT_STOMP_FRAME(f) \ 29 | f = (stomp_frame_t *) emalloc(sizeof(stomp_frame_t)); \ 30 | f->command = NULL; f->body = NULL; \ 31 | ALLOC_HASHTABLE(f->headers); \ 32 | zend_hash_init(f->headers, 0, NULL, ZVAL_PTR_DTOR, 0); 33 | 34 | typedef struct _stomp_options { 35 | long connect_timeout_sec; 36 | long connect_timeout_usec; 37 | long read_timeout_sec; 38 | long read_timeout_usec; 39 | #if HAVE_STOMP_SSL 40 | int use_ssl; 41 | #endif 42 | } stomp_options_t; 43 | 44 | typedef struct _stomp_frame { 45 | char *command; 46 | int command_length; 47 | HashTable *headers; 48 | char *body; 49 | int body_length; 50 | } stomp_frame_t; 51 | 52 | typedef struct _stomp_frame_stack { 53 | stomp_frame_t *frame; 54 | struct _stomp_frame_stack *next; 55 | } stomp_frame_stack_t; 56 | 57 | typedef struct _stomp { 58 | php_socket_t fd; 59 | php_sockaddr_storage localaddr; 60 | stomp_options_t options; 61 | char *host; 62 | unsigned short port; 63 | int status; 64 | char *error; 65 | int errnum; 66 | char *error_details; 67 | char *session; 68 | #if HAVE_STOMP_SSL 69 | SSL *ssl_handle; 70 | #endif 71 | stomp_frame_stack_t *frame_stack; 72 | struct { 73 | size_t size; 74 | char buf[STOMP_BUFSIZE]; 75 | char *pos; 76 | } read_buffer; 77 | } stomp_t; 78 | 79 | stomp_t *stomp_init(); 80 | int stomp_connect(stomp_t *stomp, const char *host, unsigned short port); 81 | void stomp_close(stomp_t *stomp); 82 | int stomp_send(stomp_t *connection, stomp_frame_t *frame); 83 | stomp_frame_t *stomp_read_frame_ex(stomp_t *connection, int use_stack); 84 | int stomp_valid_receipt(stomp_t *connection, stomp_frame_t *frame); 85 | int stomp_select_ex(stomp_t *connection, const long int sec, const long int usec); 86 | void stomp_set_error(stomp_t *stomp, const char *error, int errnum, const char *fmt, ...) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 4, 0); 87 | void stomp_free_frame(stomp_frame_t *frame); 88 | 89 | #define stomp_select(s) stomp_select_ex(s, s->options.read_timeout_sec, s->options.read_timeout_usec) 90 | #define stomp_read_frame(c) stomp_read_frame_ex(c, 1) 91 | #endif /* _STOMP_H_ */ 92 | 93 | /* 94 | * Local variables: 95 | * tab-width: 4 96 | * c-basic-offset: 4 97 | * End: 98 | * vim600: noet sw=4 ts=4 fdm=marker 99 | * vim<600: noet sw=4 ts=4 100 | */ 101 | -------------------------------------------------------------------------------- /tests/001-stomp.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for stomp presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECT-- 10 | stomp extension is available 11 | -------------------------------------------------------------------------------- /tests/002-version.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_version() 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 9 | --EXPECTF-- 10 | %d.%d.%s 11 | -------------------------------------------------------------------------------- /tests/003-connect/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_connect() - URI validation 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 12 | --EXPECT-- 13 | NULL 14 | string(18) "Invalid Broker URI" 15 | NULL 16 | string(18) "Invalid Broker URI" 17 | NULL 18 | string(18) "Invalid Broker URI" 19 | NULL 20 | string(25) "Invalid Broker URI scheme" 21 | -------------------------------------------------------------------------------- /tests/003-connect/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_connect() - Test connection 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 14 | --EXPECTF-- 15 | resource(%d) of type (stomp connection) 16 | NULL 17 | -------------------------------------------------------------------------------- /tests/003-connect/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_connect() - Test error on CONNECT 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | 15 | --EXPECTF-- 16 | string(14) "StompException" 17 | -------------------------------------------------------------------------------- /tests/004-getSessionId/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_get_session_id() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 14 | --EXPECTF-- 15 | string(%d) "%s" 16 | -------------------------------------------------------------------------------- /tests/005-close/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_close() - tests parameters 3 | --SKIPIF-- 4 | 7 | --FILE-- 8 | getMessage() . PHP_EOL; 13 | } 14 | ?> 15 | --EXPECTF-- 16 | %stomp_close()%s1%s null %s 17 | -------------------------------------------------------------------------------- /tests/005-close/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_close() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 15 | --EXPECT-- 16 | success 17 | closed 18 | -------------------------------------------------------------------------------- /tests/006-send/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_send() - tests parameters 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 21 | --EXPECTF-- 22 | Warning: stomp_send(): Destination can not be empty in %s on line %d 23 | 24 | Warning: stomp_send(): Expects parameter %d to be a string or a StompFrame object. in %s on line %d 25 | bool(true) 26 | bool(true) 27 | bool(true) 28 | -------------------------------------------------------------------------------- /tests/006-send/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp::send() - tests parameters 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | send('', array()); 16 | $s->send('/queue/test-06', array()); 17 | var_dump($s->send('/queue/test-06', '')); 18 | var_dump($s->send('/queue/test-06', 'A realMessage')); 19 | var_dump($s->send('/queue/test-06', 'بياريك شارون')); 20 | 21 | ?> 22 | --EXPECTF-- 23 | Warning: Stomp::send(): Destination can not be empty in %s on line %d 24 | 25 | Warning: Stomp::send(): Expects parameter %d to be a string or a StompFrame object. in %s on line %d 26 | bool(true) 27 | bool(true) 28 | bool(true) 29 | -------------------------------------------------------------------------------- /tests/006-send/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp::send() - test send with receipt 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | send('/queue/test-06', 'A real message', array('receipt' => 'message-12345'))); 13 | ?> 14 | --EXPECTF-- 15 | bool(true) 16 | -------------------------------------------------------------------------------- /tests/007-subscribe/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test Stomp::subscribe() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | subscribe('', array()); 14 | $s->subscribe('/queue/test', 'string'); 15 | ?> 16 | --EXPECTF-- 17 | Warning: Stomp::subscribe(): Destination can not be empty in %s007-subscribe%c001.php on line %d 18 | 19 | Fatal error: Uncaught TypeError: %s, string given in %s007-subscribe%c001.php:%d 20 | Stack trace: 21 | #0 %s001.php(%d): Stomp->subscribe('/queue/test', 'string') 22 | #1 {main} 23 | thrown in %s007-subscribe%c001.php on line %d 24 | -------------------------------------------------------------------------------- /tests/008-unsubscribe/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test Stomp::unsubscribe() 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | unsubscribe('', array()); 15 | $s->unsubscribe('/queue/test', 'string'); 16 | ?> 17 | --EXPECTF-- 18 | Warning: Stomp::unsubscribe(): Destination can not be empty in %s008-unsubscribe%c001.php on line %d 19 | 20 | Fatal error: Uncaught TypeError: %s2%s string given in %s008-unsubscribe%c001.php:%d 21 | Stack trace: 22 | #0 %s(%d): Stomp->unsubscribe('/queue/test', 'string') 23 | #1 {main} 24 | thrown in %s on line %d 25 | -------------------------------------------------------------------------------- /tests/009-readFrame/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp::readFrame() - tests functionnality and parameters 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | send('/queue/test-09', 'A test Message'); 15 | $s->subscribe('/queue/test-09', array('ack' => 'auto')); 16 | var_dump($s->readFrame()->body); 17 | var_dump($s->readFrame()); 18 | 19 | ?> 20 | --EXPECTF-- 21 | string(14) "A test Message" 22 | bool(false) 23 | -------------------------------------------------------------------------------- /tests/009-readFrame/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_read_frame() - test functionnality and parameters 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 'auto')); 15 | $result = stomp_read_frame($link); 16 | var_dump($result['body']); 17 | var_dump(stomp_read_frame($link)); 18 | 19 | ?> 20 | --EXPECTF-- 21 | string(14) "A test Message" 22 | bool(false) 23 | -------------------------------------------------------------------------------- /tests/009-readFrame/003.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp::readFrame() - custom frame class 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | send('/queue/test-09', 'A test Message'); 23 | $s->subscribe('/queue/test-09', array('ack' => 'auto')); 24 | $frame = $s->readFrame('customFrame'); 25 | var_dump(get_class($frame), $frame->body); 26 | ?> 27 | --EXPECT-- 28 | string(11) "customFrame" 29 | string(14) "A test Message" 30 | -------------------------------------------------------------------------------- /tests/009-readFrame/004.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp::readFrame() - Test the body binary safety 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | send('/queue/test-09', "A test Message\0Foo"); 15 | $s->subscribe('/queue/test-09', array('ack' => 'auto')); 16 | var_dump($s->readFrame()->body); 17 | 18 | ?> 19 | --EXPECTF-- 20 | string(18) "A test MessageFoo" 21 | -------------------------------------------------------------------------------- /tests/009-readFrame/005.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_read_frame() - Test the body binary safety 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | 'auto')); 16 | $result = stomp_read_frame($link); 17 | var_dump($result['body']); 18 | 19 | ?> 20 | --EXPECTF-- 21 | string(18) "A test MessageFoo" 22 | -------------------------------------------------------------------------------- /tests/009-readFrame/006.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp::readFrame() - test frame stack 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | subscribe('/queue/test-buffer', array('ack' => 'auto'))); 16 | var_dump($s->send('/queue/test-buffer', "Message1", array('receipt' => 'msg-1'))); 17 | var_dump($s->send('/queue/test-buffer', "Message2", array('receipt' => 'msg-2'))); 18 | var_dump($s->send('/queue/test-buffer', "Message3", array('receipt' => 'msg-3'))); 19 | var_dump($s->readFrame()->body); 20 | var_dump($s->readFrame()->body); 21 | var_dump($s->readFrame()->body); 22 | ?> 23 | --EXPECTF-- 24 | bool(true) 25 | bool(true) 26 | bool(true) 27 | bool(true) 28 | string(8) "Message1" 29 | string(8) "Message2" 30 | string(8) "Message3" 31 | -------------------------------------------------------------------------------- /tests/010-timeout/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test Stomp::getReadTimout() and Stomp::setReadTimeout() - tests functionnality and parameters 3 | --INI-- 4 | stomp.default_read_timeout_sec=5 5 | stomp.default_read_timeout_usec=5 6 | --SKIPIF-- 7 | 11 | --FILE-- 12 | getReadTimeout()); 17 | 18 | // Set read timout with an integer as seconds 19 | var_dump($s->setReadTimeout(10)); 20 | // Second test, read supposed to return 10.0 21 | var_dump($s->getReadTimeout()); 22 | 23 | // Set read timout with an integer as seconds 24 | var_dump($s->setReadTimeout(10, 5)); 25 | // Third test, read supposed to return 10.5 26 | var_dump($s->getReadTimeout()); 27 | 28 | try { 29 | // Set read timout with the first param as a string, supposed to trigger a warning/exception 30 | var_dump($s->setReadTimeout('')); 31 | } catch (TypeError $e) { 32 | echo $e->getMessage() . PHP_EOL; 33 | } 34 | // Fourth test, read supposed to get the last value set : 10.5 35 | var_dump($s->getReadTimeout()); 36 | 37 | try { 38 | // Set read timout with the second param as a string, supposed to trigger a warning/exception 39 | var_dump($s->setReadTimeout(10, '')); 40 | } catch (TypeError $e) { 41 | echo $e->getMessage() . PHP_EOL; 42 | } 43 | // Fourth test, read supposed to get the last value set : 10.5 44 | var_dump($s->getReadTimeout()); 45 | 46 | // Set read timout with the params as null 47 | var_dump($s->setReadTimeout(0, 0)); 48 | // Fifth test, read supposed to get the last value set : 0.0 49 | var_dump($s->getReadTimeout()); 50 | 51 | 52 | unset($s); 53 | ?> 54 | --EXPECTF-- 55 | array(2) { 56 | ["sec"]=> 57 | int(5) 58 | ["usec"]=> 59 | int(5) 60 | } 61 | NULL 62 | array(2) { 63 | ["sec"]=> 64 | int(10) 65 | ["usec"]=> 66 | int(0) 67 | } 68 | NULL 69 | array(2) { 70 | ["sec"]=> 71 | int(10) 72 | ["usec"]=> 73 | int(5) 74 | } 75 | %AStomp::setReadTimeout()%s1%s string given%A 76 | array(2) { 77 | ["sec"]=> 78 | int(10) 79 | ["usec"]=> 80 | int(5) 81 | } 82 | %AStomp::setReadTimeout()%s2%s string given%A 83 | array(2) { 84 | ["sec"]=> 85 | int(10) 86 | ["usec"]=> 87 | int(5) 88 | } 89 | NULL 90 | array(2) { 91 | ["sec"]=> 92 | int(0) 93 | ["usec"]=> 94 | int(0) 95 | } 96 | -------------------------------------------------------------------------------- /tests/010-timeout/002.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test stomp_get_read_timout() and stomp_set_read_timeout() - tests functionnality and parameters 3 | --INI-- 4 | stomp.default_read_timeout_sec=5 5 | stomp.default_read_timeout_usec=5 6 | --SKIPIF-- 7 | 11 | --FILE-- 12 | getMessage() . PHP_EOL; 34 | } 35 | // Fourth test, read supposed to get the last value set : 10.5 36 | var_dump(stomp_get_read_timeout($link)); 37 | 38 | try { 39 | // Set read timout with the second param as a string, supposed to trigger a warning on PHP 7 40 | // supposed to trigger an exception on PHP 8 41 | var_dump(stomp_set_read_timeout($link, 10, '')); 42 | } catch (TypeError $e) { 43 | echo $e->getMessage() . PHP_EOL; 44 | } 45 | // Fourth test, read supposed to get the last value set : 10.5 46 | var_dump(stomp_get_read_timeout($link)); 47 | 48 | // Set read timout with the params as null 49 | var_dump(stomp_set_read_timeout($link, 0, 0)); 50 | // Fifth test, read supposed to get the last value set : 0.0 51 | var_dump(stomp_get_read_timeout($link)); 52 | 53 | 54 | unset($s); 55 | ?> 56 | --EXPECTF-- 57 | array(2) { 58 | ["sec"]=> 59 | int(5) 60 | ["usec"]=> 61 | int(5) 62 | } 63 | NULL 64 | array(2) { 65 | ["sec"]=> 66 | int(10) 67 | ["usec"]=> 68 | int(0) 69 | } 70 | NULL 71 | array(2) { 72 | ["sec"]=> 73 | int(10) 74 | ["usec"]=> 75 | int(5) 76 | } 77 | %Astomp_set_read_timeout()%s2%S string given%A 78 | array(2) { 79 | ["sec"]=> 80 | int(10) 81 | ["usec"]=> 82 | int(5) 83 | } 84 | %Astomp_set_read_timeout()%s3%s string given%A 85 | array(2) { 86 | ["sec"]=> 87 | int(10) 88 | ["usec"]=> 89 | int(5) 90 | } 91 | NULL 92 | array(2) { 93 | ["sec"]=> 94 | int(0) 95 | ["usec"]=> 96 | int(0) 97 | } 98 | -------------------------------------------------------------------------------- /tests/011-commit/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Test Stomp::commit() - tests functionnality and parameters 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | begin('t1')); 17 | 18 | // sends a message to the queue and specifies a good transaction 19 | var_dump($s->send('/queue/test-011-commit', 'bar', array('transaction' => 't1'))); 20 | 21 | // sends a message to the queue and asks for a receipt 22 | $s->send('/queue/test-011-commit', 'bar', array('transaction' => 't2', 'receipt' => 'tptp')); 23 | echo gettype($s->error()) . PHP_EOL; 24 | 25 | // commits a valid transaction 26 | var_dump($s->commit('t1')); 27 | 28 | // commits non valid transaction (null as a parameter) and asks for a receipt 29 | var_dump($s->commit(null, array('receipt' => 'commit-key'))); 30 | var_dump($s->commit(null)); 31 | 32 | // commits a non valid transaction (a transaction id that does not exist) and asks for a receipt 33 | $s->commit('t2', array('receipt' => 'commit-key')); 34 | echo gettype($s->error()); 35 | 36 | unset($s); 37 | ?> 38 | --EXPECTF-- 39 | bool(true) 40 | bool(true) 41 | string 42 | bool(true) 43 | bool(false) 44 | bool(true) 45 | string 46 | -------------------------------------------------------------------------------- /tests/bug_16930.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #16930 - readFrame reports error-frames as "timeout" 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | abort('t2'); 15 | try { 16 | var_dump($s->readFrame()); 17 | } catch(StompException $e) { 18 | var_dump($e->getMessage()); 19 | } 20 | 21 | ?> 22 | --EXPECTF-- 23 | string(%d) "%s" 24 | -------------------------------------------------------------------------------- /tests/bug_16936.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Bug #16936 - Module segfaults on readFrame if Frame > STOMP_BUFSIZE 3 | --SKIPIF-- 4 | 8 | --FILE-- 9 | getMessage()); 21 | } 22 | 23 | /* send a message to the queue 'foo' */ 24 | $stomp->send($queue, $msg); 25 | 26 | /* subscribe to messages from the queue 'foo' */ 27 | $stomp->subscribe($queue, array('ack' => 'auto')); 28 | 29 | /* read a frame */ 30 | $frame = $stomp->readFrame(); 31 | 32 | if ($frame->body === $msg) { 33 | var_dump($frame->body); 34 | /* acknowledge that the frame was received */ 35 | $stomp->ack($frame); 36 | } 37 | 38 | /* close connection */ 39 | unset($stomp); 40 | 41 | ?> 42 | --EXPECTF-- 43 | string(%d) "%s" 44 | -------------------------------------------------------------------------------- /tests/config.inc: -------------------------------------------------------------------------------- 1 | 15 | --------------------------------------------------------------------------------