diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f1bf8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +*~ +*.lo +*.la + +build/ +.libs/ +autom4te.cache/ +modules/ + +tests/*.diff +tests/*.exp +tests/*.log +tests/*.out +tests/*.php +tests/*.sh + +Makefile* +configure* +libtool +ltmain.sh +missing +mkinstalldirs +install-sh +aclocal.m4 +acinclude.m4 +.deps + +config* +!config.m4 +!config.w32 + diff --git a/.travis.yml b/.travis.yml index b1abe0d..2e7693d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,17 @@ language: php + php: - - 5.6 - 5.5 - - 5.4 - - 5.3 + env: - - ZEROMQ_VERSION=v2.2.0 - - ZEROMQ_VERSION=v3.1.0 - - ZEROMQ_VERSION=v3.2.0 - - ZEROMQ_VERSION=v3.2.1 - - ZEROMQ_VERSION=v3.2.2 - - ZEROMQ_VERSION=v3.2.3 - - ZEROMQ_VERSION=v3.2.4 - - ZEROMQ_VERSION=v4.0.0 - ZEROMQ_VERSION=v4.0.1 - - ZEROMQ_VERSION=v4.0.1 WITH_CZMQ=true + - ZEROMQ_VERSION=v4.0.1 WITH_CZMQ=true WITH_ZYRE=true + before_install: - sudo apt-get update + - sudo apt-get remove libzmq3-dev libzmq3 - sudo apt-get install uuid-dev -script: travis/script.sh $ZEROMQ_VERSION $WITH_CZMQ + +script: travis/script.sh $ZEROMQ_VERSION $WITH_CZMQ $WITH_ZYRE + diff --git a/api.php b/api.php index 1c4b474..72ed89d 100644 --- a/api.php +++ b/api.php @@ -426,6 +426,91 @@ public function count() {} public function clear() {} } +class ZMQZyre { + const LIBZYRE_VERSION = '1.1.0'; + /** + * Construct a ZMQZyre + * + * @param ZMQContext $context + * @return void + */ + public function __construct(ZMQContext $ZMQContext) {} + /** + * Set node header; these are provided to other nodes during discovery and come in each ENTER message. + * + * @param string $name + * @param string $value + * @return void + */ + public function setHeader($name, $value="") {} + /** + * Start node, after setting header values. When you start a node it begins discovery and connection. + There is no stop method; to stop a node, destroy it. + * + * @return void + */ + public function start() {} + /** + * Stop node, this signals to other peers that this node will go away. + This is polite; however you can also just destroy the node without stopping it. + * + * @return void + */ + public function stop() {} + /** + * Join a named group; after joining a group you can send messages + to the group and all Zyre nodes in that group will receive them. + * + * @param string $group + * @return void + */ + public function join($group) {} + /** + * Leave a group. + * + * @param string $group + * @return void + */ + public function leave($group) {} + /** + * Receive next message from network; the message may be a control + message (ENTER, EXIT, JOIN, LEAVE) or data (WHISPER, SHOUT). + Returns associative array, or NULL if interrupted + * + * @return void + */ + public function recv() {} + /** + * Send a message on the network to a specific peer + * + * @param string $peer + * @param string $data + * @return void + */ + public function sendPeer($peer, $data) {} + /** + * Send a message on the network for a group + * + * @param string $group + * @param string $data + * @return void + */ + public function sendGroup($group, $data) {} + /** + * Get zyre ZeroMQ socket, for polling or receiving messages + * + * @return ZMQSocket + */ + public function getSocket() {} +} + +class ZMQException extends Exception {} +class ZMQContextException extends ZMQException {} +class ZMQSocketException extends ZMQException {} +class ZMQPollException extends ZMQException {} +class ZMQDeviceException extends ZMQException {} +class ZMQZyreException extends ZMQException {} + /** * A security certificate for the ØMQ CURVE authentication mechanism. */ @@ -665,5 +750,6 @@ public function deny($address) {} */ public function configure($type, $domain, $filename) {} } + ?> diff --git a/config.m4 b/config.m4 index 53890fa..6450662 100644 --- a/config.m4 +++ b/config.m4 @@ -7,6 +7,9 @@ PHP_ARG_ENABLE(zmq_pthreads, whether to enable support for php threads extens PHP_ARG_WITH(czmq, whether to enable CZMQ support, [ --with-czmq[=DIR] Enable CZMQ support. DIR is the prefix to CZMQ installation directory.], no, no) +PHP_ARG_WITH(zyre, whether to enable Zyre support, +[ --with-zyre[=DIR] Enable Zyre support. DIR is the prefix to Zyre installation directory.], no, no) + if test "$PHP_ZMQ" != "no"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) @@ -69,6 +72,29 @@ if test "$PHP_ZMQ" != "no"; then fi fi + if test "$PHP_ZYRE" != "no" -a "$PHP_CZMQ" != "no"; then + if test "x$PHP_ZYRE" != "xyes"; then + export PKG_CONFIG_PATH="${PHP_ZYRE}/${PHP_LIBDIR}/pkgconfig" + fi + + AC_MSG_CHECKING(for Zyre) + if $PKG_CONFIG --exists libzyre; then + PHP_ZYRE_VERSION=`$PKG_CONFIG libzyre --modversion` + PHP_ZYRE_PREFIX=`$PKG_CONFIG libzyre --variable=prefix` + AC_MSG_RESULT([found version $PHP_ZYRE_VERSION in $PHP_ZYRE_PREFIX]) + + PHP_ZYRE_LIBS=`$PKG_CONFIG libzyre --libs` + PHP_ZYRE_INCS=`$PKG_CONFIG libzyre --cflags` + + PHP_EVAL_LIBLINE($PHP_ZYRE_LIBS, ZMQ_SHARED_LIBADD) + PHP_EVAL_INCLINE($PHP_ZYRE_INCS) + + AC_DEFINE([HAVE_ZYRE], [], [ZYRE was found]) + else + AC_MSG_RESULT([no]) + fi + fi + AC_CHECK_HEADERS([stdint.h],[php_zmq_have_stdint=yes; break;]) if test $php_zmq_have_stdint != "yes"; then AC_MSG_ERROR(Unable to find stdint.h) diff --git a/package.xml b/package.xml index 212447b..b57c856 100644 --- a/package.xml +++ b/package.xml @@ -108,6 +108,15 @@ + + + + + + + + + diff --git a/php_zmq.h b/php_zmq.h index ef50bfb..538fe7a 100644 --- a/php_zmq.h +++ b/php_zmq.h @@ -47,4 +47,16 @@ extern zend_module_entry zmq_module_entry; #define phpext_zmq_ptr &zmq_module_entry +/* PHP 5.4 */ +#if PHP_VERSION_ID < 50399 +# define object_properties_init(zo, class_type) { \ + zval *tmp; \ + zend_hash_copy((*zo).properties, \ + &class_type->default_properties, \ + (copy_ctor_func_t) zval_add_ref, \ + (void *) &tmp, \ + sizeof(zval *)); \ + } +#endif + #endif /* _PHP_ZMQ_H_ */ diff --git a/php_zmq_private.h b/php_zmq_private.h index 7b5c24e..a4816cb 100644 --- a/php_zmq_private.h +++ b/php_zmq_private.h @@ -44,6 +44,14 @@ # endif #endif +#if defined(HAVE_CZMQ) && defined(HAVE_ZYRE) +# include +# include +# if ZYRE_VERSION_MAJOR == 1 && CZMQ_VERSION_MAJOR >= 2 +# define HAVE_ZYRE_1 +# endif +#endif + #ifdef PHP_WIN32 # include "win32/php_stdint.h" #else @@ -275,6 +283,17 @@ ZEND_BEGIN_MODULE_GLOBALS(php_zmq) php_zmq_clock_ctx_t *clock_ctx; ZEND_END_MODULE_GLOBALS(php_zmq) + +#ifdef HAVE_ZYRE_1 +#define PHP_ZMQ_ZYRE_OBJECT php_zmq_zyre *this = (php_zmq_zyre *)zend_object_store_get_object(getThis() TSRMLS_CC) +typedef struct _php_zmq_zyre { + zend_object zend_object; + zctx_t *shadow_context; + zyre_t *zyre; + zval *internal_socket; +} php_zmq_zyre; +#endif + #ifdef HAVE_CZMQ_2 typedef struct _php_zmq_cert { zend_object zend_object; diff --git a/tests/skipzyre.inc b/tests/skipzyre.inc new file mode 100644 index 0000000..e81bd91 --- /dev/null +++ b/tests/skipzyre.inc @@ -0,0 +1,6 @@ + diff --git a/tests/zyre-001.phpt b/tests/zyre-001.phpt new file mode 100644 index 0000000..cbced3e --- /dev/null +++ b/tests/zyre-001.phpt @@ -0,0 +1,15 @@ +--TEST-- +Test [Zyre] Object can be instancied multiple time +--SKIPIF-- + +--FILE-- + +--FILE-- +setHeader('X-TEST', 'Z1'); +$z1->start(); + +$z2 = new ZMQZyre($ctx); +$z2->setHeader('X-TEST', 'Z2'); +$z2->start(); + +usleep(100000); + +$obj = $z1->recv(); +var_dump($obj->command); +var_dump($obj->headers->{'X-TEST'}); + +$obj = $z2->recv(); +var_dump($obj->command); +var_dump($obj->headers->{'X-TEST'}); + +$z1->stop(); + +usleep(100000); + +$obj = $z2->recv(); +var_dump($obj->command); + +$z2->stop(); + +--EXPECTF-- +string(5) "ENTER" +string(2) "Z2" +string(5) "ENTER" +string(2) "Z1" +string(4) "EXIT" diff --git a/tests/zyre-003.phpt b/tests/zyre-003.phpt new file mode 100644 index 0000000..d2365b2 --- /dev/null +++ b/tests/zyre-003.phpt @@ -0,0 +1,79 @@ +--TEST-- +Test [Zyre] Zyre objects can exchange data in a group of peer +--SKIPIF-- + +--FILE-- +start(); +$z1->join('ZMQZyreTestGroup'); + + +$z2 = new ZMQZyre($ctx); +$z2->start(); +$z2->join('ZMQZyreTestGroup'); + + +usleep(100000); + +// Enter +$obj = $z1->recv(); +var_dump($obj->command); + +// Join +$obj = $z1->recv(); +var_dump($obj->command); +var_dump($obj->group); + +// Enter +$obj = $z2->recv(); +var_dump($obj->command); + +// Join +$obj = $z2->recv(); +var_dump($obj->command); +var_dump($obj->group); + +$z2->sendGroup('ZMQZyreTestGroup', 'Some usefull data'); + +usleep(100000); + +// SHOUT +$obj = $z1->recv(); +var_dump($obj->group); +var_dump($obj->data); + +$z1->leave('ZMQZyreTestGroup'); + +usleep(100000); + +// Leave +$obj = $z2->recv(); +var_dump($obj->command); +var_dump($obj->group); + +$z1->stop(); + +usleep(100000); + +// Exit +$obj = $z2->recv(); +var_dump($obj->command); + +$z2->stop(); + +--EXPECTF-- +string(5) "ENTER" +string(4) "JOIN" +string(16) "ZMQZyreTestGroup" +string(5) "ENTER" +string(4) "JOIN" +string(16) "ZMQZyreTestGroup" +string(16) "ZMQZyreTestGroup" +string(17) "Some usefull data" +string(5) "LEAVE" +string(16) "ZMQZyreTestGroup" +string(4) "EXIT" diff --git a/tests/zyre-004.phpt b/tests/zyre-004.phpt new file mode 100644 index 0000000..67fc720 --- /dev/null +++ b/tests/zyre-004.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test [Zyre] Zyre objects can exchange data with a peer +--SKIPIF-- + +--FILE-- +start(); + +$z2 = new ZMQZyre($ctx); +$z2->start(); + +usleep(100000); + +// Enter +$obj = $z1->recv(); +var_dump($obj->command); + +// Enter +$obj = $z2->recv(); +var_dump($obj->command); +$peer = $obj->peer; + +$z2->sendPeer($peer, 'Some very usefull data'); + +usleep(100000); + +// SHOUT +$obj = $z1->recv(); +var_dump($obj->data); + +$z1->stop(); + +usleep(100000); + +// Exit +$obj = $z2->recv(); +var_dump($obj->command); + +$z2->stop(); + +--EXPECTF-- +string(5) "ENTER" +string(5) "ENTER" +string(22) "Some very usefull data" +string(4) "EXIT" diff --git a/tests/zyre-005.phpt b/tests/zyre-005.phpt new file mode 100644 index 0000000..6ade673 --- /dev/null +++ b/tests/zyre-005.phpt @@ -0,0 +1,41 @@ +--TEST-- +Test [Zyre] Test getter of internal ZMQSocket in ZMQZyre +--SKIPIF-- + +--FILE-- +setHeader('X-TEST', 'Z1'); +$z1->start(); + +usleep(100000); + +$z2 = new ZMQZyre($ctx); +$z2->setHeader('X-TEST', 'Z2'); +$z2->start(); + +usleep(100000); + +$sock1 = $z1->getSocket(); +$sock2 = $z2->getSocket(); + + +var_dump($sock1->recv()); // Enter +$sock1->recv(); // ID +$sock1->recv(); // Headers +$sock1->recv(); // IP:PORT + +var_dump($sock2->recv()); // Enter +$sock2->recv(); // ID +$sock2->recv(); // Headers +$sock2->recv(); // IP:PORT + +unset($sock1); +unset($sock2); + +--EXPECTF-- +string(5) "ENTER" +string(5) "ENTER" diff --git a/tests/zyre-006.phpt b/tests/zyre-006.phpt new file mode 100644 index 0000000..55862d7 --- /dev/null +++ b/tests/zyre-006.phpt @@ -0,0 +1,62 @@ +--TEST-- +Test [Zyre] Use ZMQSocket, build by ZMQZyre::getSocket, in a ZMQPoll +--SKIPIF-- + +--FILE-- +start(); +$z1->join('ZMQZyreTestGroup'); + +usleep(100000); + +$z2 = new ZMQZyre($ctx); +$z2->start(); +$z2->join('ZMQZyreTestGroup'); + +usleep(100000); + +$z1->sendGroup('ZMQZyreTestGroup', 'A'); +$z1->sendGroup('ZMQZyreTestGroup', 'B'); + +$poll = new ZMQPoll; +$poll->add($z2->getSocket(), ZMQ::POLL_IN); + +$readable = array(); +$writable = array(); + +for ($i=0; $i<4; $i++) { +try { + $events = $poll->poll($readable, $writable, -1); + $errors = $poll->getLastErrors(); + + if (count($errors) > 0) { + foreach ($errors as $error) { + echo "Error polling object " . $error . "\n"; + } + die('polling error'); + } + } catch (ZMQPollException $e) { + echo "poll failed: " . $e->getMessage() . "\n"; + die('polling exception'); + } + + if ($events > 0) { + /* Loop through readable objects and recv messages */ + foreach ($readable as $r) { + // Don't read the Socket use the recv function of zyre + // Socket will return a multi-part zmq message, instead zyre will parse it and produce an abject + $obj = $z2->recv(); + var_dump($obj->command); + } + } +} +--EXPECTF-- +string(5) "ENTER" +string(4) "JOIN" +string(5) "SHOUT" +string(5) "SHOUT" + diff --git a/travis/script.sh b/travis/script.sh index ac90a93..e07331a 100755 --- a/travis/script.sh +++ b/travis/script.sh @@ -65,6 +65,22 @@ install_libsodium() { } +# Installs Zyre +# +# Parameters: +# +# 1 - The directory to install Zyre to +install_zyre() { + local zyre_dir=$1 + + if test ! -d "/tmp/php-zmq-travis-support" + then + git clone https://github.com/phuedx/php-zmq-travis-support /tmp/php-zmq-travis-support + fi + + ln -s "/tmp/php-zmq-travis-support/zyre/zyre-1.0.0" $zyre_dir +} + # Installs CZMQ v2.2.0. # # Parameters: @@ -116,19 +132,25 @@ make_test() { local zeromq_dir=$2 local with_czmq=$3 local with_czmq_option="" + local with_zyre=$4 + local with_zyre_option="" if test $with_czmq = "true" then with_czmq_option="--with-czmq=/tmp/czmq" fi + if test $with_zyre = "true" + then + with_zyre_option="--with-zyre=/tmp/zyre" + fi + pushd $build_dir phpize - ./configure --with-zmq="$zeromq_dir" $with_czmq_option + ./configure --with-zmq="$zeromq_dir" $with_czmq_option $with_zyre_option make - if test ! -e modules/zmq.so then printf "PHP extension build failed\n" @@ -171,6 +193,7 @@ done zeromq_version=$1 with_czmq=$2 +with_zyre=$3 # NOTE (phuedx, 2014/07/07): These must be kept in sync with the configure # command used to build libsodium, ØMQ and CZMQ in @@ -178,7 +201,7 @@ with_czmq=$2 libsodium_dir=/tmp/libsodium zeromq_dir=/tmp/zeromq czmq_dir=/tmp/czmq - +zyre_dir=/tmp/zyre build_dir=/tmp/build install_libsodium $libsodium_dir @@ -189,6 +212,12 @@ then install_czmq $czmq_dir fi +if test $with_zyre = "true" +then + install_zyre $zyre_dir +fi + init_build_dir $build_dir -make_test $build_dir $zeromq_dir $with_czmq +make_test $build_dir $zeromq_dir $with_czmq $with_zyre + diff --git a/zmq.c b/zmq.c index f5a8c68..c97b354 100644 --- a/zmq.c +++ b/zmq.c @@ -52,17 +52,27 @@ zend_class_entry *php_zmq_socket_sc_entry; zend_class_entry *php_zmq_poll_sc_entry; zend_class_entry *php_zmq_device_sc_entry; + +#ifdef HAVE_ZYRE_1 +zend_class_entry *php_zmq_zyre_sc_entry; +#endif + #ifdef HAVE_CZMQ_2 zend_class_entry *php_zmq_cert_sc_entry; zend_class_entry *php_zmq_auth_sc_entry; #endif + zend_class_entry *php_zmq_exception_sc_entry; zend_class_entry *php_zmq_context_exception_sc_entry; zend_class_entry *php_zmq_socket_exception_sc_entry; zend_class_entry *php_zmq_poll_exception_sc_entry; zend_class_entry *php_zmq_device_exception_sc_entry; +#ifdef HAVE_ZYRE_1 +zend_class_entry *php_zmq_zyre_exception_sc_entry; +#endif + #ifdef HAVE_CZMQ_2 zend_class_entry *php_zmq_cert_exception_sc_entry; zend_class_entry *php_zmq_auth_exception_sc_entry; @@ -74,6 +84,10 @@ static zend_object_handlers zmq_context_object_handlers; static zend_object_handlers zmq_poll_object_handlers; static zend_object_handlers zmq_device_object_handlers; +#ifdef HAVE_ZYRE_1 +static zend_object_handlers zmq_zyre_object_handlers; +#endif + #ifdef PHP_ZMQ_PTHREADS #include @@ -110,7 +124,7 @@ static int le_zmq_socket, le_zmq_context; /** {{{ static void php_zmq_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) */ -static void php_zmq_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) +static void php_zmq_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) { int major = 0, minor = 0, patch = 0; zmq_version(&major, &minor, &patch); @@ -118,6 +132,30 @@ static void php_zmq_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) } /* }}} */ +#ifdef HAVE_CZMQ +/** {{{ static void php_czmq_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) +*/ +static void php_czmq_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) +{ + int major = 0, minor = 0, patch = 0; + zsys_version(&major, &minor, &patch); + (void) snprintf(buffer, PHP_ZMQ_VERSION_LEN - 1, "%d.%d.%d", major, minor, patch); +} +/* }}} */ +#endif + +#ifdef HAVE_ZYRE +/** {{{ static void php_zyre_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) +*/ +static void php_zyre_get_lib_version(char buffer[PHP_ZMQ_VERSION_LEN]) +{ + // FFS: This is the zyre version read from include during build time + // libzyre will add a method to get the real version of the lib + (void) snprintf(buffer, PHP_ZMQ_VERSION_LEN - 1, "%d.%d.%d", ZYRE_VERSION_MAJOR, ZYRE_VERSION_MINOR, ZYRE_VERSION_PATCH); +} +/* }}} */ +#endif + /** {{{ static int php_zmq_socket_list_entry(void) */ static int php_zmq_socket_list_entry(void) @@ -885,7 +923,7 @@ PHP_METHOD(zmqsocket, recvmulti) long flags = 0; zend_bool retval; zval *msg; -#if ZMQ_VERSION_MAJOR < 3 +#if ZMQ_VERSION_MAJOR < 3 int64_t value; #else int value; @@ -915,7 +953,7 @@ PHP_METHOD(zmqsocket, recvmulti) } /* }}} */ -/** {{{ string ZMQ::getPersistentId() +/** {{{ string ZMQ::getPersistentId() Returns the persistent id of the object */ PHP_METHOD(zmqsocket, getpersistentid) @@ -1605,8 +1643,496 @@ PHP_METHOD(zmqdevice, __clone) { } /* -- END ZMQPoll */ -#ifdef HAVE_CZMQ_2 +#ifdef HAVE_ZYRE_1 +/* --- START ZMQZyre --- */ + +static void php_zmq_zyre_free_storage(void *object TSRMLS_DC) +{ + php_zmq_zyre *zmq_zyre = (php_zmq_zyre *) object; + + if (zmq_zyre->internal_socket != NULL) { + zend_objects_store_del_ref(zmq_zyre->internal_socket TSRMLS_CC); + zval_ptr_dtor (&zmq_zyre->internal_socket); + } + + if (zmq_zyre->zyre != NULL) { + zyre_destroy(&zmq_zyre->zyre); + } + + if (zmq_zyre->shadow_context != NULL) { + zctx_destroy(&zmq_zyre->shadow_context); + } + + zend_object_std_dtor(&zmq_zyre->zend_object TSRMLS_CC); + efree(zmq_zyre); +} + +static zend_object_value php_zmq_zyre_object_new(zend_class_entry *class_type TSRMLS_DC) +{ + zend_object_value result; + php_zmq_zyre *zmq_zyre; + + zmq_zyre = (php_zmq_zyre *) emalloc(sizeof(php_zmq_zyre)); + memset(&zmq_zyre->zend_object, 0, sizeof(zend_object)); + + /* zbeacon is initialised in ZMQZyre#__construct. */ + zmq_zyre->zyre = NULL; + zmq_zyre->internal_socket = NULL; + zend_object_std_init(&zmq_zyre->zend_object, class_type TSRMLS_CC); + object_properties_init(&zmq_zyre->zend_object, class_type); + + result.handle = zend_objects_store_put( + zmq_zyre, + NULL, + (zend_objects_free_object_storage_t) php_zmq_zyre_free_storage, + NULL TSRMLS_CC + ); + result.handlers = &zmq_zyre_object_handlers; + return result; +} + +/* {{{ proto void ZMQZyre::__construct(ZMQContext context) + Construct a ZMQZyre +*/ +PHP_METHOD(zmqzyre, __construct) +{ + PHP_ZMQ_ZYRE_OBJECT; + zval *object; + php_zmq_context_object *context_object; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &object, php_zmq_context_sc_entry) != SUCCESS) { + return; + } + + context_object = (php_zmq_context_object *) zend_object_store_get_object(object TSRMLS_CC); + + this->shadow_context = zctx_shadow_zmq_ctx(context_object->context->z_ctx); + if (this->shadow_context == NULL) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Failed to create the underlying shadow context object."); + return; + } + + this->zyre = zyre_new(this->shadow_context); + if (this->zyre == NULL) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Failed to create the underlying zbeacon object."); + return; + } + + return; +} + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre___construct_args, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, ZMQContext, ZMQContext, 0) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::setHeader(string name, string value) + Set node header; these are provided to other nodes during discovery and come in each ENTER message. +*/ +PHP_METHOD(zmqzyre, setHeader) +{ + PHP_ZMQ_ZYRE_OBJECT; + char *name, *value; + int name_len, value_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &value, &value_len) == FAILURE) { + return; + } + + if (name_len == 0) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Header name can not be empty"); + return; + } + + if (value_len == 0) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Header value can not be empty"); + return; + } + + zyre_set_header(this->zyre, name, value); + + ZMQ_RETURN_THIS; +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_setHeader_args, 0, 0, 1) + ZEND_ARG_INFO(0, name) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::start() + Start node, after setting header values. When you start a node it begins discovery and connection. + There is no stop method; to stop a node, destroy it. +*/ +PHP_METHOD(zmqzyre, start) +{ + PHP_ZMQ_ZYRE_OBJECT; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + zyre_start(this->zyre); + + ZMQ_RETURN_THIS; +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_start_args, 0, 0, 1) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::stop() + Stop node, this signals to other peers that this node will go away. + This is polite; however you can also just destroy the node without stopping it. +*/ +PHP_METHOD(zmqzyre, stop) +{ + PHP_ZMQ_ZYRE_OBJECT; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + zyre_stop(this->zyre); + + ZMQ_RETURN_THIS; +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_stop_args, 0, 0, 1) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::join(string group) + Join a named group; after joining a group you can send messages + to the group and all Zyre nodes in that group will receive them. +*/ +PHP_METHOD(zmqzyre, join) +{ + PHP_ZMQ_ZYRE_OBJECT; + char *group; + int group_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &group, &group_len) == FAILURE) { + return; + } + + if (group_len == 0) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Group name can not be empty"); + return; + } + + zyre_join(this->zyre, group); + + ZMQ_RETURN_THIS; +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_join_args, 0, 0, 1) + ZEND_ARG_INFO(0, group) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::leave(string group) + Leave a group. +*/ +PHP_METHOD(zmqzyre, leave) +{ + PHP_ZMQ_ZYRE_OBJECT; + char *group; + int group_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &group, &group_len) == FAILURE) { + return; + } + + if (group_len == 0) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Group name can not be empty"); + return; + } + + zyre_leave(this->zyre, group); + + ZMQ_RETURN_THIS; +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_leave_args, 0, 0, 1) + ZEND_ARG_INFO(0, group) +ZEND_END_ARG_INFO(); + + +// Helper function to convert zhash_t to php object +void zhash_to_object(const char *key, void *item, void **argument) +{ + zval *obj = (zval *)argument[0]; +#ifdef ZTS + TSRMLS_D = argument[1]; +#endif + + zend_update_property_string(NULL, obj, key, strlen(key), (char *)item TSRMLS_CC); +} + +/* {{{ proto void ZMQZyre::recv() + Receive next message from network; the message may be a control + message (ENTER, EXIT, JOIN, LEAVE) or data (WHISPER, SHOUT). + Returns associative array, or NULL if interrupted +*/ +PHP_METHOD(zmqzyre, recv) +{ + PHP_ZMQ_ZYRE_OBJECT; + zmsg_t *msg = NULL; + char *command = NULL, *peerid = NULL; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + msg = zyre_recv(this->zyre); + if (msg == NULL) { + RETURN_NULL(); + } + + object_init(return_value); + + // All frame start by a command + command = zmsg_popstr(msg); + if (command == NULL) { + goto cleanup; + } + + // 2nd parameter is always the peerid of the emiter + peerid = zmsg_popstr(msg); + if (peerid == NULL) { + goto cleanup; + } + + // Parse commands with additional content + if (strcmp(command, "ENTER") == 0) { + + zframe_t *headers_packed = zmsg_pop (msg); + char *ipaddress = zmsg_popstr(msg); + + if (headers_packed != NULL) { + + zhash_t *headers = zhash_unpack (headers_packed); + zframe_destroy (&headers_packed); + + if (headers != NULL) { + zval *h; + MAKE_STD_ZVAL(h); + object_init(h); + + void *args[2]; + args[0] = h; +#ifdef ZTS + args[1] = TSRMLS_C; +#endif + zhash_foreach(headers, zhash_to_object, args); + zhash_destroy(&headers); + + zend_update_property(NULL, return_value, "headers", strlen("headers"), h TSRMLS_CC); + } + } + + if (ipaddress != NULL) { + zend_update_property_string(NULL, return_value, "ipaddress", strlen("ipaddress"), ipaddress TSRMLS_CC); + free(ipaddress); + } else { + zend_update_property_null(NULL, return_value, "ipaddress", strlen("ipaddress") TSRMLS_CC); + } + } else + if (strcmp(command, "JOIN") == 0) { + char *group; + + group = zmsg_popstr(msg); + if (group == NULL) { + zend_update_property_null(NULL, return_value, "group", strlen("group") TSRMLS_CC); + } else { + zend_update_property_string(NULL, return_value, "group", strlen("group"), group TSRMLS_CC); + free(group); + } + } else + if (strcmp(command, "LEAVE") == 0) { + char *group; + + group = zmsg_popstr(msg); + if (group == NULL) { + zend_update_property_null(NULL, return_value, "group", strlen("group") TSRMLS_CC); + } else { + zend_update_property_string(NULL, return_value, "group", strlen("group"), group TSRMLS_CC); + free(group); + } + } else + if (strcmp(command, "SHOUT") == 0) { + char *group, *data; + + group = zmsg_popstr(msg); + if (group == NULL) { + zend_update_property_null(NULL, return_value, "group", strlen("group") TSRMLS_CC); + } else { + zend_update_property_string(NULL, return_value, "group", strlen("group"), group TSRMLS_CC); + free(group); + } + + data = zmsg_popstr(msg); + if (data == NULL) { + zend_update_property_null(NULL, return_value, "data", strlen("data") TSRMLS_CC); + } else { + zend_update_property_string(NULL, return_value, "data", strlen("data"), data TSRMLS_CC); + free(data); + } + } else + if (strcmp(command, "WHISPER") == 0) { + char *data; + + data = zmsg_popstr(msg); + if (data == NULL) { + zend_update_property_null(NULL, return_value, "data", strlen("data") TSRMLS_CC); + } else { + zend_update_property_string(NULL, return_value, "data", strlen("data"), data TSRMLS_CC); + free(data); + } + } + + zend_update_property_string(NULL, return_value, "command", strlen("command"), command TSRMLS_CC); + zend_update_property_string(NULL, return_value, "peer", strlen("peer"), peerid TSRMLS_CC); + +cleanup: + if (command != NULL) + free(command); + + if (peerid != NULL) + free(peerid); + + if (msg != NULL) + zmsg_destroy(&msg); +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_recv_args, 0, 0, 1) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::sendGroup(string group, string data) + Send a message on the network for a group +*/ +PHP_METHOD(zmqzyre, sendGroup) +{ + PHP_ZMQ_ZYRE_OBJECT; + char *data = NULL, *group = NULL; + int data_len, group_len; + int rc; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &group, &group_len, &data, &data_len) == FAILURE) { + return; + } + + if (group_len == 0) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Group name can not be empty"); + return; + } + + zyre_shouts (this->zyre, group, data); + + ZMQ_RETURN_THIS; +} + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_sendGroup_args, 0, 0, 2) + ZEND_ARG_INFO(0, group) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO(); + +/* {{{ proto void ZMQZyre::sendPeer(string peer, string data) + Send a message on the network to a specific peer +*/ +PHP_METHOD(zmqzyre, sendPeer) +{ + PHP_ZMQ_ZYRE_OBJECT; + char *data = NULL, *peer = NULL; + int data_len, peer_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &peer, &peer_len, &data, &data_len) == FAILURE) { + return; + } + + if (peer_len == 0) { + zend_throw_exception_ex(php_zmq_zyre_exception_sc_entry, 0 TSRMLS_CC, "Peer ID can not be empty"); + return; + } + + zyre_whispers(this->zyre, peer, data); + + ZMQ_RETURN_THIS; +} + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_sendPeer_args, 0, 0, 2) + ZEND_ARG_INFO(0, peer) + ZEND_ARG_INFO(0, data) +ZEND_END_ARG_INFO(); + +/* {{{ proto ZMQSocket ZMQZyre::getSocket() + Get zyre ZeroMQ socket, for polling or receiving messages +*/ +PHP_METHOD(zmqzyre, getSocket) +{ + PHP_ZMQ_ZYRE_OBJECT; + php_zmq_socket_object *zmq_sock; + void *zyre_sock = NULL; + bool is_persistent = true; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (this->internal_socket == NULL) { + zyre_sock = zyre_socket(this->zyre); + if (zyre_socket == NULL) { + RETURN_NULL(); + } + + // Create internal socket + php_zmq_socket *socket = (php_zmq_socket *) ecalloc(1, sizeof(php_zmq_socket)); + socket->z_socket = zyre_sock; + socket->ctx = this->shadow_context; + socket->pid = getpid(); + socket->is_persistent = is_persistent; + zend_hash_init(&(socket->connect), 0, NULL, NULL, is_persistent); + zend_hash_init(&(socket->bind), 0, NULL, NULL, is_persistent); + + // Create a ZMQSocket + MAKE_STD_ZVAL(this->internal_socket); + object_init_ex(this->internal_socket, php_zmq_socket_sc_entry); + zmq_sock = (php_zmq_socket_object *) zend_object_store_get_object(this->internal_socket TSRMLS_CC); + zmq_sock->socket = socket; + zmq_sock->persistent_id = NULL; + } + + *return_value = *(this->internal_socket); + zval_copy_ctor(return_value); +} +/* }}} */ + +ZEND_BEGIN_ARG_INFO_EX(zmqzyre_getSocket_args, 0, 0, 0) +ZEND_END_ARG_INFO() + + +static zend_function_entry php_zmq_zyre_class_methods[] = { + PHP_ME(zmqzyre, __construct, zmqzyre___construct_args, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR | ZEND_ACC_FINAL) + PHP_ME(zmqzyre, setHeader, zmqzyre_setHeader_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, start, zmqzyre_start_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, stop, zmqzyre_stop_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, join, zmqzyre_join_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, leave, zmqzyre_leave_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, recv, zmqzyre_recv_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, sendPeer, zmqzyre_sendPeer_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, sendGroup, zmqzyre_sendGroup_args, ZEND_ACC_PUBLIC) + PHP_ME(zmqzyre, getSocket, zmqzyre_getSocket_args, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +/* --- END ZMQZyre --- */ +#endif // HAVE_ZYRE_1 + + +#ifdef HAVE_CZMQ_2 /* --- START ZMQCert --- */ static void php_zmq_cert_free_storage(void *object TSRMLS_DC) @@ -1683,6 +2209,7 @@ PHP_METHOD(zmqcert, __construct) return; } +/* }}} */ ZEND_BEGIN_ARG_INFO_EX(zmqcert___construct_args, 0, 0, 0) ZEND_ARG_INFO(0, filename) @@ -2326,7 +2853,7 @@ static zend_function_entry php_zmq_socket_class_methods[] = { PHP_ME(zmqsocket, getsockopt, zmq_socket_getsockopt_args, ZEND_ACC_PUBLIC) PHP_ME(zmqsocket, __clone, zmq_socket_clone_args, ZEND_ACC_PRIVATE|ZEND_ACC_FINAL) PHP_MALIAS(zmqsocket, sendmsg, send, zmq_socket_send_args, ZEND_ACC_PUBLIC) - PHP_MALIAS(zmqsocket, recvmsg, recv, zmq_socket_recv_args, ZEND_ACC_PUBLIC) + PHP_MALIAS(zmqsocket, recvmsg, recv, zmq_socket_recv_args, ZEND_ACC_PUBLIC) {NULL, NULL, NULL} }; @@ -2661,10 +3188,14 @@ PHP_MINIT_FUNCTION(zmq) zend_class_entry ce, ce_context, ce_socket, ce_poll, ce_device; zend_class_entry ce_exception, ce_context_exception, ce_socket_exception, ce_poll_exception, ce_device_exception; +#ifdef HAVE_ZYRE_1 + zend_class_entry ce_zyre, ce_zyre_exception; +#endif #ifdef HAVE_CZMQ_2 zend_class_entry ce_cert, ce_cert_exception, ce_auth, ce_auth_exception; #endif + le_zmq_context = zend_register_list_destructors_ex(NULL, php_zmq_context_dtor, "ZMQ persistent context", module_number); le_zmq_socket = zend_register_list_destructors_ex(NULL, php_zmq_socket_dtor, "ZMQ persistent socket", module_number); @@ -2674,6 +3205,9 @@ PHP_MINIT_FUNCTION(zmq) memcpy(&zmq_poll_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); memcpy(&zmq_device_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); +#ifdef HAVE_ZYRE_1 + memcpy(&zmq_zyre_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); +#endif #ifdef HAVE_CZMQ_2 memcpy(&zmq_cert_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); memcpy(&zmq_auth_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); @@ -2704,6 +3238,12 @@ PHP_MINIT_FUNCTION(zmq) zmq_device_object_handlers.clone_obj = NULL; php_zmq_device_sc_entry = zend_register_internal_class(&ce_device TSRMLS_CC); +#ifdef HAVE_ZYRE_1 + INIT_CLASS_ENTRY(ce_zyre, "ZMQZyre", php_zmq_zyre_class_methods); + ce_zyre.create_object = php_zmq_zyre_object_new; + zmq_zyre_object_handlers.clone_obj = NULL; + php_zmq_zyre_sc_entry = zend_register_internal_class(&ce_zyre TSRMLS_CC); +#endif #ifdef HAVE_CZMQ_2 INIT_CLASS_ENTRY(ce_cert, "ZMQCert", php_zmq_cert_class_methods); ce_cert.create_object = php_zmq_cert_new; @@ -2736,6 +3276,11 @@ PHP_MINIT_FUNCTION(zmq) php_zmq_device_exception_sc_entry = zend_register_internal_class_ex(&ce_device_exception, php_zmq_exception_sc_entry, "ZMQException" TSRMLS_CC); php_zmq_device_exception_sc_entry->ce_flags |= ZEND_ACC_FINAL_CLASS; +#ifdef HAVE_ZYRE_1 + INIT_CLASS_ENTRY(ce_zyre_exception, "ZMQZyreException", NULL); + php_zmq_zyre_exception_sc_entry = zend_register_internal_class_ex(&ce_zyre_exception, php_zmq_exception_sc_entry, "ZMQException" TSRMLS_CC); + php_zmq_zyre_exception_sc_entry->ce_flags |= ZEND_ACC_FINAL_CLASS; +#endif #ifdef HAVE_CZMQ_2 INIT_CLASS_ENTRY(ce_cert_exception, "ZMQCertException", NULL); php_zmq_cert_exception_sc_entry = zend_register_internal_class_ex(&ce_cert_exception, php_zmq_exception_sc_entry, "ZMQException" TSRMLS_CC); @@ -2825,6 +3370,15 @@ PHP_MINIT_FUNCTION(zmq) #undef PHP_ZMQ_REGISTER_CONST_LONG #undef PHP_ZMQ_REGISTER_CONST_STRING +#ifdef HAVE_ZYRE_1 +#define PHP_ZYRE_REGISTER_CONST_STRING(const_name, value) \ + zend_declare_class_constant_string (php_zmq_zyre_sc_entry, const_name, sizeof(const_name)-1, value TSRMLS_CC); + + php_zyre_get_lib_version(version); + PHP_ZYRE_REGISTER_CONST_STRING("LIBZYRE_VERSION", version); + +#undef PHP_ZYRE_REGISTER_CONST_STRING +#endif return SUCCESS; } @@ -2842,14 +3396,33 @@ PHP_MSHUTDOWN_FUNCTION(zmq) PHP_MINFO_FUNCTION(zmq) { - char version[PHP_ZMQ_VERSION_LEN]; - php_zmq_get_lib_version(version); + char zmq_version[PHP_ZMQ_VERSION_LEN]; +#ifdef HAVE_CZMQ + char czmq_version[PHP_ZMQ_VERSION_LEN]; +#endif +#ifdef HAVE_ZYRE + char zyre_version[PHP_ZMQ_VERSION_LEN]; +#endif + + php_zmq_get_lib_version(zmq_version); +#ifdef HAVE_CZMQ + php_czmq_get_lib_version(czmq_version); +#endif +#ifdef HAVE_ZYRE + php_zyre_get_lib_version(zyre_version); +#endif php_info_print_table_start(); php_info_print_table_header(2, "ZMQ extension", "enabled"); php_info_print_table_row(2, "ZMQ extension version", PHP_ZMQ_VERSION); - php_info_print_table_row(2, "libzmq version", version); + php_info_print_table_row(2, "libzmq version", zmq_version); +#ifdef HAVE_CZMQ + php_info_print_table_row(2, "czmq version", czmq_version); +#endif +#ifdef HAVE_ZYRE + php_info_print_table_row(2, "libzyre version", zyre_version); +#endif php_info_print_table_end(); DISPLAY_INI_ENTRIES();