├── CREDITS ├── image └── p_1.jpg ├── config.w32 ├── tests └── 001.phpt ├── README.md ├── config.m4 ├── php_foolsock.h ├── example └── memcache_client.php └── foolsock.c /CREDITS: -------------------------------------------------------------------------------- 1 | foolsock -------------------------------------------------------------------------------- /image/p_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pangudashu/foolsock/HEAD/image/p_1.jpg -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("foolsock", "for foolsock support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("foolsock", "enable foolsock support", "no"); 9 | 10 | if (PHP_FOOLSOCK != "no") { 11 | EXTENSION("foolsock", "foolsock.c"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for foolsock presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | foolsock extension is available 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # foolsock 2 | foolsock是为了实现RPC通信中TCP长连接而开发的PHP扩展 3 | 4 | ### 简介 5 | PHP很多项目随着业务规模的增长(尤其是终端众多的情况下)逐渐向服务化演变,常见的一种架构模型是将相对独立或者比较耗时的业务抽象为单独的服务(如用户模块)使用c/c++、golang等更高效的语言处理,具体的业务层(如:网页端、移动端)来调用各个服务,这种架构大大降低了各业务之间的耦合度,同时最大限度的提高了模块的重用性。 6 | 7 | ![struct](https://github.com/pangudashu/foolsock/raw/master/image/p_1.jpg) 8 | 9 | 业务层与后端的服务之间的通信协议中,http并不是一种高效的rpc协议。事实上php中有众多的扩展可以为我们提供很好的范例,如:mysql、memcached等等都是最常见不过的"服务"了,我们完全可以采用它们的客户端处理方式。 10 | 11 | mysql、memcached这些扩展都是采用TCP与服务端进行通信,你肯定记得他们都有长连接的连接方式,有兴趣的同学可以去翻一下它们的源码。 12 | 13 | 如果像mysql、memcached那样将协议的处理也封装在php扩展中,那么意味着每增加一个服务我们都需要安装一个扩展,这样将很不利于维护,同时也会降低开发效率。 14 | 15 | foolsock对socket进行了一层简单的封装,将连接放在persistent_list哈希表中,每个fastcgi进程连接后不会被释放,下次请求时直接使用。目前最大的连接数等于fastcgi进程数,当然你也可以自行修改下实现连接池的效果。 16 | 17 | 使用foolsock可以将协议相关的逻辑也使用php实现,可以大大降低开发成本,foolsock/example/memcache_client.php提供了一个简单的memcache客户端的示例。 18 | 19 | ### 安装 20 | 从github下载源码后解压 21 | cd foolsock 22 | phpize 23 | ./configure 24 | make && make install 25 | 最后将extension=foolsock.so加入php.ini,重启php-fpm或者其他fastcgi 26 | 27 | ### 使用 28 | pconnect([ int $timeoutms ]); //超时时间,单位:毫秒 32 | if(false === $r){ 33 | exit(); 34 | } 35 | 36 | //write 37 | $sock->write(string $msg);//返回false时可以调用$sock->pclose()关闭再重连$sock->pconnect() 38 | 39 | //read 40 | $sock0->read(int $read_buf_size); //超时时间大于0时如果read无数据返回此操作将会阻塞直至超时 41 | 42 | ### 示例 43 | example/memcache_client.php提供了一个简单的memcache客户端的例子(使用memcache二进制协议),实现memcache两个基本操作:Add/Get,经过测试处理效率与使用memcache扩展相当 44 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension foolsock 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | PHP_ARG_WITH(foolsock, for foolsock support, 11 | Make sure that the comment is aligned: 12 | [ --with-foolsock Include foolsock support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | dnl PHP_ARG_ENABLE(foolsock, whether to enable foolsock support, 17 | dnl Make sure that the comment is aligned: 18 | dnl [ --enable-foolsock Enable foolsock support]) 19 | 20 | if test "$PHP_FOOLSOCK" != "no"; then 21 | dnl Write more examples of tests here... 22 | 23 | dnl # --with-foolsock -> check with-path 24 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 25 | dnl SEARCH_FOR="/include/foolsock.h" # you most likely want to change this 26 | dnl if test -r $PHP_FOOLSOCK/$SEARCH_FOR; then # path given as parameter 27 | dnl FOOLSOCK_DIR=$PHP_FOOLSOCK 28 | dnl else # search default path list 29 | dnl AC_MSG_CHECKING([for foolsock files in default path]) 30 | dnl for i in $SEARCH_PATH ; do 31 | dnl if test -r $i/$SEARCH_FOR; then 32 | dnl FOOLSOCK_DIR=$i 33 | dnl AC_MSG_RESULT(found in $i) 34 | dnl fi 35 | dnl done 36 | dnl fi 37 | dnl 38 | dnl if test -z "$FOOLSOCK_DIR"; then 39 | dnl AC_MSG_RESULT([not found]) 40 | dnl AC_MSG_ERROR([Please reinstall the foolsock distribution]) 41 | dnl fi 42 | 43 | dnl # --with-foolsock -> add include path 44 | dnl PHP_ADD_INCLUDE($FOOLSOCK_DIR/include) 45 | 46 | dnl # --with-foolsock -> check for lib and symbol presence 47 | dnl LIBNAME=foolsock # you may want to change this 48 | dnl LIBSYMBOL=foolsock # you most likely want to change this 49 | 50 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 51 | dnl [ 52 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $FOOLSOCK_DIR/lib, FOOLSOCK_SHARED_LIBADD) 53 | dnl AC_DEFINE(HAVE_FOOLSOCKLIB,1,[ ]) 54 | dnl ],[ 55 | dnl AC_MSG_ERROR([wrong foolsock lib version or lib not found]) 56 | dnl ],[ 57 | dnl -L$FOOLSOCK_DIR/lib -lm 58 | dnl ]) 59 | dnl 60 | dnl PHP_SUBST(FOOLSOCK_SHARED_LIBADD) 61 | 62 | PHP_NEW_EXTENSION(foolsock, foolsock.c, $ext_shared) 63 | fi 64 | -------------------------------------------------------------------------------- /php_foolsock.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 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 | /* $Id$ */ 20 | 21 | #ifndef PHP_FOOLSOCK_H 22 | #define PHP_FOOLSOCK_H 23 | 24 | extern zend_module_entry foolsock_module_entry; 25 | #define phpext_foolsock_ptr &foolsock_module_entry 26 | 27 | #define PHP_FOOLSOCK_VERSION "0.1.0" /* Replace with version number for your extension */ 28 | 29 | #ifdef PHP_WIN32 30 | # define PHP_FOOLSOCK_API __declspec(dllexport) 31 | #elif defined(__GNUC__) && __GNUC__ >= 4 32 | # define PHP_FOOLSOCK_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_FOOLSOCK_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | #include "TSRM.h" 39 | #endif 40 | 41 | #if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION >= 4) 42 | # define FOOLSOCK_LIST_INSERT(item, list) zend_list_insert(item, list TSRMLS_CC); 43 | #else 44 | # define FOOLSOCK_LIST_INSERT(item, list) zend_list_insert(item, list); 45 | #endif 46 | 47 | PHP_MINIT_FUNCTION(foolsock); 48 | PHP_MSHUTDOWN_FUNCTION(foolsock); 49 | PHP_RINIT_FUNCTION(foolsock); 50 | PHP_RSHUTDOWN_FUNCTION(foolsock); 51 | PHP_MINFO_FUNCTION(foolsock); 52 | PHP_METHOD(foolsock,__construct); 53 | PHP_METHOD(foolsock,pconnect); 54 | PHP_METHOD(foolsock,read); 55 | PHP_METHOD(foolsock,write); 56 | PHP_METHOD(foolsock,pclose); 57 | 58 | #ifdef ZTS 59 | #define FOOLSOCK_G(v) TSRMG(foolsock_globals_id, zend_foolsock_globals *, v) 60 | #else 61 | #define FOOLSOCK_G(v) (foolsock_globals.v) 62 | #endif 63 | 64 | #endif /* PHP_FOOLSOCK_H */ 65 | 66 | #define CLASS_PROPERTY_RESOURCE "_resource" 67 | 68 | typedef struct _foolsock_s{ 69 | php_stream* stream; 70 | char* host; 71 | int port; 72 | long timeoutms; 73 | int status; 74 | }foolsock_t; 75 | 76 | /* 77 | * Local variables: 78 | * tab-width: 4 79 | * c-basic-offset: 4 80 | * End: 81 | * vim600: noet sw=4 ts=4 fdm=marker 82 | * vim<600: noet sw=4 ts=4 83 | */ 84 | -------------------------------------------------------------------------------- /example/memcache_client.php: -------------------------------------------------------------------------------- 1 | Add("foolsock_memcache_test_key","This is a socket connect to memcached by foolsock~",3600); 6 | 7 | $start = time(); 8 | for($i = 0;$i < 100000;$i++){ 9 | $begin_time = microtime(true); 10 | $value = $memcache->Get("foolsock_memcache_test_key"); 11 | if(false == $value){ 12 | echo "error to get\n"; 13 | } 14 | echo $i,"->[",$value,"] [time : ",(microtime(true) - $begin_time)*1000," ms]\n"; 15 | } 16 | 17 | $total_time = time()-$start; 18 | echo "total time : ",$total_time," qps : ",100000/$total_time," time per request : ",($total_time/100000)*1000,"[ms]\n"; 19 | 20 | 21 | class FoolSockMemcache 22 | { 23 | private $sock = null; 24 | 25 | public function __construct($host = '127.0.0.1',$port = 11211) 26 | { 27 | $this->sock = new FoolSock($host,$port); 28 | $this->sock->pconnect(100); 29 | } 30 | 31 | public function Get($key) 32 | {/*{{{*/ 33 | $header = $this->setHeader(0x80,0x00,strlen($key),0x00,0x00,0x0000,strlen($key),0x00000000); 34 | 35 | /*request body*/ 36 | $body = pack("a*",$key); 37 | $data = $header.$body; 38 | 39 | $write_res = $this->send($data); 40 | if(false === $write_res){ 41 | return false; 42 | } 43 | 44 | //read response header 45 | $_response_header = $this->read(24); 46 | if(false === $_response_header){ 47 | return false; 48 | } 49 | $response_header = $this->parseHeader($_response_header); 50 | 51 | //go on read response body 52 | $body = $this->read($response_header['body_len']); 53 | if(false === $body){ 54 | return false; 55 | } 56 | if($response_header['status'] != 0){ 57 | return $body; 58 | }else{ 59 | return substr($body,4); 60 | } 61 | }/*}}}*/ 62 | 63 | public function Add($key,$value,$expire = 0) 64 | {/*{{{*/ 65 | /*request header*/ 66 | $header = $this->setHeader(0x80,0x01,strlen($key),0x08,0x00,0x0000,strlen($key)+strlen($value)+8,0x00000000); 67 | /*request body*/ 68 | $body['extra_flag'] = pack("N",0xdeadbeef); 69 | $body['extra_expirte'] = pack("N",$expire); 70 | $body['key'] = pack("a*",$key); 71 | $body['value'] = pack("a*",$value); 72 | $body = implode("",$body); 73 | 74 | $data = $header.$body; 75 | 76 | $write_res = $this->send($data); 77 | if(false === $write_res){ 78 | return false; 79 | } 80 | //read response header 81 | $_response_header = $this->read(24); 82 | if(false === $_response_header){ 83 | return false; 84 | } 85 | $response_header = $this->parseHeader($_response_header); 86 | 87 | if($response_header['status'] == 0){ 88 | return true; 89 | } 90 | //go on read response body 91 | $body = $this->read($response_header['body_len']); 92 | if(false === $body){ 93 | return false; 94 | } 95 | return $body; 96 | }/*}}}*/ 97 | 98 | public function setHeader($magic,$opcode,$key_len,$extra_len = 0,$data_type = 0,$status = 0,$body_len,$opaque) 99 | {/*{{{*/ 100 | $header['magic'] = pack("C",$magic); 101 | $header['opcode'] = pack("C",$opcode); 102 | $header['ken_len'] = pack("n",$key_len); 103 | $header['extra_len'] = pack("C",$extra_len); 104 | $header['date_type'] = pack("C",$data_type); 105 | $header['status'] = pack("n",$status); 106 | $header['body_len'] = pack("N",$body_len); 107 | $header['opaque'] = pack("N",$opaque); 108 | $header['cas'] = pack("C8",0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00); 109 | 110 | return implode("",$header); 111 | }/*}}}*/ 112 | 113 | public function parseHeader($response_header) 114 | {/*{{{*/ 115 | $res_header['magic'] = unpack("C",substr($response_header,0,1)); 116 | $res_header['opcode'] = unpack("C",substr($response_header,1,1)); 117 | $res_header['ken_len'] = unpack("n",substr($response_header,2,2)); 118 | $res_header['extra_len'] = unpack("C",substr($response_header,4,1)); 119 | $res_header['date_type'] = unpack("C",substr($response_header,5,1)); 120 | $res_header['status'] = unpack("n",substr($response_header,6,2)); 121 | $res_header['body_len'] = unpack("N",substr($response_header,8,4)); 122 | $res_header['opaque'] = unpack("N",substr($response_header,12,4)); 123 | 124 | foreach($res_header as &$v){ 125 | $v = $v[1]; 126 | } 127 | return $res_header; 128 | }/*}}}*/ 129 | 130 | public function send($msg) 131 | {/*{{{*/ 132 | $start = microtime(true)*1000; 133 | do{ 134 | $res = $this->sock->write($msg); 135 | if($res != false && strlen($msg) == $res){ 136 | break; 137 | } 138 | $now = microtime(true)*1000; 139 | if($now - $start > 2000){ 140 | return false; 141 | }else{ 142 | //reconnect 143 | $this->sock->pclose(); 144 | $this->sock->pconnect(100); 145 | } 146 | }while(1); 147 | return true; 148 | }/*}}}*/ 149 | 150 | public function read($bytes) 151 | {/*{{{*/ 152 | if($bytes <= 0){ 153 | return true; 154 | } 155 | $d = ''; 156 | $start = microtime(true)*1000; 157 | while(1){ 158 | $d .= $this->sock->read($bytes); 159 | if(strlen($d) == $bytes){ 160 | return $d; 161 | } 162 | if(false === $d){ 163 | return false; 164 | } 165 | $now = microtime(true)*1000; 166 | if($now - $start > 2000){ 167 | return false; 168 | } 169 | } 170 | }/*}}}*/ 171 | 172 | 173 | } 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /foolsock.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 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 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "php.h" 31 | #include "php_ini.h" 32 | #include "ext/standard/info.h" 33 | #include "php_network.h" 34 | #include "php_foolsock.h" 35 | 36 | #define PHP_FOOLSOCK_NAME "foolsock persistent connection" 37 | 38 | static int le_foolsock; 39 | 40 | zend_class_entry *foolsock_ce; 41 | 42 | /* {{{ foolsock_functions[] 43 | */ 44 | const zend_function_entry foolsock_functions[] = { 45 | PHP_FE_END /* Must be the last line in foolsock_functions[] */ 46 | }; 47 | /* }}} */ 48 | 49 | /*{{{ foolsock_methods[] 50 | */ 51 | const zend_function_entry foolsock_methods[] = { 52 | ZEND_ME(foolsock,__construct,NULL,ZEND_ACC_PUBLIC) 53 | ZEND_ME(foolsock,pconnect,NULL,ZEND_ACC_PUBLIC) 54 | ZEND_ME(foolsock,read,NULL,ZEND_ACC_PUBLIC) 55 | ZEND_ME(foolsock,write,NULL,ZEND_ACC_PUBLIC) 56 | ZEND_ME(foolsock,pclose,NULL,ZEND_ACC_PUBLIC) 57 | PHP_FE_END /* Must be the last line in foolconf_functions[] */ 58 | }; 59 | /*}}}*/ 60 | 61 | /* {{{ foolsock_module_entry 62 | */ 63 | zend_module_entry foolsock_module_entry = { 64 | #if ZEND_MODULE_API_NO >= 20010901 65 | STANDARD_MODULE_HEADER, 66 | #endif 67 | "foolsock", 68 | foolsock_functions, 69 | PHP_MINIT(foolsock), 70 | PHP_MSHUTDOWN(foolsock), 71 | PHP_RINIT(foolsock), /* Replace with NULL if there's nothing to do at request start */ 72 | PHP_RSHUTDOWN(foolsock), /* Replace with NULL if there's nothing to do at request end */ 73 | PHP_MINFO(foolsock), 74 | #if ZEND_MODULE_API_NO >= 20010901 75 | PHP_FOOLSOCK_VERSION, 76 | #endif 77 | STANDARD_MODULE_PROPERTIES 78 | }; 79 | /* }}} */ 80 | 81 | #ifdef COMPILE_DL_FOOLSOCK 82 | ZEND_GET_MODULE(foolsock) 83 | #endif 84 | 85 | /* {{{ PHP_INI 86 | */ 87 | /* Remove comments and fill if you need to have entries in php.ini 88 | PHP_INI_BEGIN() 89 | STD_PHP_INI_ENTRY("foolsock.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_foolsock_globals, foolsock_globals) 90 | STD_PHP_INI_ENTRY("foolsock.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_foolsock_globals, foolsock_globals) 91 | PHP_INI_END() 92 | */ 93 | /* }}} */ 94 | 95 | /*{{{ static foolsock_t* create_new_resource(char* host, int port TSRMLS_DC) 96 | */ 97 | static foolsock_t* create_new_resource(char* host, int port TSRMLS_DC) 98 | { 99 | int host_len = strlen(host); 100 | foolsock_t* resource = (foolsock_t*)pemalloc(sizeof(foolsock_t),1); 101 | if(resource == NULL){ 102 | return NULL; 103 | } 104 | memset(resource, 0, sizeof(*resource)); 105 | 106 | resource->host = pemalloc(host_len + 1, 1); 107 | memcpy(resource->host, host, host_len); 108 | resource->host[host_len] = '\0'; 109 | resource->port = port; 110 | 111 | return resource; 112 | } 113 | /*}}}*/ 114 | 115 | /*{{{ static void foolsock_free(foolsock_t* fs TSRMLS_DC) 116 | */ 117 | static void foolsock_free(foolsock_t* fs TSRMLS_DC) 118 | { 119 | if(fs->stream != NULL){ 120 | php_stream_pclose(fs->stream); 121 | } 122 | pefree(fs->host,1); 123 | pefree(fs,1); 124 | } 125 | /*}}}*/ 126 | 127 | /*{{{ static struct timeval convert_timeoutms_to_ts(long msecs) 128 | */ 129 | static struct timeval convert_timeoutms_to_ts(long msecs) 130 | { 131 | struct timeval tv; 132 | int secs = 0; 133 | 134 | secs = msecs / 1000; 135 | tv.tv_sec = secs; 136 | tv.tv_usec = ((msecs - (secs * 1000)) * 1000) % 1000000; 137 | return tv; 138 | } 139 | /*}}}*/ 140 | 141 | /*{{{ static int get_stream(foolsock_t* f_obj TSRMLS_DC) 142 | */ 143 | static int get_stream(foolsock_t* f_obj TSRMLS_DC) 144 | { 145 | char* hash_key; 146 | 147 | spprintf(&hash_key, 0, "foolsock:%s:%d", f_obj->host,f_obj->port); 148 | 149 | switch(php_stream_from_persistent_id(hash_key, &(f_obj->stream) TSRMLS_CC)) { 150 | case PHP_STREAM_PERSISTENT_SUCCESS: 151 | if (php_stream_eof(f_obj->stream)) { 152 | php_stream_pclose(f_obj->stream); 153 | f_obj->stream = NULL; 154 | break; 155 | } 156 | case PHP_STREAM_PERSISTENT_FAILURE: 157 | break; 158 | } 159 | 160 | struct timeval tv = convert_timeoutms_to_ts(f_obj->timeoutms); 161 | 162 | if(!f_obj->stream){ 163 | int socktype = SOCK_STREAM; 164 | f_obj->stream = php_stream_sock_open_host(f_obj->host, f_obj->port, socktype, &tv, hash_key); 165 | } 166 | efree(hash_key); 167 | 168 | if(!f_obj->stream){ 169 | return 0; 170 | } 171 | 172 | php_stream_auto_cleanup(f_obj->stream); 173 | php_stream_set_option(f_obj->stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &tv); 174 | php_stream_set_option(f_obj->stream, PHP_STREAM_OPTION_WRITE_BUFFER, PHP_STREAM_BUFFER_NONE, NULL); 175 | php_stream_set_chunk_size(f_obj->stream, 8192); 176 | 177 | return 1; 178 | } 179 | /*}}}*/ 180 | 181 | /*{{{ public function FoolSock::__construct(string $host, string $port) 182 | */ 183 | PHP_METHOD(foolsock,__construct) 184 | { 185 | long port; 186 | char* host; 187 | int host_len; 188 | 189 | if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"sl|l",&host,&host_len,&port) == FAILURE){ 190 | RETURN_FALSE; 191 | } 192 | 193 | char* hash_key; 194 | int hash_key_len; 195 | int resource_id; 196 | int re_conn = 0; 197 | zend_rsrc_list_entry *le; 198 | foolsock_t* f_obj = NULL; 199 | 200 | hash_key_len = spprintf(&hash_key, 0,"foolsock_connect:%s:%d", host,port); 201 | if(zend_hash_find(&EG(persistent_list), hash_key,hash_key_len + 1, (void **)&le) == FAILURE) 202 | { 203 | f_obj = create_new_resource(host,port TSRMLS_CC); 204 | if(NULL == f_obj){ 205 | RETURN_FALSE; 206 | } 207 | 208 | zend_rsrc_list_entry new_le; 209 | new_le.type = le_foolsock; 210 | new_le.ptr = f_obj; 211 | 212 | if(zend_hash_update(&EG(persistent_list),hash_key, hash_key_len + 1,(void*)&new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE){ 213 | foolsock_free(f_obj TSRMLS_CC); 214 | }else{ 215 | //add to EG(regular_list) 216 | resource_id = FOOLSOCK_LIST_INSERT(f_obj,le_foolsock); 217 | } 218 | re_conn = 1; 219 | }else if(le->type != le_foolsock || le->ptr == NULL){ 220 | zend_hash_del(&EG(persistent_list), hash_key, hash_key_len+1); 221 | 222 | f_obj = create_new_resource(host,port TSRMLS_CC); 223 | if(NULL == f_obj){ 224 | RETURN_FALSE; 225 | } 226 | 227 | zend_rsrc_list_entry new_le; 228 | new_le.type = le_foolsock; 229 | new_le.ptr = f_obj; 230 | 231 | if(zend_hash_update(&EG(persistent_list),hash_key, hash_key_len + 1,(void*)&new_le, sizeof(zend_rsrc_list_entry), NULL) == FAILURE){ 232 | foolsock_free(f_obj TSRMLS_CC); 233 | }else{ 234 | resource_id = FOOLSOCK_LIST_INSERT(f_obj,le_foolsock); 235 | } 236 | re_conn = 1; 237 | }else{ 238 | f_obj = (foolsock_t*)le->ptr; 239 | resource_id = FOOLSOCK_LIST_INSERT(f_obj,le_foolsock); 240 | } 241 | 242 | efree(hash_key); 243 | add_property_resource(getThis(),CLASS_PROPERTY_RESOURCE,resource_id); 244 | } 245 | /*}}}*/ 246 | 247 | /*{{{ public function FoolSock::pconnect([int $timeoutms]) 248 | */ 249 | PHP_METHOD(foolsock,pconnect) 250 | { 251 | zval* resource; 252 | int resource_type; 253 | foolsock_t* f_obj; 254 | long timeoutms = 0; 255 | 256 | if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"|l",&timeoutms) == FAILURE){ 257 | RETURN_FALSE; 258 | } 259 | 260 | resource = zend_read_property(foolsock_ce,getThis(),ZEND_STRL(CLASS_PROPERTY_RESOURCE),1 TSRMLS_CC); 261 | if(resource == NULL){ 262 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource"); 263 | RETURN_FALSE; 264 | } 265 | 266 | f_obj = (foolsock_t*)zend_list_find(Z_LVAL_P(resource),&resource_type TSRMLS_CC); 267 | 268 | if(f_obj == NULL){ 269 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource"); 270 | RETURN_FALSE; 271 | } 272 | 273 | if(resource_type != le_foolsock){ 274 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource Type"); 275 | RETURN_FALSE; 276 | } 277 | f_obj->timeoutms = timeoutms; 278 | 279 | int stream_r = get_stream(f_obj TSRMLS_CC); 280 | if(!stream_r){ 281 | RETURN_FALSE; 282 | } 283 | RETURN_TRUE; 284 | } 285 | /*}}}*/ 286 | 287 | /*{{{ public function FoolSock::write(string $msg) 288 | */ 289 | PHP_METHOD(foolsock,write) 290 | { 291 | zval* resource; 292 | foolsock_t* f_obj; 293 | char* msg; 294 | int msg_len,res,resource_type; 295 | 296 | if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"s",&msg,&msg_len) == FAILURE){ 297 | RETURN_FALSE; 298 | } 299 | 300 | resource = zend_read_property(foolsock_ce,getThis(),ZEND_STRL(CLASS_PROPERTY_RESOURCE),1 TSRMLS_CC); 301 | if(resource == NULL){ 302 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource"); 303 | RETURN_FALSE; 304 | } 305 | 306 | f_obj = (foolsock_t*)zend_list_find(Z_LVAL_P(resource),&resource_type TSRMLS_CC); 307 | 308 | if(f_obj == NULL){ 309 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource"); 310 | RETURN_FALSE; 311 | } 312 | 313 | if(resource_type != le_foolsock){ 314 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource Type"); 315 | RETURN_FALSE; 316 | } 317 | 318 | if(f_obj->stream == NULL){ 319 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Socket Not Connected"); 320 | RETURN_FALSE; 321 | } 322 | 323 | res = php_stream_write(f_obj->stream,msg,msg_len); 324 | if(res != msg_len){ 325 | RETURN_FALSE; 326 | } 327 | RETURN_LONG(res); 328 | } 329 | /*}}}*/ 330 | 331 | /*{{{ public function FoolSock::read(int $size) 332 | */ 333 | PHP_METHOD(foolsock,read) 334 | { 335 | long size; 336 | foolsock_t* f_obj; 337 | char* response_buf; 338 | zval* resource; 339 | int resource_type; 340 | 341 | if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"l",&size) == FAILURE){ 342 | RETURN_FALSE; 343 | } 344 | 345 | if(size <= 0){ 346 | RETURN_TRUE; 347 | } 348 | 349 | resource = zend_read_property(foolsock_ce,getThis(),ZEND_STRL(CLASS_PROPERTY_RESOURCE),1 TSRMLS_CC); 350 | if(resource == NULL){ 351 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource"); 352 | RETURN_FALSE; 353 | } 354 | 355 | f_obj = (foolsock_t*)zend_list_find(Z_LVAL_P(resource),&resource_type TSRMLS_CC); 356 | 357 | if(f_obj == NULL){ 358 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource"); 359 | RETURN_FALSE; 360 | } 361 | 362 | if(resource_type != le_foolsock){ 363 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Resource Type"); 364 | RETURN_FALSE; 365 | } 366 | 367 | if(f_obj->stream == NULL){ 368 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "Socket Not Connected"); 369 | RETURN_FALSE; 370 | } 371 | 372 | response_buf = emalloc(size + 1); 373 | 374 | int r = php_stream_read(f_obj->stream,response_buf,size); 375 | if(r <= 0){ 376 | if(errno == EAGAIN || errno == EINPROGRESS){ 377 | RETURN_TRUE; 378 | } 379 | RETURN_FALSE; 380 | } 381 | response_buf[r] = '\0'; 382 | RETURN_STRINGL(response_buf,r,0); 383 | } 384 | /*}}}*/ 385 | 386 | /*{{{ public function FoolSock::pclose() 387 | */ 388 | PHP_METHOD(foolsock,pclose) 389 | { 390 | zval* resource; 391 | int resource_type,hash_key_len; 392 | char* hash_key; 393 | foolsock_t* f_obj; 394 | 395 | resource = zend_read_property(foolsock_ce,getThis(),ZEND_STRL(CLASS_PROPERTY_RESOURCE),1 TSRMLS_CC); 396 | if(resource == NULL){ 397 | RETURN_TRUE; 398 | } 399 | 400 | f_obj = (foolsock_t*)zend_list_find(Z_LVAL_P(resource),&resource_type TSRMLS_CC); 401 | if(f_obj == NULL){ 402 | RETURN_TRUE; 403 | } 404 | 405 | if(resource_type != le_foolsock){ 406 | RETURN_TRUE; 407 | } 408 | 409 | if(f_obj->stream != NULL){ 410 | php_stream_pclose(f_obj->stream); 411 | f_obj->stream = NULL; 412 | } 413 | 414 | RETURN_TRUE; 415 | } 416 | /*}}}*/ 417 | 418 | /*{{{ static void foolsock_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) 419 | */ 420 | static void foolsock_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) 421 | { 422 | foolsock_t* f_obj = (foolsock_t*)rsrc->ptr; 423 | 424 | foolsock_free(f_obj TSRMLS_CC); 425 | } 426 | /*}}}*/ 427 | 428 | /*{{{ PHP_MINIT_FUNCTION 429 | */ 430 | PHP_MINIT_FUNCTION(foolsock) 431 | { 432 | zend_class_entry ce; 433 | INIT_CLASS_ENTRY(ce, "FoolSock",foolsock_methods); 434 | 435 | foolsock_ce = zend_register_internal_class(&ce TSRMLS_CC); 436 | 437 | le_foolsock = zend_register_list_destructors_ex(NULL,foolsock_dtor,PHP_FOOLSOCK_NAME,module_number); 438 | 439 | return SUCCESS; 440 | } 441 | /*}}}*/ 442 | 443 | /* {{{ PHP_MSHUTDOWN_FUNCTION 444 | */ 445 | PHP_MSHUTDOWN_FUNCTION(foolsock) 446 | { 447 | /* uncomment this line if you have INI entries 448 | UNREGISTER_INI_ENTRIES(); 449 | */ 450 | return SUCCESS; 451 | } 452 | /* }}} */ 453 | 454 | /* {{{ PHP_RINIT_FUNCTION 455 | */ 456 | PHP_RINIT_FUNCTION(foolsock) 457 | { 458 | return SUCCESS; 459 | } 460 | /* }}} */ 461 | 462 | /* {{{ PHP_RSHUTDOWN_FUNCTION 463 | */ 464 | PHP_RSHUTDOWN_FUNCTION(foolsock) 465 | { 466 | return SUCCESS; 467 | } 468 | /* }}} */ 469 | 470 | /* {{{ PHP_MINFO_FUNCTION 471 | */ 472 | PHP_MINFO_FUNCTION(foolsock) 473 | { 474 | php_info_print_table_start(); 475 | php_info_print_table_header(2, "foolsock support", "enabled"); 476 | php_info_print_table_end(); 477 | } 478 | /* }}} */ 479 | 480 | 481 | /* 482 | * Local variables: 483 | * tab-width: 4 484 | * c-basic-offset: 4 485 | * End: 486 | * vim600: noet sw=4 ts=4 fdm=marker 487 | * vim<600: noet sw=4 ts=4 488 | */ 489 | --------------------------------------------------------------------------------