├── EXPERIMENTAL ├── CREDITS ├── config.w32 ├── examples ├── pub.php ├── identify_pub.php ├── deferred_pub.php ├── sub.php └── identify_sub.php ├── tests └── 001.phpt ├── nsq.php ├── Dockerfile ├── .gitignore ├── CMakeLists.txt ├── message.h ├── nsq_lookupd.h ├── pub.h ├── command.h ├── nsq_exception.h ├── common.h ├── sub.h ├── php_nsq.h ├── command.c ├── LICENSE ├── package.xml ├── config.m4 ├── message.c ├── nsq_exception.c ├── common.c ├── nsq_lookupd.c ├── README.md ├── pub.c ├── sub.c └── nsq.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | rick 2 | chinese name : 吴振宇(ZhenYuwu) 3 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // vim:ft=javascript 2 | 3 | // If your extension references something external, use ARG_WITH 4 | // ARG_WITH("nsq", "for nsq support", "no"); 5 | 6 | // Otherwise, use ARG_ENABLE 7 | // ARG_ENABLE("nsq", "enable nsq support", "no"); 8 | 9 | if (PHP_NSQ != "no") { 10 | EXTENSION("nsq", "nsq.c", PHP_EXTNAME_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /examples/pub.php: -------------------------------------------------------------------------------- 1 | connectNsqd($nsqdAddr); 9 | 10 | for($i = 0; $i < 1000; $i++){ 11 | $msg = "nihao".$i; 12 | $nsq->publish("test", $msg); 13 | } 14 | 15 | $nsq->closeNsqdConnection(); 16 | -------------------------------------------------------------------------------- /examples/identify_pub.php: -------------------------------------------------------------------------------- 1 | 2 | "begon", 11 | "deflate" => false, 12 | "deflate_level" => 6, 13 | "feature_negotiation" => true, 14 | "heartbeat_interval" => 50000, 15 | ); 16 | $nsq = new Nsq($identify);// the same to sub 17 | $isTrue = $nsq->connectNsqd($nsqdAddr); 18 | 19 | for($i = 0; $i < 10; $i++){ 20 | $msg = "nihao".$i; 21 | $nsq->publish("test", $msg); 22 | } 23 | 24 | $nsq->closeNsqdConnection(); 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/deferred_pub.php: -------------------------------------------------------------------------------- 1 | 2 | connectNsqd($nsqdAddr); 13 | 14 | for($i = 0; $i < 9; $i++){ 15 | 16 | $delayMsec = 3600000; 17 | //deferredPublish(string topic,string message, int millisecond); 18 | $deferred->deferredPublish("test", "the message will be received after".$delayMsec/(1000*60)."minutes", $delayMsec); 19 | 20 | } 21 | $deferred->closeNsqdConnection(); 22 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for nsq presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | nsq extension is available 22 | -------------------------------------------------------------------------------- /nsq.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('nsq')) { 5 | dl('nsq.' . PHP_SHLIB_SUFFIX); 6 | } 7 | /* 8 | $module = 'nsq'; 9 | $functions = get_extension_funcs($module); 10 | echo "Functions available in the test extension:$br\n"; 11 | foreach($functions as $func) { 12 | echo $func."$br\n"; 13 | } 14 | echo "$br\n"; 15 | $function = 'confirm_' . $module . '_compiled'; 16 | if (extension_loaded($module)) { 17 | $str = $function($module); 18 | } else { 19 | $str = "Module $module is not compiled into PHP"; 20 | } 21 | echo "$str\n"; 22 | */ 23 | $d = subscribe("sss"); 24 | var_dump($d); 25 | 26 | ?> 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:alpine AS builder 2 | 3 | ENV PHP_NSQ_VERSION=3.1.0 4 | 5 | RUN apk add libevent-dev autoconf alpine-sdk 6 | RUN wget "https://github.com/yunnian/php-nsq/archive/v${PHP_NSQ_VERSION}.tar.gz" 7 | RUN tar xvzf "v${PHP_NSQ_VERSION}.tar.gz" 8 | WORKDIR "/php-nsq-${PHP_NSQ_VERSION}" 9 | RUN phpize 10 | RUN ./configure 11 | RUN make 12 | RUN make install 13 | 14 | FROM php:alpine 15 | 16 | COPY --from=builder /usr/local/lib/php/extensions/no-debug-non-zts-20170718/nsq.so /usr/local/lib/php/extensions/no-debug-non-zts-20170718/nsq.so 17 | RUN apk add --no-cache libevent && \ 18 | echo "extension=nsq.so" > /usr/local/etc/php/conf.d/docker-php-ext-nsq.ini -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | configure.ac 2 | *.swp 3 | .idea 4 | cmake-build-debug/ 5 | .deps 6 | *.lo 7 | *.la 8 | .libs 9 | acinclude.m4 10 | aclocal.m4 11 | autom4te.cache 12 | build 13 | config.guess 14 | config.h 15 | config.h.in 16 | config.log 17 | config.nice 18 | config.status 19 | config.sub 20 | configure 21 | configure.in 22 | include 23 | install-sh 24 | libtool 25 | ltmain.sh 26 | ltmain.sh.backup 27 | Makefile 28 | Makefile.fragments 29 | Makefile.global 30 | Makefile.objects 31 | missing 32 | mkinstalldirs 33 | modules 34 | run-tests.php 35 | tests/*/*.diff 36 | tests/*/*.out 37 | tests/*/*.php 38 | tests/*/*.exp 39 | tests/*/*.log 40 | tests/*/*.sh 41 | 42 | # Debug files 43 | *.dSYM/ 44 | *.su 45 | *.idb 46 | *.pdb 47 | 48 | # Kernel Module Compile Results 49 | *.mod* 50 | *.cmd 51 | .tmp_versions/ 52 | modules.order 53 | Module.symvers 54 | Mkfile.old 55 | dkms.conf 56 | 57 | # Dependency files 58 | *.dep 59 | 60 | # Temporary files from autotools 61 | config.h.in~ 62 | configure~ 63 | 64 | # NSQ data files 65 | examples/nsqd.dat 66 | -------------------------------------------------------------------------------- /examples/sub.php: -------------------------------------------------------------------------------- 1 | "test", 10 | "channel" => "struggle", 11 | "rdy" => 10, 12 | "connect_num" => 1, 13 | "retry_delay_time" => 5000, // after 5000 msec, message will be retried 14 | "auto_finish" => true, 15 | ); 16 | //new NsqMessage(); 17 | 18 | $nsq->subscribe($nsq_lookupd, $config, function($msg,$bev){ 19 | 20 | try{ 21 | echo $msg->payload . " " . "attempts:".$msg->attempts."\n"; 22 | //do something 23 | }catch(Exception $e){ 24 | 25 | if($msg->attempts < 3){ 26 | //the message will be retried after you configure retry_delay_time 27 | throw new Exception(""); 28 | }else{ 29 | echo $e->getMessage(); 30 | return; 31 | } 32 | } 33 | //$msg->touch($bev,$msg->message_id); //if you callback run long time ,you can use this function 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(php-nsq) 3 | set(CMAKE_CXX_STANDARD 11) 4 | set(SOURCE_FILES 5 | php_nsq.h 6 | nsq.c 7 | nsq_exception.h 8 | nsq_exception.c 9 | nsq_lookupd.c 10 | nsq_lookupd.h 11 | command.c 12 | command.h 13 | common.c 14 | command.h 15 | sub.c 16 | sub.h 17 | pub.c 18 | pub.h 19 | message.h 20 | message.c 21 | 22 | ) 23 | 24 | add_executable(php-nsq ${SOURCE_FILES}) 25 | 26 | execute_process ( 27 | COMMAND php-config --include-dir 28 | OUTPUT_VARIABLE PHP_SOURCE 29 | ) 30 | 31 | string(REGEX REPLACE "\n$" "" PHP_SOURCE "${PHP_SOURCE}") 32 | 33 | message("Using source directory: ${PHP_SOURCE}") 34 | 35 | include_directories(${PHP_SOURCE}/main) 36 | include_directories(${PHP_SOURCE}/Zend) 37 | include_directories(${PHP_SOURCE}/sapi) 38 | include_directories(${PHP_SOURCE}/pear) 39 | include_directories(${PHP_SOURCE}/TSRM) 40 | include_directories(${PHP_SOURCE}) 41 | 42 | add_custom_target(makefile COMMAND sudo phpize && ./configure && make 43 | WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}) -------------------------------------------------------------------------------- /message.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | 18 | 19 | #ifndef STRUGGLE_NSQ_MESSAGE_H_CLIENT 20 | #define STRUGGLE_NSQ_MESSAGE_H_CLIENT 21 | 22 | void nsq_message_init(); 23 | //extern int le_bufferevent; 24 | 25 | #endif -------------------------------------------------------------------------------- /nsq_lookupd.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef STRUGGLE_NSQ_LOOKUP_CLIENT_C_H 18 | #define STRUGGLE_NSQ_LOOKUP_CLIENT_C_H 19 | 20 | char * request(char * url); 21 | 22 | void lookupd_init(); 23 | 24 | char *lookup(char *host, char *topic); 25 | 26 | char *request(char *url); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /pub.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef STRUGGLE_NSQ_PUB_CLIENT_C_H 18 | #define STRUGGLE_NSQ_PUB_CLIENT_C_H 19 | typedef struct nsqd_connect_config { 20 | char *host; 21 | char *port; 22 | 23 | } nsqd_connect_config; 24 | 25 | int * connect_nsqd(zval *ce, nsqd_connect_config *connect_config_arr, int connect_num); 26 | 27 | int publish(int sock, char *topic, char *msg, size_t msg_len); 28 | 29 | int deferredPublish(int sock, char *topic, char *msg, size_t msg_len, int delay_time); 30 | 31 | #endif //STRUGGLE_NSQ_PUB_CLIENT_C_H 32 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef STRUGGLE_NSQ_MESSAGE_CLIENT_C_H 18 | #define STRUGGLE_NSQ_MESSAGE_CLIENT_C_H 19 | 20 | void nsq_subscribe(struct bufferevent *bev, const char *topic, const char *channel); 21 | 22 | void nsq_requeue(struct bufferevent *bev, const char *id, int timeout_ms); 23 | 24 | void nsq_finish(struct bufferevent *bev, const char *id); 25 | 26 | void nsq_touch(struct bufferevent *bev, const char *id); 27 | 28 | void nsq_ready(struct bufferevent *bev, int count); 29 | 30 | void nsq_nop(struct bufferevent *bev); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /examples/identify_sub.php: -------------------------------------------------------------------------------- 1 | 2 | "begon", 10 | "deflate" => false, 11 | "deflate_level" => 6, 12 | "feature_negotiation" => true, 13 | "heartbeat_interval" => 60000, // Cannot exceed "-max-heartbeat-interval duration" configure that when the nsqd startup 14 | "hostname"=>"bogon", 15 | "long_id"=>"bogon", 16 | "msg_timeout" => 800000, // you should set --msg-timeout too when the nsqd startup 17 | "output_buffer_size" => 16384, 18 | "output_buffer_timeout" => 250, 19 | "sample_rate" => 0, 20 | "short_id" => "bogon", 21 | "snappy" => false, 22 | "tls_v1" => false, 23 | "user_agent" => "php-nsq/3.0" 24 | ); 25 | 26 | $nsq = new Nsq($identify); 27 | 28 | $config = array( 29 | "topic" => "test", 30 | "channel" => "struggle", 31 | "rdy" => 10, 32 | "connect_num" => 1, 33 | "retry_delay_time" => 5000, // after 5000 msec, message will be retried 34 | "auto_finish" => true, 35 | ); 36 | 37 | $nsq->subscribe($nsq_lookupd, $config, function($msg,$bev){ 38 | 39 | try{ 40 | echo $msg->payload . " " . "attempts:".$msg->attempts."\n"; 41 | //do something 42 | }catch(Exception $e){ 43 | 44 | if($msg->attempts < 3){ 45 | //the message will be retried after you configure retry_delay_time 46 | throw new Exception(""); 47 | }else{ 48 | echo $e->getMessage(); 49 | return; 50 | } 51 | } 52 | //$msg->touch($bev,$msg->message_id); //if you callback run long time ,you can use this function 53 | 54 | }); 55 | -------------------------------------------------------------------------------- /nsq_exception.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef STRUGGLE_NSQ_EXCEPTION_CLIENT_C_H 18 | #define STRUGGLE_NSQ_EXCEPTION_CLIENT_C_H 19 | 20 | typedef enum { 21 | PHP_NSQ_ERROR_NONE = 0, 22 | PHP_NSQ_ERROR_NO_CONNECTION, 23 | PHP_NSQ_ERROR_UNABLE_TO_PUBLISH_MESSAGE, 24 | PHP_NSQ_ERROR_TOPIC_KEY_REQUIRED, 25 | PHP_NSQ_ERROR_CHANNEL_KEY_REQUIRED, 26 | PHP_NSQ_ERROR_LOOKUPD_SERVER_NOT_AVAILABLE, 27 | PHP_NSQ_ERROR_PUB_LOST_CONNECTION, 28 | PHP_NSQ_ERROR_TOPIC_NOT_EXISTS, 29 | PHP_NSQ_ERROR_CALLBACK_FUNCTION_IS_NOT_CALLABLE, 30 | PHP_NSQ_ERROR_LIBEVENT_COULD_NOT_BE_INITIALIZED, 31 | PHP_NSQ_ERROR_CONNECTION_FAILED, 32 | } php_nsq_error_code; 33 | 34 | static const char *php_nsq_get_error_msg(php_nsq_error_code error_code); 35 | 36 | void nsq_exception_init(); 37 | 38 | void throw_exception(php_nsq_error_code error_code); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef STRUGGLE_NSQ_COMMON_H 18 | #define STRUGGLE_NSQ_COMMON_H 19 | #include 20 | #include "php.h" 21 | 22 | #if PHP_VERSION_ID >= 80000 23 | #define NSQ_COMPAT_OBJ_P(val) Z_OBJ_P(val) 24 | #define NSQ_COMPAT_VAL zend_object 25 | #define NSQ_COMPAT_GET_OBJ(val) val 26 | #else /* PHP_VERSION_ID < 80000 */ 27 | #define NSQ_COMPAT_OBJ_P(val) val 28 | #define NSQ_COMPAT_VAL zval 29 | #define NSQ_COMPAT_GET_OBJ(val) Z_OBJ_P(val) 30 | #endif /* PHP_VERSION_ID >= 80000 */ 31 | 32 | int readI16(const unsigned char *pData, uint16_t *pValue); 33 | 34 | int readI32(const unsigned char *pData, int32_t *pValue); 35 | 36 | int readI64(const unsigned char *data, int64_t *pValue); 37 | 38 | uint64_t ntoh64(const uint8_t *data); 39 | 40 | int check_ipaddr(const char *ip); 41 | 42 | int send_identify(zval *nsq_obj, int sock); 43 | 44 | #endif //STRUGGLE_NSQ_SUB_CLIENT_H 45 | -------------------------------------------------------------------------------- /sub.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef STRUGGLE_NSQ_SUB_CLIENT_H 18 | #define STRUGGLE_NSQ_SUB_CLIENT_H 19 | 20 | typedef struct NSQMsg { 21 | const char *topic; 22 | const char *channel; 23 | int32_t frame_type; 24 | int64_t timestamp; 25 | uint16_t attempts; 26 | char *message_id; 27 | int32_t size; 28 | char *body; 29 | int rdy; 30 | int delay_time; 31 | zend_bool auto_finish; 32 | } NSQMsg; 33 | 34 | typedef struct NSQArg { 35 | NSQMsg *msg; 36 | zend_resource * bev_res; 37 | const char *host; 38 | const char *port; 39 | zend_fcall_info *fci; 40 | zend_fcall_info_cache *fcc; 41 | zval *nsq_obj; 42 | } NSQArg; 43 | 44 | typedef struct ArgPidArr { 45 | pid_t pid; 46 | NSQArg arg; 47 | } ArgPidArr; 48 | 49 | //param is the nsqlookeupd's ip and port ,return the socket fd 50 | int subscribe(NSQArg *nsq_arg); 51 | 52 | #endif //STRUGGLE_NSQ_SUB_CLIENT_H 53 | -------------------------------------------------------------------------------- /php_nsq.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #ifndef PHP_NSQ_H 18 | #define PHP_NSQ_H 19 | 20 | extern zend_module_entry nsq_module_entry; 21 | #define phpext_nsq_ptr &nsq_module_entry 22 | 23 | #define PHP_NSQ_VERSION "3.9.1" /* Replace with version number for your extension */ 24 | 25 | #ifdef PHP_WIN32 26 | # define PHP_NSQ_API __declspec(dllexport) 27 | #elif defined(__GNUC__) && __GNUC__ >= 4 28 | # define PHP_NSQ_API __attribute__ ((visibility("default"))) 29 | #else 30 | # define PHP_NSQ_API 31 | #endif 32 | 33 | #ifdef ZTS 34 | #include "TSRM.h" 35 | #endif 36 | 37 | #define get_object_handle(object) Z_OBJ_HANDLE_P(object) 38 | 39 | 40 | 41 | void error_handlings(char *message); 42 | 43 | void error_handlings(char *message) { 44 | fputs(message, stderr); 45 | fputc('\n', stderr); 46 | //exit(1); 47 | } 48 | 49 | 50 | /* 51 | Declare any global variables you may need between the BEGIN 52 | and END macros here: 53 | 54 | ZEND_BEGIN_MODULE_GLOBALS(nsq) 55 | zend_long global_value; 56 | char *global_string; 57 | ZEND_END_MODULE_GLOBALS(nsq) 58 | */ 59 | 60 | /* Always refer to the globals in your function as NSQ_G(variable). 61 | You are encouraged to rename these macros something shorter, see 62 | examples in any other php module directory. 63 | */ 64 | #define NSQ_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(nsq, v) 65 | 66 | #if defined(ZTS) && defined(COMPILE_DL_NSQ) 67 | ZEND_TSRMLS_CACHE_EXTERN() 68 | #endif 69 | 70 | #endif /* PHP_NSQ_H */ 71 | 72 | 73 | /* 74 | * Local variables: 75 | * tab-width: 4 76 | * c-basic-offset: 4 77 | * End: 78 | * vim600: noet sw=4 ts=4 fdm=marker 79 | * vim<600: noet sw=4 ts=4 80 | */ 81 | -------------------------------------------------------------------------------- /command.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | const static char *NEW_LINE = "\n"; 24 | const static int MAX_BUF_SIZE = 128; 25 | 26 | void nsq_subscribe(struct bufferevent *bev, const char *topic, const char *channel) { 27 | char b[MAX_BUF_SIZE]; 28 | size_t n; 29 | n = sprintf(b, "SUB %s %s%s", topic, channel, NEW_LINE); 30 | bufferevent_write(bev, b, n); 31 | } 32 | 33 | void nsq_ready(struct bufferevent *bev, int count) { 34 | char b[MAX_BUF_SIZE]; 35 | size_t n; 36 | n = sprintf(b, "RDY %d%s", count, NEW_LINE); 37 | bufferevent_write(bev, b, n); 38 | } 39 | 40 | void nsq_finish(struct bufferevent *bev, const char *id) { 41 | char b[MAX_BUF_SIZE]; 42 | size_t n; 43 | n = sprintf(b, "FIN %s%s", id, NEW_LINE); 44 | bufferevent_write(bev, b, n); 45 | } 46 | 47 | void nsq_touch(struct bufferevent *bev, const char *id) { 48 | char b[MAX_BUF_SIZE]; 49 | size_t n; 50 | n = sprintf(b, "TOUCH %s%s", id, NEW_LINE); 51 | //bufferevent_write(bev, b, n); 52 | evutil_socket_t fd = bufferevent_getfd(bev); 53 | //int res = buffer_write(bev->output, fd); 54 | int res = write(fd, b, n); 55 | } 56 | 57 | 58 | void nsq_nop(struct bufferevent *bev) { 59 | char b[MAX_BUF_SIZE]; 60 | size_t n; 61 | n = sprintf(b, "NOP%s", NEW_LINE); 62 | bufferevent_write(bev, b, n); 63 | } 64 | 65 | void nsq_requeue(struct bufferevent *bev, const char *id, int timeout_ms) { 66 | char b[MAX_BUF_SIZE]; 67 | size_t n; 68 | n = sprintf(b, "REQ %s %d%s", id, timeout_ms, NEW_LINE); 69 | bufferevent_write(bev, b, n); 70 | } 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2011 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 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | nsq 4 | pecl.php.net 5 | PHP extension for NSQ client 6 | This extension is a NSQ client .NSQ is a message queue system which like Kafka . 7 | 8 | rick/ZhenYuwu 9 | rick 10 | 936321732@qq.com 11 | yes 12 | 13 | 2025-06-09 14 | 15 | 16 | 3.9.1 17 | 2.0.0 18 | 19 | 20 | stable 21 | stable 22 | 23 | PHP 24 | 25 | Fixed consumer not working on CentOS: Switched to timer-based solution, abandoned multi-process approach 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 7.0.0 62 | 63 | 64 | 1.4.0 65 | 66 | 67 | 68 | nsq 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl config.m4 for extension nsq 2 | 3 | dnl Comments in this file start with the string 'dnl'. 4 | dnl Remove where necessary. This file will not work 5 | dnl without editing. 6 | 7 | dnl If your extension references something external, use with: 8 | 9 | PHP_ARG_WITH(nsq, for nsq support, 10 | Make sure that the comment is aligned: 11 | [ --with-nsq Include nsq support]) 12 | 13 | PHP_ARG_WITH(libevent-path, for libevent support, 14 | Make sure that the comment is aligned: 15 | [ --with-libevent-path[=DIR] you libevent path],no,no) 16 | dnl Otherwise use enable: 17 | 18 | dnl PHP_ARG_ENABLE(nsq, whether to enable nsq support, 19 | dnl Make sure that the comment is aligned: 20 | dnl [ --enable-nsq Enable nsq support]) 21 | 22 | if test "$PHP_NSQ" != "no"; then 23 | SEARCH_FOR="/include/event2/event.h" 24 | if test "$PHP_LIBEVENT_PATH" != "no"; then 25 | AC_MSG_CHECKING([for libevent headers in $PHP_LIBEVENT_PATH]) 26 | if test -r $PHP_LIBEVENT_PATH/$SEARCH_FOR; then 27 | LIBEVENT_DIR=$PHP_LIBEVENT_PATH 28 | AC_MSG_RESULT([found]) 29 | fi 30 | else 31 | AC_MSG_CHECKING([for libevent headers in default path]) 32 | SEARCH_PATH="/usr /usr/local /opt/local /usr/local/Cellar/libevent/*" 33 | for i in $SEARCH_PATH ; do 34 | if test -r $i/$SEARCH_FOR; then 35 | LIBEVENT_DIR=$i 36 | AC_MSG_RESULT(found in $i) 37 | fi 38 | done 39 | fi 40 | 41 | echo "libevent-path:$LIBEVENT_DIR"; 42 | if test -z "$LIBEVENT_DIR"; then 43 | AC_MSG_RESULT([not found]) 44 | AC_MSG_ERROR([Cannot find libevent headers]) 45 | fi 46 | 47 | PHP_ADD_INCLUDE($LIBEVENT_DIR/include) 48 | 49 | LIBNAME=event 50 | LIBSYMBOL=event_base_new 51 | 52 | if test "x$PHP_LIBDIR" = "x"; then 53 | PHP_LIBDIR=lib 54 | fi 55 | 56 | PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 57 | [ 58 | PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBEVENT_DIR/$PHP_LIBDIR, NSQ_SHARED_LIBADD) 59 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME) 60 | 61 | ],[ 62 | AC_MSG_ERROR([wrong libevent version {1.4.+ is required} or lib not found]) 63 | ],[ 64 | dnl -L$LIBEVENT_DIR/$PHP_LIBDIR 65 | -l$LIBNAME 66 | ]) 67 | 68 | AC_CHECK_HEADERS([sys/wait.h]) 69 | 70 | 71 | dnl PHP_ADD_EXTENSION_DEP(libevent, sockets, true) 72 | dnl PHP_SUBST(LIBEVENT_SHARED_LIBADD) 73 | dnl Write more examples of tests here... 74 | 75 | dnl # --with-nsq -> check with-path 76 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 77 | dnl SEARCH_FOR="/include/nsq.h" # you most likely want to change this 78 | dnl if test -r $PHP_NSQ/$SEARCH_FOR; then # path given as parameter 79 | dnl NSQ_DIR=$PHP_NSQ 80 | dnl else # search default path list 81 | dnl AC_MSG_CHECKING([for nsq files in default path]) 82 | dnl for i in $SEARCH_PATH ; do 83 | dnl if test -r $i/$SEARCH_FOR; then 84 | dnl NSQ_DIR=$i 85 | dnl AC_MSG_RESULT(found in $i) 86 | dnl fi 87 | dnl done 88 | dnl fi 89 | dnl 90 | dnl if test -z "$NSQ_DIR"; then 91 | dnl AC_MSG_RESULT([not found]) 92 | dnl AC_MSG_ERROR([Please reinstall the nsq distribution]) 93 | dnl fi 94 | 95 | dnl # --with-nsq -> add include path 96 | dnl PHP_ADD_INCLUDE($NSQ_DIR/include) 97 | 98 | dnl # --with-nsq -> check for lib and symbol presence 99 | dnl LIBNAME=nsq # you may want to change this 100 | dnl LIBSYMBOL=nsq # you most likely want to change this 101 | 102 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 103 | dnl [ 104 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $NSQ_DIR/$PHP_LIBDIR, NSQ_SHARED_LIBADD) 105 | dnl AC_DEFINE(HAVE_NSQLIB,1,[ ]) 106 | dnl ],[ 107 | dnl AC_MSG_ERROR([wrong nsq lib version or lib not found]) 108 | dnl ],[ 109 | dnl -L$NSQ_DIR/$PHP_LIBDIR -lm 110 | dnl ]) 111 | dnl 112 | PHP_SUBST(NSQ_SHARED_LIBADD) 113 | 114 | PHP_NEW_EXTENSION(nsq, nsq.c pub.c nsq_lookupd.c nsq_exception.c message.c sub.c command.c common.c, $ext_shared) 115 | PHP_ADD_EXTENSION_DEP(nsq, json, true) 116 | fi 117 | -------------------------------------------------------------------------------- /message.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include 18 | #include "ext/standard/php_var.h" 19 | #include "message.h" 20 | #include 21 | #include "command.h" 22 | 23 | zend_class_entry *nsq_message_ce; 24 | 25 | extern int le_bufferevent; 26 | 27 | PHP_METHOD(NsqMessage,touch) 28 | { 29 | zval *bev_zval; 30 | zval *message_id; 31 | ZEND_PARSE_PARAMETERS_START(2,2) 32 | Z_PARAM_RESOURCE(bev_zval) 33 | Z_PARAM_ZVAL(message_id) 34 | ZEND_PARSE_PARAMETERS_END(); 35 | struct bufferevent *bev = (struct bufferevent*)zend_fetch_resource(Z_RES_P(bev_zval), "buffer event", le_bufferevent); 36 | nsq_touch(bev, Z_STRVAL_P(message_id)); 37 | } 38 | 39 | //undo: don't finish agin after requeue 40 | PHP_METHOD(NsqMessage,requeue) 41 | { 42 | zval *bev_zval; 43 | zval *message_id; 44 | zval *time_ms; 45 | ZEND_PARSE_PARAMETERS_START(3,3) 46 | Z_PARAM_RESOURCE(bev_zval) 47 | Z_PARAM_ZVAL(message_id) 48 | Z_PARAM_ZVAL(time_ms) 49 | ZEND_PARSE_PARAMETERS_END(); 50 | struct bufferevent *bev = (struct bufferevent*)zend_fetch_resource(Z_RES_P(bev_zval), "buffer event", le_bufferevent); 51 | nsq_requeue(bev, Z_STRVAL_P(message_id), Z_LVAL_P(time_ms)); 52 | } 53 | 54 | 55 | 56 | PHP_METHOD(NsqMessage,finish) 57 | { 58 | zval *bev_zval; 59 | zval *message_id; 60 | ZEND_PARSE_PARAMETERS_START(2,2) 61 | Z_PARAM_RESOURCE(bev_zval) 62 | Z_PARAM_ZVAL(message_id) 63 | ZEND_PARSE_PARAMETERS_END(); 64 | struct bufferevent *bev = (struct bufferevent*)zend_fetch_resource(Z_RES_P(bev_zval), "buffer event", le_bufferevent); 65 | nsq_finish(bev, Z_STRVAL_P(message_id)); 66 | } 67 | 68 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_requeue, 0, 0, -1) 69 | ZEND_ARG_INFO(0, bev_zval) 70 | ZEND_ARG_INFO(0, message_id) 71 | ZEND_ARG_INFO(0, time_ms) 72 | ZEND_END_ARG_INFO() 73 | 74 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_touch, 0, 0, -1) 75 | ZEND_ARG_INFO(0, bev_zval) 76 | ZEND_ARG_INFO(0, message_id) 77 | ZEND_END_ARG_INFO() 78 | 79 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_finish, 0, 0, -1) 80 | ZEND_ARG_INFO(0, bev_zval) 81 | ZEND_ARG_INFO(0, message_id) 82 | ZEND_END_ARG_INFO() 83 | 84 | static const zend_function_entry nsq_message_functions[] = { 85 | PHP_ME(NsqMessage, touch, arginfo_nsq_touch, ZEND_ACC_PUBLIC) 86 | PHP_ME(NsqMessage, requeue, arginfo_nsq_requeue, ZEND_ACC_PUBLIC) 87 | PHP_ME(NsqMessage, finish, arginfo_nsq_finish, ZEND_ACC_PUBLIC) 88 | PHP_FE_END /* Must be the last line in nsq_functions[] */ 89 | 90 | }; 91 | 92 | void nsq_message_init() { 93 | zend_class_entry nsq_message; 94 | INIT_CLASS_ENTRY(nsq_message,"NsqMessage",nsq_message_functions); 95 | nsq_message_ce = zend_register_internal_class(&nsq_message); 96 | zend_declare_property_null(nsq_message_ce,ZEND_STRL("message_id"),ZEND_ACC_PUBLIC); 97 | zend_declare_property_null(nsq_message_ce,ZEND_STRL("messageId"),ZEND_ACC_PUBLIC); 98 | zend_declare_property_null(nsq_message_ce,ZEND_STRL("timestamp"),ZEND_ACC_PUBLIC); 99 | zend_declare_property_null(nsq_message_ce,ZEND_STRL("attempts"),ZEND_ACC_PUBLIC); 100 | zend_declare_property_null(nsq_message_ce,ZEND_STRL("payload"),ZEND_ACC_PUBLIC); 101 | } 102 | -------------------------------------------------------------------------------- /nsq_exception.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include "php.h" 18 | #include "stdio.h" 19 | #include "string.h" 20 | #include "assert.h" 21 | #include "nsq_exception.h" 22 | #include "zend_exceptions.h" 23 | 24 | #define PHP_NSQ_EXCEPTION_REGISTER_CONSTANT(_name, _value) \ 25 | zend_register_long_constant((_name), sizeof(_name)-1, (_value), (CONST_CS | CONST_PERSISTENT), 0); 26 | 27 | PHPAPI zend_class_entry *php_nsq_exception_ce; 28 | 29 | static const char *php_nsq_get_error_msg(php_nsq_error_code error_code) 30 | { 31 | switch(error_code) { 32 | case PHP_NSQ_ERROR_NONE: 33 | return "No error"; 34 | case PHP_NSQ_ERROR_NO_CONNECTION: 35 | return "No connection to close"; 36 | case PHP_NSQ_ERROR_UNABLE_TO_PUBLISH_MESSAGE: 37 | return "Unable to publish message"; 38 | case PHP_NSQ_ERROR_TOPIC_KEY_REQUIRED: 39 | return "Topic key is required"; 40 | case PHP_NSQ_ERROR_CHANNEL_KEY_REQUIRED: 41 | return "Channel key is required"; 42 | case PHP_NSQ_ERROR_LOOKUPD_SERVER_NOT_AVAILABLE: 43 | return "Lookupd server not available"; 44 | case PHP_NSQ_ERROR_PUB_LOST_CONNECTION: 45 | return "Pub lost connection"; 46 | case PHP_NSQ_ERROR_TOPIC_NOT_EXISTS: 47 | return "Topic not exists"; 48 | case PHP_NSQ_ERROR_CALLBACK_FUNCTION_IS_NOT_CALLABLE: 49 | return "Callback function in subscribe is not callable"; 50 | case PHP_NSQ_ERROR_LIBEVENT_COULD_NOT_BE_INITIALIZED: 51 | return "Could not initialize libevent"; 52 | case PHP_NSQ_ERROR_CONNECTION_FAILED: 53 | return "Connection failed"; 54 | default: 55 | return "Unknown error"; 56 | } 57 | } 58 | 59 | static PHP_METHOD(NsqException, __construct); 60 | 61 | PHPAPI zend_class_entry *php_nsq_exception_ce; 62 | 63 | void nsq_exception_init() { 64 | zend_class_entry nsq_exception; 65 | 66 | INIT_CLASS_ENTRY(nsq_exception, "NsqException", NULL); 67 | php_nsq_exception_ce = zend_register_internal_class_ex(&nsq_exception, zend_ce_exception); 68 | 69 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_NONE", PHP_NSQ_ERROR_NONE); 70 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_NO_CONNECTION", PHP_NSQ_ERROR_NO_CONNECTION); 71 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_UNABLE_TO_PUBLISH_MESSAGE", PHP_NSQ_ERROR_UNABLE_TO_PUBLISH_MESSAGE); 72 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_TOPIC_KEY_REQUIRED", PHP_NSQ_ERROR_TOPIC_KEY_REQUIRED); 73 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_CHANNEL_KEY_REQUIRED", PHP_NSQ_ERROR_CHANNEL_KEY_REQUIRED); 74 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_LOOKUPD_SERVER_NOT_AVAILABLE", PHP_NSQ_ERROR_LOOKUPD_SERVER_NOT_AVAILABLE); 75 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_PUB_LOST_CONNECTION", PHP_NSQ_ERROR_PUB_LOST_CONNECTION); 76 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_TOPIC_NOT_EXISTS", PHP_NSQ_ERROR_TOPIC_NOT_EXISTS); 77 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_CALLBACK_FUNCTION_IS_NOT_CALLABLE", PHP_NSQ_ERROR_CALLBACK_FUNCTION_IS_NOT_CALLABLE); 78 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_LIBEVENT_COULD_NOT_BE_INITIALIZED", PHP_NSQ_ERROR_LIBEVENT_COULD_NOT_BE_INITIALIZED); 79 | PHP_NSQ_EXCEPTION_REGISTER_CONSTANT("NSQ_ERROR_CONNECTION_FAILED", PHP_NSQ_ERROR_CONNECTION_FAILED); 80 | } 81 | 82 | void throw_exception(php_nsq_error_code error_code) { 83 | zend_throw_exception(php_nsq_exception_ce, php_nsq_get_error_msg(error_code), error_code); 84 | } 85 | 86 | PHP_METHOD (NsqException, __construct) { 87 | zval *self; 88 | self = getThis(); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /common.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | 18 | #include "common.h" 19 | #include 20 | #include "ext/standard/php_var.h" 21 | #include "ext/standard/info.h" 22 | #include "ext/json/php_json.h" 23 | #include "ext/standard/php_string.h" 24 | #include "zend_smart_str.h" 25 | 26 | int send_identify(zval *nsq_obj, int sock) 27 | { 28 | //IDENTIFY 29 | zval * nsq_config; 30 | zval rv3; 31 | zval json; 32 | nsq_config = zend_read_property(Z_OBJCE_P(nsq_obj), NSQ_COMPAT_OBJ_P(nsq_obj), "nsqConfig", sizeof("nsqConfig")-1, 1, &rv3); 33 | 34 | smart_str json_buf = {0}; 35 | if(Z_TYPE_P(nsq_config) != IS_NULL){ 36 | php_json_encode(&json_buf, nsq_config, 0) ; 37 | smart_str_0(&json_buf); 38 | ZVAL_NEW_STR(&json,json_buf.s); 39 | char * identify_command = emalloc(256); 40 | memset(identify_command, '\0', 256); 41 | int identify_len = sprintf(identify_command, "%s", "IDENTIFY\n"); 42 | uint32_t json_len = htonl(Z_STRLEN(json)); 43 | memcpy(identify_command + identify_len, &json_len, 4); 44 | int len_2 = sprintf(identify_command + identify_len + 4, "%s", Z_STRVAL(json)); 45 | send(sock,identify_command, identify_len+Z_STRLEN(json)+4 ,0); 46 | zval *negotiation = zend_hash_str_find(Z_ARRVAL_P(nsq_config), "feature_negotiation", sizeof("feature_negotiation") - 1); 47 | 48 | 49 | int l = 0; 50 | int current_l = 0; 51 | int msg_size; 52 | char *message; 53 | char *msg_size_char = malloc(4); 54 | memset(msg_size_char, 0x00, 4); 55 | int size; 56 | 57 | again_size: 58 | size = read(sock, msg_size_char, 4); 59 | if(size <= 0){ 60 | goto again_size; 61 | } 62 | readI32((const unsigned char *) msg_size_char, &msg_size); 63 | 64 | free(msg_size_char); 65 | 66 | message = emalloc(msg_size + 1); 67 | memset(message, 0x00, msg_size); 68 | again: 69 | l += read(sock, message +l , msg_size); 70 | if( l < msg_size ){ 71 | goto again; 72 | 73 | } 74 | 75 | efree(message); 76 | efree(identify_command); 77 | zval_ptr_dtor(nsq_config); 78 | zval_ptr_dtor(&json); 79 | } 80 | 81 | return 0; 82 | } 83 | 84 | int readI16(const unsigned char *pData, uint16_t *pValue) { 85 | *pValue = (pData[0] << 8) | pData[1]; 86 | return 0; 87 | } 88 | 89 | int readI32(const unsigned char *pData, int32_t *pValue) { 90 | *pValue = (pData[0] << 24) | (pData[1] << 16) | (pData[2] << 8) | pData[3]; 91 | return 0; 92 | } 93 | 94 | 95 | int readI64(const unsigned char *data, int64_t *pValue) { 96 | *pValue = ((uint64_t) data[0] << 56) | ((uint64_t) data[1] << 48) | ((uint64_t) data[2] << 40) | 97 | ((uint64_t) data[3] << 32) | ((uint64_t) data[4] << 24) | ((uint64_t) data[5] << 16) | 98 | ((uint64_t) data[6] << 8) | (uint64_t) data[7]; 99 | return 0; 100 | 101 | } 102 | 103 | uint64_t ntoh64(const uint8_t *data) { 104 | return (uint64_t) (data[7]) | (uint64_t) (data[6]) << 8 | 105 | (uint64_t) (data[5]) << 16 | (uint64_t) (data[4]) << 24 | 106 | (uint64_t) (data[3]) << 32 | (uint64_t) (data[2]) << 40 | 107 | (uint64_t) (data[1]) << 48 | (uint64_t) (data[0]) << 56; 108 | } 109 | 110 | int check_ipaddr(const char *str) { 111 | if (str == NULL || *str == '\0') { 112 | return 0; 113 | } 114 | 115 | struct sockaddr_in6 addr6; 116 | struct sockaddr_in addr4; 117 | 118 | if (1 == inet_pton(AF_INET, str, &addr4.sin_addr)) { 119 | return 1; 120 | } else if (1 == inet_pton(AF_INET6, str, &addr6.sin6_addr)) { 121 | return 1; 122 | } 123 | return 0; 124 | } 125 | -------------------------------------------------------------------------------- /nsq_lookupd.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "ext/standard/php_var.h" 22 | #include "nsq_lookupd.h" 23 | #include "common.h" 24 | 25 | #include "event2/http.h" 26 | #include "event2/http_struct.h" 27 | #include "event2/event.h" 28 | #include "event2/buffer.h" 29 | #include "event2/dns.h" 30 | #include "event2/thread.h" 31 | 32 | #include 33 | 34 | extern void error_handling(char *message); 35 | 36 | typedef struct { 37 | struct event_base *base; 38 | char *result; 39 | } result; 40 | 41 | 42 | ZEND_BEGIN_ARG_INFO_EX(ctor, 0, 0, 1) 43 | ZEND_ARG_INFO(0, address) 44 | ZEND_END_ARG_INFO() 45 | 46 | static PHP_METHOD(NsqLookupd, __construct); 47 | 48 | zend_class_entry *nsq_lookupd_ce; 49 | 50 | static const zend_function_entry nsq_lookupd_functions[] = { 51 | PHP_ME(NsqLookupd, __construct, ctor, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 52 | PHP_FE_END /* Must be the last line in nsq_functions[] */ 53 | 54 | }; 55 | 56 | void lookupd_init() { 57 | zend_class_entry nsq_lookupd; 58 | INIT_CLASS_ENTRY(nsq_lookupd, "NsqLookupd", nsq_lookupd_functions); 59 | nsq_lookupd_ce = zend_register_internal_class(&nsq_lookupd); 60 | zend_declare_property_null(nsq_lookupd_ce, ZEND_STRL("address"), ZEND_ACC_PUBLIC); 61 | } 62 | 63 | PHP_METHOD (NsqLookupd, __construct) { 64 | zval *self; 65 | zval *address; 66 | self = getThis(); 67 | ZEND_PARSE_PARAMETERS_START(1, 1) 68 | Z_PARAM_ZVAL(address) 69 | ZEND_PARSE_PARAMETERS_END(); 70 | zend_update_property(Z_OBJCE_P(self), NSQ_COMPAT_OBJ_P(self), ZEND_STRL("address"), address); 71 | } 72 | 73 | void FinshCallback(struct evhttp_request *remote_rsp, void *arg) { 74 | result *re = arg; 75 | const int code = remote_rsp ? evhttp_request_get_response_code(remote_rsp) : 0; 76 | struct evbuffer *buf = evhttp_request_get_input_buffer(remote_rsp); 77 | evbuffer_add(buf, "", 1); /* NUL-terminate the buffer */ 78 | char *payload = (char *) evbuffer_pullup(buf, -1); 79 | re->result = strdup(payload); 80 | event_base_loopbreak(re->base); 81 | //event_base_loopexit((struct event_base*)arg, NULL); 82 | } 83 | 84 | 85 | void RequestErrorCallback(enum evhttp_request_error *error, void *arg) { 86 | fprintf(stderr, "request failed\n"); 87 | event_base_loopexit((struct event_base *) arg, NULL); 88 | } 89 | 90 | void ConnectionCloseCallback(struct evhttp_connection *connection, void *arg) { 91 | fprintf(stderr, "remote connection closed\n"); 92 | event_base_loopexit((struct event_base *) arg, NULL); 93 | } 94 | 95 | char *lookup(char *host, char *topic) { 96 | char *url = emalloc(sizeof(host) + sizeof(topic) + 128); 97 | if (strstr(host, "http://")) { 98 | sprintf(url, "%s%s%s", host, "/lookup?topic=", topic); 99 | } else { 100 | sprintf(url, "%s%s%s%s", "http://", host, "/lookup?topic=", topic); 101 | } 102 | char *data = request(url); 103 | efree(url); 104 | return data; 105 | 106 | } 107 | 108 | char *request(char *url) { 109 | char *msg ; 110 | struct evhttp_uri *uri = evhttp_uri_parse(url); 111 | if (!uri) { 112 | fprintf(stderr, "parse url failed!\n"); 113 | msg = "{\"message\":\"parse url failed!\"}"; 114 | return msg; 115 | } 116 | 117 | struct event_base *base = event_base_new(); 118 | if (!base) { 119 | fprintf(stderr, "create event base failed!\n"); 120 | msg = "{\"message\":\"create event base failed!\"}"; 121 | return msg; 122 | } 123 | 124 | struct evdns_base *dnsbase = evdns_base_new(base, 1); 125 | if (!dnsbase) { 126 | fprintf(stderr, "create dns base failed!\n"); 127 | msg = "{\"message\":\"create dns base failed!\"}"; 128 | return msg; 129 | } 130 | assert(dnsbase); 131 | 132 | result * re = (result * ) emalloc ( sizeof(result) ); 133 | re->base = base; 134 | 135 | struct evhttp_request *request = evhttp_request_new(FinshCallback, re); 136 | //evhttp_request_set_error_cb(request, RequestErrorCallback); 137 | 138 | const char *host = evhttp_uri_get_host(uri); 139 | if (!host) { 140 | fprintf(stderr, "parse host failed!\n"); 141 | msg = "{\"message\":\"stderr, parse host failed!\"}"; 142 | return msg; 143 | } 144 | 145 | 146 | int port = evhttp_uri_get_port(uri); 147 | if (port < 0) port = 80; 148 | 149 | const char *request_url = url; 150 | const char *path = evhttp_uri_get_path(uri); 151 | if (path == NULL || strlen(path) == 0) { 152 | request_url = "/"; 153 | } 154 | 155 | struct evhttp_connection *connection = evhttp_connection_base_new(base, dnsbase, host, port); 156 | if (!connection) { 157 | fprintf(stderr, "create evhttp connection failed!\n"); 158 | msg = "{\"message\":\"create evhttp connection failed!\"}"; 159 | return msg; 160 | } 161 | 162 | evhttp_connection_set_closecb(connection, ConnectionCloseCallback, base); 163 | 164 | evhttp_add_header(evhttp_request_get_output_headers(request), "Host", host); 165 | evhttp_make_request(connection, request, EVHTTP_REQ_GET, request_url); 166 | 167 | event_base_dispatch(base); 168 | return re->result; 169 | } 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-nsq 2 | 3 | NSQ client for php7&php8
4 | 5 | ### install : 6 | 7 | Dependencies: libevent (apt-get install libevent-dev ,yum install libevent-devel, brew install libevent) 8 | 9 | pecl install nsq 10 | 11 | or: 12 | 13 | 1. sudo phpize 14 | 2. ./configure 15 | 3. make 16 | 4. make install 17 | 18 | add in your php.ini: 19 | 20 | extension = nsq.so; 21 | ---------------------------------------------------------------- 22 | 23 | Tips: 24 | 25 | if has the error "configure: error: Cannot find libevent headers" you should run : 26 | 27 | ./configure --with-php-config=/usr/local/php-7.2.12/bin/php-config --with-libevent-path=/usr/local/libevent-2.1.8-stable/ 28 | 29 | 30 | ### Example for pub: 31 | 32 | ```php 33 | // Normal publish 34 | $nsqdAddr = array( 35 | "127.0.0.1:4150", 36 | "127.0.0.1:4154" 37 | ); 38 | 39 | $nsq = new Nsq(); 40 | $isTrue = $nsq->connectNsqd($nsqdAddr); 41 | 42 | for($i = 0; $i < 10000; $i++){ 43 | $nsq->publish("test", "nihao"); 44 | } 45 | $nsq->closeNsqdConnection(); 46 | 47 | // Deferred publish 48 | //function : deferredPublish(string topic,string message, int millisecond); 49 | //millisecond default : [0 < millisecond < 3600000] 50 | 51 | $deferred = new Nsq(); 52 | $isTrue = $deferred->connectNsqd($nsqdAddr); 53 | for($i = 0; $i < 20; $i++){ 54 | $deferred->deferredPublish("test", "message daly", 3000); 55 | } 56 | $deferred->closeNsqdConnection(); 57 | 58 | ``` 59 | 60 | ### Example for sub: 61 | ```php 62 | "test", 70 | "channel" => "struggle", 71 | "rdy" => 2, //optional , default 1 72 | "connect_num" => 1, //optional , default 1 73 | "retry_delay_time" => 5000, //optional, default 0 , if run callback failed, after 5000 msec, message will be retried 74 | "auto_finish" => true, //default true 75 | ); 76 | 77 | $nsq->subscribe($nsq_lookupd, $config, function($msg,$bev){ 78 | 79 | echo $msg->payload."\n"; 80 | echo $msg->attempts."\n"; 81 | echo $msg->messageId."\n"; 82 | echo $msg->timestamp."\n"; 83 | 84 | 85 | }); 86 | 87 | ``` 88 | ### Nsq Object 89 | 90 | * `connectNsqd($nsqdAddrArr)`
91 | publish use, You can also use it for health check; 92 | 93 | * `closeNsqdConnection()`
94 | close connecNsqd's socket 95 | 96 | * `publish($topic,$msg)`
97 | 98 | * `deferredPublish($topic,$msg,$msec)`
99 | 100 | * `subscribe($nsq_lookupd,$config,$callback)`
101 | 102 | * `conn_timeout = 100`
103 | connection timeout for `connectNsqd()` in milliseconds 104 | 105 | ### Message Object 106 | 107 | The following properties and methods are available on Message objects produced by a Reader 108 | instance. 109 | 110 | * `timestamp`
111 | Numeric timestamp for the Message provided by nsqd. 112 | * `attempts`
113 | The number of attempts that have been made to process this message. 114 | * `messageId`
115 | The opaque string id for the Message provided by nsqd. 116 | * `payload`
117 | The message payload as a Buffer object. 118 | * `finish($bev,$msg->messageId)`
119 | Finish the message as successful. 120 | * `touch($bev,$msg->messageId)`
121 | Tell nsqd that you want extra time to process the message. It extends the 122 | soft timeout by the normal timeout amount. 123 | 124 | 125 | 126 | ### Tips : 127 | 128 | 129 | 1. `If you need some variable in callback ,you should use 'use' :`
130 | 131 | ``` 132 | $nsq->subscribe($nsq_lookupd, $config, function($msg,$bev) use ($you_variable){ 133 | 134 | echo $msg->payload; 135 | 136 | }); 137 | ``` 138 | 139 | 2. `Requeue/Retry -- if you whant to retry your message when callback have something wrong, just throw any Exception , example: 140 | `
141 | 142 | ``` 143 | subscribe($nsq_lookupd, $config, function($msg){ 146 | try{ 147 | echo $msg->payload . " " . "attempts:".$msg->attempts."\n"; 148 | //do something 149 | }catch(Exception $e){ 150 | 151 | if($msg->attempts < 3){ 152 | //the message will be retried after you configure retry_delay_time 153 | throw new Exception(""); 154 | }else{ 155 | echo $e->getMessage(); 156 | return; 157 | } 158 | } 159 | 160 | }); 161 | 162 | ``` 163 | 164 | 3. `If you want to increase your message timeout and heartbeats time ,Two steps are needed: `
165 | ``` 166 | #1 when nsqd startup you should set command line option: 167 | 168 | nsqd --lookupd-tcp-address=127.0.0.1:4160 --max-heartbeat-interval=1m30s --msg-timeout=10m30s 169 | 170 | 171 | #2 And , you should use identify config. For details, see the identify example file. 172 | 173 | 174 | ``` 175 | 176 | 4. `If your have strong consuming ability ,you can add you rdy num and connect num`
177 | 178 | 179 | 5. `If your execution time is more than 1 minute, you should use 'touch()' function `
180 | 181 | 5. `Do not support calling publish or deferredPublish in subscribe function, please use nsqd's HTTP interface.`
182 | 183 | ## Contributors 184 | 185 | Any form of contribution is welcome. 186 | 187 | 188 | ### Connection 189 | Wechat: yunnian_wechat 190 | 191 | Changes 192 | ------- 193 | * **3.9.1** 194 | * Fixed consumer not working on CentOS: Switched to timer-based solution, abandoned multi-process approach 195 | * **3.9.0** 196 | * fix connection be closed when function run over twice heartbeat time 197 | * **3.5.1** 198 | * fix publish return error when get the heartbeat 199 | * add exception 200 | * for PHP8.0 201 | * fix https://github.com/yunnian/php-nsq/issues/39 message are binary-safe now 202 | * **3.3.0** 203 | * add the process management 204 | * When the child process exits abnormally, it will pull up a new child process 205 | * When the master process exits, all child processes also exit 206 | * **3.2.0** 207 | * Fix The error message was not reported 208 | * Fix pub error when ip or url too long 209 | * **3.1.0** 210 | * Fix memmory wrong 211 | * Fix subscribe wrong 212 | * **3.0** 213 | * Fix libevent more than 4096 bytes are truncated 214 | * add the identify command,can use be set or increase heartbeats time and msg-timeout 215 | -------------------------------------------------------------------------------- /pub.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include "php.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "pub.h" 26 | #include "common.h" 27 | #include "ext/standard/php_var.h" 28 | #include 29 | #include 30 | #include "ext/standard/php_smart_string_public.h" 31 | #include "ext/json/php_json.h" 32 | #include "zend_smart_str.h" 33 | #include 34 | #include "nsq_exception.h" 35 | #include "zend_exceptions.h" 36 | 37 | extern void error_handlings(char *message); 38 | //typedef void (*sighandler_t)(int); 39 | //void respond_hearbeat(int sock); 40 | 41 | void nsq_conf_timeout(zval *nsq_obj, struct timeval *timeout) 42 | { 43 | zval *conn_timeout; 44 | zval rv3; 45 | 46 | if (!nsq_obj || !timeout) 47 | return; 48 | 49 | conn_timeout = zend_read_property(Z_OBJCE_P(nsq_obj), NSQ_COMPAT_OBJ_P(nsq_obj), "conn_timeout", sizeof("conn_timeout") - 1, 1, &rv3); 50 | if(Z_TYPE_P(conn_timeout) != IS_LONG || Z_LVAL_P(conn_timeout) < 0) 51 | return; 52 | 53 | timeout->tv_sec = Z_LVAL_P(conn_timeout) / 1000; 54 | timeout->tv_usec = (Z_LVAL_P(conn_timeout) % 1000) * 1000; 55 | } 56 | 57 | int * connect_nsqd(zval *nsq_obj, nsqd_connect_config *connect_config_arr, int connect_num) { 58 | int *sock_arr = emalloc(connect_num * sizeof(int)); 59 | zval *fds; 60 | zval *val; 61 | zval rv3; 62 | struct hostent *he; 63 | struct timeval timeout; 64 | 65 | fds = zend_read_property(Z_OBJCE_P(nsq_obj), NSQ_COMPAT_OBJ_P(nsq_obj), "nsqd_connection_fds", sizeof("nsqd_connection_fds") - 1, 1, 66 | &rv3); 67 | 68 | if (Z_TYPE_P(fds) != IS_NULL) { 69 | int i = 0; 70 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(fds), val){ 71 | sock_arr[i] = Z_LVAL_P(val); 72 | i++; 73 | }ZEND_HASH_FOREACH_END(); 74 | return sock_arr; 75 | } 76 | 77 | memset(&timeout, 0, sizeof(timeout)); 78 | nsq_conf_timeout(nsq_obj, &timeout); 79 | 80 | int i; 81 | for (i = 0; i < connect_num; i++) { 82 | struct sockaddr_in serv_addr; 83 | memset(&serv_addr, 0, sizeof(serv_addr)); 84 | sock_arr[i] = socket(PF_INET, SOCK_STREAM, 0); 85 | if (sock_arr[i] == -1) { 86 | error_handlings("socket() error"); 87 | } 88 | 89 | serv_addr.sin_family = AF_INET; 90 | 91 | if (check_ipaddr(connect_config_arr->host)) { 92 | serv_addr.sin_addr.s_addr = inet_addr(connect_config_arr->host); 93 | } else { 94 | /* resolve hostname */ 95 | if ((he = gethostbyname(connect_config_arr->host)) == NULL) { 96 | exit(1); /* error */ 97 | } 98 | /* copy the network address to sockaddr_in structure */ 99 | memcpy(&serv_addr.sin_addr, he->h_addr_list[0], he->h_length); 100 | } 101 | serv_addr.sin_port = htons(atoi(connect_config_arr->port)); 102 | if (i < connect_num - 1) { 103 | connect_config_arr--; 104 | } 105 | 106 | if (timeout.tv_usec > 0 || timeout.tv_sec > 0) 107 | setsockopt(sock_arr[i], SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); 108 | 109 | if (connect(sock_arr[i], (struct sockaddr *) &serv_addr, sizeof(serv_addr)) == -1) { 110 | error_handlings("connect() error"); 111 | sock_arr[i] = 0; 112 | continue; 113 | } 114 | 115 | // reset timeout to default behaviour 116 | if (timeout.tv_usec > 0 || timeout.tv_sec > 0) { 117 | timeout.tv_sec = 0; 118 | timeout.tv_usec = 0; 119 | setsockopt(sock_arr[i], SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); 120 | } 121 | 122 | int flags = fcntl(sock_arr[i], F_GETFL, 0); 123 | fcntl(sock_arr[i], F_SETFL, flags | O_NONBLOCK); 124 | char *msgs = (char *) emalloc(4); 125 | memcpy(msgs, " V2", 4); 126 | int r = send((sock_arr[i]), msgs, 4, MSG_DONTWAIT); 127 | send_identify(nsq_obj, sock_arr[i]); 128 | efree(msgs); 129 | } 130 | 131 | zval fd_arr; 132 | array_init(&fd_arr); 133 | for (i = 0; i < connect_num; i++) { 134 | if (!(sock_arr[i] > 0)) { 135 | zval_dtor(&fd_arr); 136 | return sock_arr; 137 | } 138 | zval fd_val; 139 | ZVAL_LONG(&fd_val, sock_arr[i]); 140 | zend_hash_index_add(Z_ARRVAL(fd_arr), i, &fd_val); 141 | //zval_dtor(&fd_val); 142 | } 143 | 144 | 145 | zend_update_property(Z_OBJCE_P(nsq_obj), NSQ_COMPAT_OBJ_P(nsq_obj), ZEND_STRL("nsqd_connection_fds"), &fd_arr); 146 | zval_dtor(&fd_arr); 147 | //zval_dtor(&rv3); 148 | return sock_arr; 149 | } 150 | 151 | 152 | extern int errno; 153 | 154 | int publish(int sock, char *topic, char *msg, size_t msg_len) { 155 | char buf[1024 * 1024]; 156 | 157 | size_t ofs = 0; 158 | // write command 159 | // PUB /n 160 | // write command 161 | strncpy(&buf[ofs], "PUB ", 4); 162 | ofs+=4; 163 | // write topic 164 | strcpy(&buf[ofs], topic); 165 | ofs+=strlen(topic); 166 | // write command delimiter 167 | buf[ofs] = '\n'; 168 | ofs+=1; 169 | 170 | // write msg len 171 | int len = htonl(msg_len); 172 | memcpy(&buf[ofs], &len, 4); 173 | ofs+=4; 174 | // write msg 175 | memcpy(&buf[ofs], msg, msg_len); 176 | ofs+=msg_len; 177 | 178 | int send_len = send(sock, buf, ofs, 0); 179 | if ( -1 == send_len) { 180 | printf("%d, send error :%s\n",__LINE__,strerror(errno)); 181 | } 182 | if( send_len == 0 ){ 183 | throw_exception(PHP_NSQ_ERROR_PUB_LOST_CONNECTION); 184 | return -1; 185 | } 186 | 187 | int l ; 188 | int msg_size; 189 | char *message; 190 | char *msg_size_char = malloc(4); 191 | int size; 192 | 193 | again_read: 194 | l = 0; 195 | memset(msg_size_char, 0x00, 4); 196 | size = read(sock, msg_size_char, 4); 197 | if( size == 0 ){ 198 | throw_exception(PHP_NSQ_ERROR_PUB_LOST_CONNECTION); 199 | free(msg_size_char); 200 | return -1; 201 | } 202 | if(size == -1){ 203 | goto again_read; 204 | } 205 | 206 | readI32((const unsigned char *) msg_size_char, &msg_size); 207 | message = emalloc(msg_size + 1); 208 | memset(message, 0x00, msg_size); 209 | again: 210 | l += read(sock, message +l , msg_size); 211 | if( l < msg_size && l>0){ 212 | goto again; 213 | 214 | } 215 | if (strcmp(message + 4, "OK") == 0) { 216 | efree(message); 217 | free(msg_size_char); 218 | return sock; 219 | } else if ( strcmp(message + 4, "_heartbeat_") == 0 ) { 220 | // Send heartbeat response immediately 221 | send(sock, "NOP\n", 4, 0); 222 | efree(message); 223 | goto again_read; 224 | }else{ 225 | efree(message); 226 | free(msg_size_char); 227 | return -1; 228 | } 229 | 230 | } 231 | 232 | int deferredPublish(int sock, char *topic, char *msg, size_t msg_len, int defer_time) { 233 | char buf[1024 * 1024]; 234 | 235 | size_t ofs = 0; 236 | // write command 237 | // DPUB /n 238 | // write command 239 | strncpy(&buf[ofs], "DPUB ", 5); 240 | ofs+=5; 241 | // write topic 242 | strcpy(&buf[ofs], topic); 243 | ofs+=strlen(topic); 244 | buf[ofs] = ' '; 245 | ofs+=1; 246 | // write defer_time 247 | int defer_len = sprintf(&buf[ofs], "%lld", defer_time); 248 | ofs+=defer_len; 249 | // write command delimiter 250 | buf[ofs] = '\n'; 251 | ofs+=1; 252 | 253 | // write msg len 254 | int len = htonl(msg_len); 255 | memcpy(&buf[ofs], &len, 4); 256 | ofs+=4; 257 | // write msg 258 | memcpy(&buf[ofs], msg, msg_len); 259 | ofs+=msg_len; 260 | 261 | send(sock, buf, ofs, 0); 262 | 263 | int l ; 264 | int msg_size; 265 | char *message; 266 | char *msg_size_char = malloc(4); 267 | int size; 268 | 269 | again_read: 270 | l = 0; 271 | memset(msg_size_char, 0x00, 4); 272 | size = read(sock, msg_size_char, 4); 273 | if( size == 0 ){ 274 | throw_exception(PHP_NSQ_ERROR_PUB_LOST_CONNECTION); 275 | free(msg_size_char); 276 | return -1; 277 | } 278 | if(size == -1){ 279 | goto again_read; 280 | } 281 | 282 | readI32((const unsigned char *) msg_size_char, &msg_size); 283 | message = emalloc(msg_size + 1); 284 | memset(message, 0x00, msg_size); 285 | again: 286 | l += read(sock, message +l , msg_size); 287 | if( l < msg_size && l>0){ 288 | goto again; 289 | 290 | } 291 | if (strcmp(message + 4, "OK") == 0) { 292 | efree(message); 293 | free(msg_size_char); 294 | return sock; 295 | } else if ( strcmp(message + 4, "_heartbeat_") == 0 ) { 296 | // Send heartbeat response immediately 297 | send(sock, "NOP\n", 4, 0); 298 | efree(message); 299 | goto again_read; 300 | }else{ 301 | efree(message); 302 | free(msg_size_char); 303 | return -1; 304 | } 305 | } 306 | // Heartbeat handling has been fixed - both publish() and deferredPublish() 307 | // now properly respond to heartbeats with "NOP\n" to maintain connection 308 | 309 | 310 | -------------------------------------------------------------------------------- /sub.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Copyright (c) 1997-2017 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: Zhenyu Wu | 14 | +----------------------------------------------------------------------+ 15 | */ 16 | 17 | #include "php.h" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "sub.h" 27 | #include "command.h" 28 | #include "common.h" 29 | #include "ext/standard/php_var.h" 30 | #include "nsq_exception.h" 31 | #include "zend_exceptions.h" 32 | 33 | extern zend_class_entry *nsq_message_ce; 34 | 35 | extern void error_handlings(char *message); 36 | 37 | void conn_writecb(struct bufferevent *, void *); 38 | 39 | void readcb(struct bufferevent *, void *msg); 40 | 41 | void conn_eventcb(struct bufferevent *, short, void *); 42 | 43 | void cleanup_message_queue(); 44 | 45 | extern int le_bufferevent; 46 | 47 | // Message queue structure - for caching business messages to be processed 48 | typedef struct pending_message { 49 | char message_id[17]; 50 | char *body; 51 | size_t body_len; 52 | int64_t timestamp; 53 | uint16_t attempts; 54 | int delay_time; 55 | int auto_finish; 56 | struct pending_message *next; 57 | } pending_message_t; 58 | 59 | // Global message queue 60 | static pending_message_t *message_queue_head = NULL; 61 | static pending_message_t *message_queue_tail = NULL; 62 | static int pending_message_count = 0; 63 | 64 | // Add message to queue 65 | void enqueue_message(const char *message_id, const char *body, size_t body_len, 66 | int64_t timestamp, uint16_t attempts, int delay_time, int auto_finish) { 67 | pending_message_t *msg = malloc(sizeof(pending_message_t)); 68 | if (!msg) return; // Memory allocation check 69 | 70 | memcpy(msg->message_id, message_id, 16); 71 | msg->message_id[16] = '\0'; 72 | msg->body = malloc(body_len + 1); 73 | if (!msg->body) { 74 | free(msg); 75 | return; // Memory allocation check 76 | } 77 | 78 | memcpy(msg->body, body, body_len); 79 | msg->body[body_len] = '\0'; 80 | msg->body_len = body_len; 81 | msg->timestamp = timestamp; 82 | msg->attempts = attempts; 83 | msg->delay_time = delay_time; 84 | msg->auto_finish = auto_finish; 85 | msg->next = NULL; 86 | 87 | if (message_queue_tail) { 88 | message_queue_tail->next = msg; 89 | message_queue_tail = msg; 90 | } else { 91 | message_queue_head = message_queue_tail = msg; 92 | } 93 | pending_message_count++; 94 | } 95 | 96 | // Get message from queue 97 | pending_message_t* dequeue_message() { 98 | if (!message_queue_head) return NULL; 99 | 100 | pending_message_t *msg = message_queue_head; 101 | message_queue_head = msg->next; 102 | if (!message_queue_head) { 103 | message_queue_tail = NULL; 104 | } 105 | pending_message_count--; 106 | 107 | return msg; 108 | } 109 | 110 | // Function to process single business message 111 | void process_business_message(pending_message_t *msg, zend_fcall_info *fci, zend_fcall_info_cache *fcc, 112 | zend_resource *bev_res, struct bufferevent *bev, struct NSQMsg *nsq_msg) { 113 | 114 | // Create PHP object and call user function 115 | zval retval; 116 | zval params[2]; 117 | zval msg_object; 118 | zval message_id; 119 | zval attempts; 120 | zval payload; 121 | zval timestamp; 122 | 123 | object_init_ex(&msg_object, nsq_message_ce); 124 | 125 | //message_id 126 | zend_string *message_id_str = zend_string_init(msg->message_id, 16, 0); 127 | ZVAL_STR_COPY(&message_id, message_id_str); 128 | zend_update_property(nsq_message_ce, NSQ_COMPAT_OBJ_P(&msg_object), ZEND_STRL("message_id"), &message_id); 129 | zend_update_property(nsq_message_ce, NSQ_COMPAT_OBJ_P(&msg_object), ZEND_STRL("messageId"), &message_id); 130 | 131 | //attempts 132 | ZVAL_LONG(&attempts, msg->attempts); 133 | zend_update_property(nsq_message_ce, NSQ_COMPAT_OBJ_P(&msg_object), ZEND_STRL("attempts"), &attempts); 134 | //timestamp 135 | ZVAL_LONG(×tamp, msg->timestamp); 136 | zend_update_property(nsq_message_ce, NSQ_COMPAT_OBJ_P(&msg_object), ZEND_STRL("timestamp"), ×tamp); 137 | 138 | //payload 139 | zend_string *payload_str = zend_string_init(msg->body, msg->body_len, 0); 140 | ZVAL_STR_COPY(&payload, payload_str); 141 | zend_update_property(nsq_message_ce, NSQ_COMPAT_OBJ_P(&msg_object), ZEND_STRL("payload"), &payload); 142 | 143 | //call function 144 | ZVAL_OBJ(¶ms[0], Z_OBJ(msg_object)); 145 | ZVAL_RES(¶ms[1], bev_res); 146 | fci->params = params; 147 | fci->param_count = 2; 148 | fci->retval = &retval; 149 | 150 | int callback_success = 0; 151 | if (zend_call_function(fci, fcc) == SUCCESS) { 152 | if (!EG(exception)) { 153 | callback_success = 1; 154 | } else { 155 | zend_clear_exception(); 156 | } 157 | } 158 | 159 | // Send FIN/REQ based on callback result 160 | if (msg->auto_finish) { 161 | if (callback_success) { 162 | char fin_cmd[64]; 163 | snprintf(fin_cmd, sizeof(fin_cmd), "FIN %s\n", msg->message_id); 164 | bufferevent_write(bev, fin_cmd, strlen(fin_cmd)); 165 | } else { 166 | char req_cmd[128]; 167 | snprintf(req_cmd, sizeof(req_cmd), "REQ %s %d\n", msg->message_id, msg->delay_time); 168 | bufferevent_write(bev, req_cmd, strlen(req_cmd)); 169 | } 170 | } 171 | 172 | //free memory 173 | zval_dtor(¶ms[0]); 174 | zend_string_release(payload_str); 175 | zend_string_release(message_id_str); 176 | zval_dtor(×tamp); 177 | zval_dtor(&retval); 178 | zval_dtor(&message_id); 179 | zval_dtor(&attempts); 180 | zval_dtor(&payload); 181 | } 182 | 183 | // Message processing event callback - use timer to periodically process messages in queue 184 | void process_message_queue(evutil_socket_t fd, short events, void *arg) { 185 | struct NSQArg *nsq_arg = (struct NSQArg *)arg; 186 | struct bufferevent *bev = (struct bufferevent *)nsq_arg->bev_res->ptr; 187 | struct NSQMsg *nsq_msg = nsq_arg->msg; 188 | 189 | // Process at most one message at a time to ensure timely heartbeat processing 190 | pending_message_t *msg = dequeue_message(); 191 | if (msg) { 192 | process_business_message(msg, nsq_arg->fci, nsq_arg->fcc, nsq_arg->bev_res, bev, nsq_msg); 193 | 194 | // Clean up message 195 | free(msg->body); 196 | free(msg); 197 | 198 | // Send RDY to prepare for next message 199 | nsq_ready(bev, nsq_msg->rdy); 200 | } 201 | } 202 | 203 | int subscribe(NSQArg *arg) { 204 | 205 | struct sockaddr_in srv; 206 | struct hostent *he; 207 | memset(&srv, 0, sizeof(srv)); 208 | int retry_num = 1; 209 | 210 | if (check_ipaddr(arg->host)) { 211 | srv.sin_addr.s_addr = inet_addr(arg->host); 212 | } else { 213 | /* resolve hostname */ 214 | if ((he = gethostbyname(arg->host)) == NULL) { 215 | exit(1); /* error */ 216 | } 217 | /* copy the network address to sockaddr_in structure */ 218 | memcpy(&srv.sin_addr, he->h_addr_list[0], he->h_length); 219 | } 220 | srv.sin_family = AF_INET; 221 | srv.sin_port = htons(atoi(arg->port)); 222 | struct event_base *base = event_base_new(); 223 | if (!base) { 224 | throw_exception(PHP_NSQ_ERROR_LIBEVENT_COULD_NOT_BE_INITIALIZED); 225 | return 1; 226 | } 227 | 228 | struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 229 | arg->bev_res = zend_register_resource(bev, le_bufferevent); 230 | 231 | bufferevent_setcb(bev, readcb, NULL, conn_eventcb, (void *) arg); 232 | int flag = bufferevent_socket_connect(bev, (struct sockaddr *) &srv, sizeof(srv)); 233 | bufferevent_enable(bev, EV_READ | EV_WRITE); 234 | 235 | // Add timer to periodically process message queue (0ms interval, fastest response) 236 | struct timeval tv = {0, 1000}; // 1000 milliseconds - fastest processing 237 | struct event *process_event = event_new(base, -1, EV_PERSIST, process_message_queue, (void *)arg); 238 | event_add(process_event, &tv); 239 | 240 | if (-1 == flag) { 241 | throw_exception(PHP_NSQ_ERROR_CONNECTION_FAILED); 242 | return 1; 243 | } 244 | 245 | event_base_dispatch(base); 246 | 247 | // Cleanup on exit 248 | cleanup_message_queue(); 249 | event_base_free(base); 250 | return 1; 251 | } 252 | 253 | void conn_eventcb(struct bufferevent *bev, short events, void *user_data) { 254 | if (events & BEV_EVENT_EOF) { 255 | printf("Connection closed ,retrying\n"); 256 | subscribe((NSQArg *) user_data); 257 | } else if (events & BEV_EVENT_ERROR) { 258 | printf("Got an error on the connection: %s, retry agin\n", strerror(errno)); 259 | //close fd 260 | sleep(1); 261 | bufferevent_free(bev); 262 | subscribe((NSQArg *) user_data); 263 | } else if (events & BEV_EVENT_CONNECTED) { 264 | struct NSQMsg *msg = ((struct NSQArg *) user_data)->msg; 265 | char *v = (char *) emalloc(4); 266 | memcpy(v, " V2", 4); 267 | evutil_socket_t fd = bufferevent_getfd(bev); 268 | int res = write(fd, v, 4); 269 | efree(v); 270 | send_identify(((struct NSQArg *) user_data)->nsq_obj, fd); 271 | 272 | nsq_subscribe(bev, msg->topic, msg->channel); 273 | nsq_ready(bev, msg->rdy); 274 | 275 | return; 276 | } 277 | 278 | bufferevent_free(bev); 279 | } 280 | 281 | void readcb(struct bufferevent *bev, void *arg) { 282 | static struct NSQMsg *msg = NULL; 283 | static int is_first = 1; 284 | static int l = 0; 285 | static char *message = NULL; 286 | 287 | msg = ((struct NSQArg *) arg)->msg; 288 | int auto_finish = msg->auto_finish; 289 | zend_fcall_info *fci = ((struct NSQArg *) arg)->fci;; 290 | zend_fcall_info_cache *fcc = ((struct NSQArg *) arg)->fcc; 291 | errno = 0; 292 | int i = 0; 293 | 294 | while (1){ 295 | 296 | if(is_first){ 297 | char *msg_size = emalloc(4); 298 | memset(msg_size, 0x00, 4); 299 | size_t size_l = bufferevent_read(bev, msg_size, 4); 300 | readI32((const unsigned char *) msg_size, &msg->size); 301 | 302 | message = emalloc(msg->size + 1); 303 | memset(message, 0x00, msg->size); 304 | efree(msg_size); 305 | } 306 | 307 | l += bufferevent_read(bev, message + l, msg->size - l ); 308 | 309 | if(l < msg->size){ 310 | 311 | is_first = 0; 312 | break; 313 | } 314 | 315 | if (errno) { 316 | //printf("errno = %d\n", errno); // errno = 33 317 | //printf("error: %s\n", strerror(errno)); 318 | } 319 | if (l == msg->size) { 320 | readI32((const unsigned char *) message, &msg->frame_type); 321 | 322 | if (msg->frame_type == 0) { 323 | // Heartbeat message - respond immediately 324 | if (msg->size == 15) { 325 | bufferevent_write(bev, "NOP\n", strlen("NOP\n")); 326 | // this is response OK 327 | }else if (msg->size == 6){ 328 | //nothing 329 | } 330 | l = 0; 331 | is_first = 1; 332 | efree(message); 333 | if(msg->size !=0){ 334 | memset(&msg->size, 0x00, 4); 335 | continue; 336 | }else{ 337 | break; 338 | } 339 | break; 340 | } else if (msg->frame_type == 2) { 341 | // Business message - put into queue for processing 342 | 343 | char message_id[17]; 344 | memset(message_id, '\0', 17); 345 | 346 | int64_t timestamp = (int64_t) ntoh64((const unsigned char *) message + 4); 347 | uint16_t attempts; 348 | readI16((const unsigned char *) message + 12, &attempts); 349 | 350 | memcpy(message_id, message + 14, 16); 351 | 352 | char *body = (char *) malloc(msg->size - 30 + 1); 353 | if (body) { // Memory allocation check 354 | memset(body, '\0', msg->size - 30 + 1); 355 | memcpy(body, message + 30, msg->size - 30); 356 | 357 | // Put message into queue 358 | enqueue_message(message_id, body, msg->size - 30, timestamp, attempts, 359 | msg->delay_time, auto_finish); 360 | 361 | // Free temporary memory 362 | free(body); 363 | } 364 | 365 | memset(&msg->size, 0x00, 4); 366 | efree(message); 367 | l = 0; 368 | is_first = 1; 369 | } 370 | } else { 371 | memset(&msg->size, 0x00, 4); 372 | efree(message); 373 | l = 0; 374 | is_first = 1; 375 | break; 376 | } 377 | if (l == -1) { 378 | error_handlings("read() error"); 379 | l = 0; 380 | is_first = 1; 381 | break; 382 | } 383 | 384 | } 385 | } 386 | 387 | void conn_writecb(struct bufferevent *bev, void *user_data) { 388 | } 389 | 390 | // Clean up all messages in queue (for shutdown) 391 | void cleanup_message_queue() { 392 | while (message_queue_head) { 393 | pending_message_t *msg = dequeue_message(); 394 | if (msg) { 395 | free(msg->body); 396 | free(msg); 397 | } 398 | } 399 | } 400 | 401 | -------------------------------------------------------------------------------- /nsq.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 7 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2017 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include "php.h" 24 | #include "ext/standard/php_var.h" 25 | #include "ext/standard/info.h" 26 | #include "ext/json/php_json.h" 27 | #include "php_nsq.h" 28 | #include "common.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "ext/standard/php_string.h" 36 | #include "sub.h" 37 | #include "pub.h" 38 | #include "message.h" 39 | #include "nsq_lookupd.h" 40 | #include "nsq_exception.h" 41 | //#include 42 | 43 | #ifdef HAVE_SYS_WAIT_H 44 | #include "sys/wait.h" 45 | #endif 46 | 47 | /* If you declare any globals in php_nsq.h uncomment this: 48 | ZEND_DECLARE_MODULE_GLOBALS(nsq) 49 | */ 50 | 51 | /* True global resources - no need for thread safety here */ 52 | static int le_nsq; 53 | int le_bufferevent; 54 | 55 | /* {{{ PHP_INI 56 | */ 57 | /* Remove comments and fill if you need to have entries in php.ini 58 | PHP_INI_BEGIN() 59 | STD_PHP_INI_ENTRY("nsq.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_nsq_globals, nsq_globals) 60 | STD_PHP_INI_ENTRY("nsq.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_nsq_globals, nsq_globals) 61 | PHP_INI_END() 62 | */ 63 | /* }}} */ 64 | 65 | /* Remove the following function when you have successfully modified config.m4 66 | so that your module can be compiled into PHP, it exists only for testing 67 | purposes. */ 68 | 69 | /* Every user-visible function in PHP should document itself in the source */ 70 | /* {{{ proto string confirm_nsq_compiled(string arg) 71 | Return a string to confirm that the module is compiled in */ 72 | 73 | zend_class_entry *nsq_ce/*, *nsq_message_exception*/; 74 | 75 | static void signal_handle(int sig); 76 | 77 | PHP_METHOD(Nsq, __construct){ 78 | zval *self; 79 | zval *nsq_config = (zval *)malloc(sizeof(zval)); //use send IDENTIFY comand 80 | bzero(nsq_config, sizeof(zval)); 81 | ZVAL_NULL(nsq_config); 82 | self = getThis(); 83 | ZEND_PARSE_PARAMETERS_START(0,1) 84 | Z_PARAM_OPTIONAL 85 | Z_PARAM_ZVAL(nsq_config) 86 | ZEND_PARSE_PARAMETERS_END(); 87 | 88 | if(Z_TYPE_P(nsq_config) != IS_NULL){ 89 | zend_update_property(Z_OBJCE_P(self),NSQ_COMPAT_OBJ_P(self),ZEND_STRL("nsqConfig"), nsq_config); 90 | } 91 | } 92 | 93 | 94 | 95 | PHP_METHOD (Nsq, connectNsqd) 96 | { 97 | zval *connect_addr_arr; 98 | zval *val; 99 | zval explode_re; 100 | 101 | zend_string *delim = zend_string_init(":", sizeof(":") - 1, 0); 102 | ZEND_PARSE_PARAMETERS_START(1, 1) 103 | Z_PARAM_ARRAY(connect_addr_arr) 104 | ZEND_PARSE_PARAMETERS_END(); 105 | 106 | int count = zend_array_count(Z_ARRVAL_P(connect_addr_arr)); 107 | nsqd_connect_config *connect_config_arr = emalloc(count * sizeof(nsqd_connect_config)); 108 | memset(connect_config_arr, 0, count * sizeof(nsqd_connect_config)); 109 | int j = 0, i, h; 110 | zval *host, *port; 111 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(connect_addr_arr), val) { 112 | array_init(&explode_re); 113 | php_explode(delim, Z_STR_P(val), &explode_re, 1); 114 | host = zend_hash_index_find(Z_ARRVAL_P(&explode_re), 0); 115 | port = zend_hash_index_find(Z_ARRVAL_P(&explode_re), 1); 116 | connect_config_arr->port = emalloc(Z_STRLEN_P(port)); 117 | connect_config_arr->host = emalloc(Z_STRLEN_P(host)); 118 | strcpy(connect_config_arr->host, Z_STRVAL_P(host)); 119 | strcpy(connect_config_arr->port, Z_STRVAL_P(port)); 120 | j++; 121 | if (j < count) { 122 | connect_config_arr++; 123 | } 124 | zval_dtor(&explode_re); 125 | 126 | } ZEND_HASH_FOREACH_END(); 127 | int * sock_arr = connect_nsqd(getThis(), connect_config_arr, count); 128 | 129 | for (h = 0; h < count; h++) { 130 | efree(connect_config_arr->host); 131 | efree(connect_config_arr->port); 132 | if (h < count - 1) { 133 | connect_config_arr--; 134 | } 135 | 136 | } 137 | efree(connect_config_arr); 138 | 139 | zend_string_release(delim); 140 | //zval_dtor(host); 141 | //zval_dtor(val); 142 | //zval_dtor(port); 143 | int sock_is_true = 1; 144 | for (i = 0; i < count; i++) { 145 | if (!(sock_arr[i] > 0)) { 146 | sock_is_true = 0; 147 | } 148 | } 149 | efree(sock_arr); 150 | if(sock_is_true){ 151 | RETURN_TRUE; 152 | }else{ 153 | RETURN_FALSE; 154 | } 155 | } 156 | 157 | PHP_METHOD (Nsq, closeNsqdConnection) 158 | { 159 | zval *connection_fds; 160 | zval rv3; 161 | zval *val; 162 | connection_fds = zend_read_property(Z_OBJCE_P(getThis()), NSQ_COMPAT_OBJ_P(getThis()), "nsqd_connection_fds", sizeof("nsqd_connection_fds") - 1, 163 | 1, &rv3); 164 | int count = zend_array_count(Z_ARRVAL_P(connection_fds)); 165 | if(count == 0){ 166 | throw_exception(PHP_NSQ_ERROR_NO_CONNECTION); 167 | } 168 | int close_success = 1; 169 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(connection_fds), val) { 170 | if(Z_LVAL_P(val)>0){ 171 | int success = close(Z_LVAL_P(val)); 172 | if(success != 0){ 173 | close_success = 0; 174 | } 175 | }; 176 | } ZEND_HASH_FOREACH_END(); 177 | zval_ptr_dtor(connection_fds); 178 | ZVAL_NULL(connection_fds); 179 | if(close_success){ 180 | RETURN_TRUE; 181 | }else{ 182 | RETURN_FALSE; 183 | } 184 | } 185 | 186 | PHP_METHOD (Nsq, publish) 187 | { 188 | zval *topic; 189 | zval *msg; 190 | zval *val; 191 | zval *sock; 192 | zval rv3; 193 | 194 | ZEND_PARSE_PARAMETERS_START(2, 2) 195 | Z_PARAM_ZVAL(topic) 196 | Z_PARAM_ZVAL(msg) 197 | ZEND_PARSE_PARAMETERS_END(); 198 | val = zend_read_property(Z_OBJCE_P(getThis()), NSQ_COMPAT_OBJ_P(getThis()), "nsqd_connection_fds", sizeof("nsqd_connection_fds") - 1, 199 | 1, &rv3); 200 | int count = zend_array_count(Z_ARRVAL_P(val)); 201 | if(count == 0){ 202 | throw_exception(PHP_NSQ_ERROR_UNABLE_TO_PUBLISH_MESSAGE); 203 | } 204 | int r = rand() % count; 205 | sock = zend_hash_index_find(Z_ARRVAL_P(val), r); 206 | 207 | convert_to_string(msg); 208 | int re = publish(Z_LVAL_P(sock), Z_STRVAL_P(topic), Z_STRVAL_P(msg), Z_STRLEN_P(msg)); 209 | //zval_dtor(&rv3); 210 | //zval_ptr_dtor(msg); 211 | //zval_dtor(sock); 212 | if (re > 0) { 213 | RETURN_TRUE; 214 | } else { 215 | RETURN_FALSE; 216 | } 217 | } 218 | 219 | PHP_METHOD (Nsq, deferredPublish) 220 | { 221 | zval *topic; 222 | zval *delay_time; 223 | zval *msg; 224 | zval *val; 225 | zval *sock; 226 | zval rv3; 227 | 228 | ZEND_PARSE_PARAMETERS_START(3, 3) 229 | Z_PARAM_ZVAL(topic) 230 | Z_PARAM_ZVAL(msg) 231 | Z_PARAM_ZVAL(delay_time) 232 | ZEND_PARSE_PARAMETERS_END(); 233 | val = zend_read_property(Z_OBJCE_P(getThis()), NSQ_COMPAT_OBJ_P(getThis()), "nsqd_connection_fds", sizeof("nsqd_connection_fds") - 1, 234 | 1, &rv3); 235 | int count = zend_array_count(Z_ARRVAL_P(val)); 236 | if(count == 0){ 237 | throw_exception(PHP_NSQ_ERROR_UNABLE_TO_PUBLISH_MESSAGE); 238 | } 239 | int r = rand() % count; 240 | sock = zend_hash_index_find(Z_ARRVAL_P(val), r); 241 | 242 | convert_to_string(msg); 243 | int re = deferredPublish(Z_LVAL_P(sock), Z_STRVAL_P(topic), Z_STRVAL_P(msg), Z_STRLEN_P(msg), Z_LVAL_P(delay_time)); 244 | if (re > 0) { 245 | RETURN_TRUE; 246 | } else { 247 | RETURN_FALSE; 248 | } 249 | } 250 | 251 | 252 | HashTable *child_fd; 253 | int is_init = 0; 254 | pid_t master = 0; 255 | ArgPidArr *arg_arr; 256 | int nsqd_num ; 257 | 258 | void start_worker_process(NSQArg arg, int index) 259 | { 260 | 261 | zval zval_pid; 262 | zval zval_arg; 263 | pid_t pid; 264 | pid = fork(); 265 | 266 | if (pid == 0) { 267 | subscribe(&arg); 268 | }else if(pid > 0 ){ 269 | if(!is_init) { 270 | master = getpid(); 271 | signal(SIGCHLD, signal_handle); 272 | signal(SIGTERM, signal_handle); 273 | 274 | // init hash table 275 | ALLOC_HASHTABLE(child_fd); 276 | zend_hash_init(child_fd, 0, NULL, ZVAL_PTR_DTOR, 1); 277 | is_init = 1; 278 | 279 | } 280 | 281 | arg_arr[index].pid = pid; 282 | arg_arr[index].arg = arg; 283 | 284 | ZVAL_LONG(&zval_pid, pid); 285 | zval *fd_res = zend_hash_next_index_insert(child_fd, &zval_pid); 286 | } 287 | } 288 | 289 | 290 | static void signal_handle(int sig) 291 | { 292 | int status; 293 | pid_t pid; 294 | zend_ulong index; 295 | zval *val; 296 | int count; 297 | pid_t current = getpid(); 298 | switch (sig) 299 | { 300 | case SIGTERM: 301 | if(current == master){ 302 | count = zend_array_count(child_fd); 303 | // quit all child 304 | ZEND_HASH_FOREACH_NUM_KEY_VAL(child_fd, index, val); 305 | kill(Z_LVAL_P(val), SIGTERM); 306 | ZEND_HASH_FOREACH_END(); 307 | } 308 | 309 | exit(0); 310 | break; 311 | /** 312 | * TODO reload all workers 313 | */ 314 | case SIGUSR1: 315 | break; 316 | case SIGCHLD: 317 | /* 318 | while((pid = waitpid(-1, &status, WNOHANG)) > 0){ 319 | printf("child %d terminated, will reload \n", pid); 320 | int i; 321 | for(i = 0; i < nsqd_num; i++){ 322 | if(arg_arr[i].pid == pid){ 323 | struct NSQArg arg = arg_arr[i].arg; 324 | start_worker_process(arg, i); 325 | } 326 | } 327 | }; 328 | */ 329 | break; 330 | case SIGALRM: 331 | break; 332 | default: 333 | break; 334 | } 335 | } 336 | 337 | 338 | PHP_METHOD (Nsq, subscribe) 339 | { 340 | zend_fcall_info fci; 341 | zend_fcall_info_cache fcc; 342 | zval *config; 343 | zval *class_lookupd; 344 | zval *lookupd_addr, rv3, lookupd_re; 345 | 346 | ZEND_PARSE_PARAMETERS_START(3, 3) 347 | Z_PARAM_OBJECT(class_lookupd) 348 | Z_PARAM_ARRAY(config) 349 | Z_PARAM_FUNC(fci, fcc) 350 | ZEND_PARSE_PARAMETERS_END(); 351 | 352 | 353 | lookupd_addr = zend_read_property(Z_OBJCE_P(class_lookupd), NSQ_COMPAT_OBJ_P(class_lookupd), "address", sizeof("address") - 1, 1, 354 | &rv3); 355 | 356 | zval *topic = zend_hash_str_find(Z_ARRVAL_P(config), "topic", sizeof("topic") - 1); 357 | if (!topic) { 358 | throw_exception(PHP_NSQ_ERROR_TOPIC_KEY_REQUIRED); 359 | return; 360 | } 361 | 362 | zval *channel = zend_hash_str_find(Z_ARRVAL_P(config), "channel", sizeof("channel") - 1); 363 | if (!channel) { 364 | throw_exception(PHP_NSQ_ERROR_CHANNEL_KEY_REQUIRED); 365 | } 366 | 367 | zval *rdy = zend_hash_str_find(Z_ARRVAL_P(config), "rdy", sizeof("rdy") - 1); 368 | zval *delay_time = zend_hash_str_find(Z_ARRVAL_P(config), "retry_delay_time", sizeof("retry_delay_time") - 1); 369 | zval *connect_num = zend_hash_str_find(Z_ARRVAL_P(config), "connect_num", sizeof("connect_num") - 1); 370 | if (!connect_num) { 371 | connect_num = emalloc(sizeof(zval)); 372 | ZVAL_LONG(connect_num, 1); 373 | } 374 | 375 | zval *auto_finish = zend_hash_str_find(Z_ARRVAL_P(config), "auto_finish", sizeof("auto_finish") - 1); 376 | if (auto_finish && Z_TYPE_P(auto_finish) == IS_FALSE) { 377 | 378 | ZVAL_FALSE(auto_finish); 379 | 380 | } else if (auto_finish && Z_TYPE_P(auto_finish) == IS_TRUE) { 381 | 382 | ZVAL_TRUE(auto_finish); 383 | 384 | } else { 385 | 386 | auto_finish = emalloc(sizeof(zval)); 387 | ZVAL_TRUE(auto_finish); 388 | } 389 | 390 | char *lookupd_re_str; 391 | zval * producers; 392 | zval *message; 393 | int producers_count; 394 | 395 | lookup: 396 | lookupd_re_str = lookup(Z_STRVAL_P(lookupd_addr), Z_STRVAL_P(topic)); 397 | 398 | if (*lookupd_re_str == '\0') { 399 | throw_exception(PHP_NSQ_ERROR_LOOKUPD_SERVER_NOT_AVAILABLE); 400 | return; 401 | }; 402 | 403 | php_json_decode(&lookupd_re, lookupd_re_str, strlen(lookupd_re_str), 1, PHP_JSON_PARSER_DEFAULT_DEPTH); 404 | producers = zend_hash_str_find(Z_ARRVAL(lookupd_re), "producers", sizeof("producers") - 1); 405 | if (!producers) { 406 | message = zend_hash_str_find(Z_ARRVAL(lookupd_re), "message", sizeof("message") - 1); 407 | throw_exception(PHP_NSQ_ERROR_TOPIC_NOT_EXISTS); 408 | // php_printf("%s\n", Z_STRVAL_P(message)); 409 | return; 410 | 411 | } 412 | 413 | producers_count = zend_array_count(Z_ARRVAL_P(producers)); 414 | nsqd_num = producers_count; 415 | 416 | if(producers_count < 1){ 417 | //TODO: this maybe exception? 418 | php_printf("The topic '%s' has not produced on any nsqd in the cluster but are present in the lookup data. The program will be retried after 10 seconds \n",Z_STRVAL_P(topic)); 419 | sleep(10); 420 | goto lookup; 421 | 422 | } 423 | 424 | arg_arr = (struct ArgPidArr*) emalloc(sizeof(ArgPidArr) * producers_count); 425 | memset(arg_arr, 0, producers_count * sizeof(ArgPidArr)); 426 | 427 | // foreach producers to get nsqd address 428 | zval *val; 429 | pid_t pid; 430 | int i, j; 431 | j = 0; 432 | 433 | 434 | for (i = 0; i < Z_LVAL_P(connect_num); i++) { 435 | 436 | ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(producers), val) { 437 | 438 | zval *nsqd_host = zend_hash_str_find(Z_ARRVAL_P(val), "broadcast_address", sizeof("broadcast_address") - 1); 439 | zval *nsqd_port = zend_hash_str_find(Z_ARRVAL_P(val), "tcp_port", sizeof("tcp_port") - 1); 440 | struct NSQMsg *msg; 441 | msg = (struct NSQMsg*) emalloc(sizeof(NSQMsg)); 442 | memset(msg, 0, sizeof(NSQMsg)); 443 | msg->topic = Z_STRVAL_P(topic); 444 | msg->channel = Z_STRVAL_P(channel); 445 | 446 | if (rdy) { 447 | msg->rdy = Z_LVAL_P(rdy); 448 | } else { 449 | msg->rdy = 1; 450 | } 451 | 452 | if (delay_time) { 453 | msg->delay_time = Z_LVAL_P(delay_time); 454 | } else { 455 | msg->delay_time = 0; 456 | } 457 | 458 | if (auto_finish && Z_TYPE_P(auto_finish) == IS_TRUE) { 459 | msg->auto_finish = 1; 460 | } else { 461 | msg->auto_finish = 0; 462 | } 463 | 464 | convert_to_string(nsqd_port); 465 | 466 | NSQArg arg; 467 | arg.msg = msg; 468 | 469 | arg.host = Z_STRVAL_P(nsqd_host); 470 | arg.port = Z_STRVAL_P(nsqd_port); 471 | arg.fci = &fci; 472 | arg.fcc = &fcc; 473 | arg.nsq_obj = getThis(); 474 | 475 | start_worker_process(arg, j); 476 | 477 | j++; 478 | 479 | }ZEND_HASH_FOREACH_END(); 480 | 481 | } 482 | int ret_pid = 0; 483 | while (1) { 484 | ret_pid = wait(NULL); 485 | if (ret_pid == -1) { 486 | if (errno == EINTR) { 487 | continue; 488 | } 489 | break; 490 | } 491 | printf("child process pids %d be terminated, trying reload \n", ret_pid); 492 | int i ,j ; 493 | int k = 0; 494 | printf("last all pid:\n"); 495 | for(i = 0; i < Z_LVAL_P(connect_num); i++){ 496 | for(j = 0; j < nsqd_num; j++){ 497 | printf(" %d\n", arg_arr[k].pid); 498 | if(arg_arr[k].pid == ret_pid){ 499 | struct NSQArg arg = arg_arr[k].arg; 500 | start_worker_process(arg, k); 501 | } 502 | k++; 503 | } 504 | } 505 | 506 | 507 | } 508 | 509 | zval_dtor(auto_finish); 510 | zval_dtor(connect_num); 511 | zval_dtor(config); 512 | zval_dtor(&lookupd_re); 513 | } 514 | 515 | 516 | static void _php_bufferevent_dtor(zend_resource *rsrc) /* {{{ */ { 517 | struct bufferevent *bevent = (struct bufferevent *) rsrc; 518 | efree(bevent); 519 | } 520 | 521 | /* }}} */ 522 | /* The previous line is meant for vim and emacs, so it can correctly fold and 523 | unfold functions in source code. See the corresponding marks just before 524 | function definition, where the functions purpose is also documented. Please 525 | follow this convention for the convenience of others editing your code. 526 | */ 527 | 528 | 529 | /* {{{ php_nsq_init_globals 530 | */ 531 | /* Uncomment this function if you have INI entries 532 | static void php_nsq_init_globals(zend_nsq_globals *nsq_globals) 533 | { 534 | nsq_globals->global_value = 0; 535 | nsq_globals->global_string = NULL; 536 | } 537 | */ 538 | /* }}} */ 539 | 540 | /* {{{ nsq_functions[] 541 | * 542 | * Every user visible function must have an entry in nsq_functions[]. 543 | */ 544 | 545 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_ctor, 0, 0, -1) 546 | ZEND_ARG_INFO(0, nsq_config) 547 | ZEND_END_ARG_INFO() 548 | 549 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_connect_nsqd, 0, 0, -1) 550 | ZEND_ARG_INFO(0, connect_addr_arr) 551 | ZEND_END_ARG_INFO() 552 | 553 | ZEND_BEGIN_ARG_INFO_EX(arginfo_close_nsqd_connection, 0, 0, -1) 554 | ZEND_END_ARG_INFO() 555 | 556 | 557 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_subscribe, 0, 0, -1) 558 | ZEND_ARG_INFO(0, conifg) 559 | ZEND_ARG_INFO(0, callback) 560 | ZEND_END_ARG_INFO() 561 | 562 | 563 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_publish, 0, 0, -1) 564 | ZEND_ARG_INFO(0, topic) 565 | ZEND_ARG_INFO(0, msg) 566 | ZEND_END_ARG_INFO() 567 | 568 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_d_publish, 0, 0, -1) 569 | ZEND_ARG_INFO(0, topic) 570 | ZEND_ARG_INFO(0, msg) 571 | ZEND_ARG_INFO(0, delay_time) 572 | ZEND_END_ARG_INFO() 573 | 574 | ZEND_BEGIN_ARG_INFO_EX(arginfo_nsq_requeue, 0, 0, -1) 575 | ZEND_ARG_INFO(0, delay_time) 576 | ZEND_END_ARG_INFO() 577 | 578 | const zend_function_entry nsq_functions[] = { 579 | PHP_ME(Nsq, __construct, arginfo_nsq_ctor, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR) 580 | PHP_ME(Nsq, connectNsqd, arginfo_nsq_connect_nsqd, ZEND_ACC_PUBLIC) 581 | PHP_ME(Nsq, closeNsqdConnection, arginfo_close_nsqd_connection, ZEND_ACC_PUBLIC) 582 | PHP_ME(Nsq, publish, arginfo_nsq_publish, ZEND_ACC_PUBLIC) 583 | PHP_ME(Nsq, deferredPublish, arginfo_nsq_d_publish, ZEND_ACC_PUBLIC) 584 | PHP_ME(Nsq, subscribe, arginfo_nsq_subscribe, ZEND_ACC_PUBLIC) 585 | PHP_FE_END /* Must be the last line in nsq_functions[] */ 586 | }; 587 | /* }}} */ 588 | 589 | 590 | /* {{{ PHP_MINIT_FUNCTION 591 | */ 592 | PHP_MINIT_FUNCTION (nsq) 593 | { 594 | zend_class_entry nsq; 595 | INIT_CLASS_ENTRY(nsq, "Nsq", nsq_functions); 596 | 597 | nsq_ce = zend_register_internal_class(&nsq); 598 | zend_declare_property_null(nsq_ce,ZEND_STRL("nsqConfig"),ZEND_ACC_PUBLIC); 599 | zend_declare_property_null(nsq_ce, ZEND_STRL("nsqd_connection_fds"), ZEND_ACC_PUBLIC); 600 | zend_declare_property_null(nsq_ce, ZEND_STRL("conn_timeout"), ZEND_ACC_PUBLIC); 601 | le_bufferevent = zend_register_list_destructors_ex(_php_bufferevent_dtor, NULL, "buffer event", module_number); 602 | lookupd_init(); 603 | nsq_message_init(); 604 | nsq_exception_init(); 605 | 606 | return SUCCESS; 607 | } 608 | /* }}} */ 609 | 610 | /* {{{ PHP_MSHUTDOWN_FUNCTION 611 | */ 612 | PHP_MSHUTDOWN_FUNCTION (nsq) 613 | { 614 | /* uncomment this line if you have INI entries 615 | UNREGISTER_INI_ENTRIES(); 616 | */ 617 | return SUCCESS; 618 | } 619 | /* }}} */ 620 | 621 | /* Remove if there's nothing to do at request start */ 622 | /* {{{ PHP_RINIT_FUNCTION 623 | */ 624 | PHP_RINIT_FUNCTION (nsq) 625 | { 626 | #if defined(COMPILE_DL_NSQ) && defined(ZTS) 627 | ZEND_TSRMLS_CACHE_UPDATE(); 628 | #endif 629 | return SUCCESS; 630 | } 631 | /* }}} */ 632 | 633 | /* Remove if there's nothing to do at request end */ 634 | /* {{{ PHP_RSHUTDOWN_FUNCTION 635 | */ 636 | PHP_RSHUTDOWN_FUNCTION (nsq) 637 | { 638 | return SUCCESS; 639 | } 640 | /* }}} */ 641 | 642 | /* {{{ PHP_MINFO_FUNCTION 643 | */ 644 | PHP_MINFO_FUNCTION (nsq) 645 | { 646 | php_info_print_table_start(); 647 | php_info_print_table_header(2, "nsq support", "enabled"); 648 | php_info_print_table_row(2, "version", PHP_NSQ_VERSION); 649 | php_info_print_table_row(2, "author", "zhenyu.wu[email:wuzhenyu@kuangjue.com]"); 650 | php_info_print_table_end(); 651 | 652 | /* Remove comments if you have entries in php.ini 653 | DISPLAY_INI_ENTRIES(); 654 | */ 655 | } 656 | /* }}} */ 657 | 658 | static const zend_module_dep nsq_deps[] = { 659 | ZEND_MOD_REQUIRED("json") 660 | ZEND_MOD_END 661 | }; 662 | 663 | /* {{{ nsq_module_entry 664 | */ 665 | zend_module_entry nsq_module_entry = { 666 | STANDARD_MODULE_HEADER_EX, 667 | NULL, 668 | nsq_deps, 669 | "nsq", 670 | NULL, //nsq_functions, 671 | PHP_MINIT(nsq), 672 | PHP_MSHUTDOWN(nsq), 673 | PHP_RINIT(nsq), /* Replace with NULL if there's nothing to do at request start */ 674 | PHP_RSHUTDOWN(nsq), /* Replace with NULL if there's nothing to do at request end */ 675 | PHP_MINFO(nsq), 676 | PHP_NSQ_VERSION, 677 | STANDARD_MODULE_PROPERTIES 678 | }; 679 | /* }}} */ 680 | 681 | #ifdef COMPILE_DL_NSQ 682 | #ifdef ZTS 683 | ZEND_TSRMLS_CACHE_DEFINE() 684 | #endif 685 | ZEND_GET_MODULE(nsq) 686 | #endif 687 | 688 | /* 689 | * Local variables: 690 | * tab-width: 4 691 | * c-basic-offset: 4 692 | * End: 693 | * vim600: noet sw=4 ts=4 fdm=marker 694 | * vim<600: noet sw=4 ts=4 695 | */ 696 | --------------------------------------------------------------------------------