Commits

Ruslan Osmanov committed 50d3250 Merge

Merge branch 'develop'

  • Participants
  • Parent commits f187ad6, 9205cec

Comments (0)

Files changed (8)

 #include <php_network.h>
 #include <php_streams.h>
 
+#ifdef AF_UNIX
+# include <sys/un.h>
+#endif
+
 #include <signal.h>
 
 #if PHP_VERSION_ID >= 50301 && (HAVE_SOCKETS || defined(COMPILE_DL_SOCKETS))
 #include <event2/util.h>
 
 #ifdef HAVE_EVENT_EXTRA_LIB
+# include <event2/listener.h>
 # include <event2/dns.h>
 # include <event2/http.h>
 # include <event2/rpc.h>
 
 #if HAVE_EVENT_EXTRA_LIB
 static int le_event_dns_base;
+static int le_event_listener;
 #endif
 
 static const zend_module_dep event_deps[] = {
 #define PHP_EVENT_FETCH_DNS_BASE(b, zb) \
 	ZEND_FETCH_RESOURCE((b), php_event_dns_base_t *, &(zb), -1, PHP_EVENT_DNS_BASE_RES_NAME, le_event_dns_base)
 
+#define PHP_EVENT_FETCH_LISTENER(b, zb) \
+	ZEND_FETCH_RESOURCE((b), php_event_listener_t *, &(zb), -1, PHP_EVENT_LISTENER_RES_NAME, le_event_listener)
+
 #define PHP_EVENT_TIMEVAL_SET(tv, t)                     \
         do {                                             \
             tv.tv_sec  = (long) t;                       \
 
 #define PHP_EVENT_TIMEVAL_TO_DOUBLE(tv) (tv.tv_sec + tv.tv_usec * 1e-6)
 
-#define PHP_EVENT_RET_SOCKETS_REQUIRED_NORET                                   \
+#define PHP_EVENT_SOCKETS_REQUIRED_NORET                                       \
     php_error_docref(NULL TSRMLS_CC, E_ERROR, "`sockets' extension required. " \
             "If you have `sockets' installed, rebuild `event' extension")
 
-#define PHP_EVENT_RET_SOCKETS_REQUIRED                                         \
-    PHP_EVENT_RET_SOCKETS_REQUIRED_NORET;                                      \
+#define PHP_EVENT_SOCKETS_REQUIRED_RET                                         \
+    PHP_EVENT_SOCKETS_REQUIRED_NORET;                                          \
     RETURN_FALSE
 
 
 }
 /* }}} */
 
+/* {{{ _php_event_sockaddr_parse
+ * Parse in_addr and fill out_arr with IP and port.
+ * out_arr must be a pre-allocated empty zend array */
+static int _php_event_sockaddr_parse(const struct sockaddr *in_addr, zval *out_zarr)
+{
+	char buf[256];
+	int  ret      = FAILURE;
+
+	PHP_EVENT_ASSERT(Z_TYPE_P(out_zarr) == IS_ARRAY);
+
+	switch (in_addr->sa_family) {
+		case AF_INET:
+			if (evutil_inet_ntop(in_addr->sa_family, &((struct sockaddr_in *) in_addr)->sin_addr,
+						(void *) &buf, sizeof(buf))) {
+				add_next_index_string(out_zarr, (char *)&buf, 1);
+				add_next_index_long(out_zarr,
+						ntohs(((struct sockaddr_in *) in_addr)->sin_port));
+
+				ret = SUCCESS;
+			}
+			break;
+#if HAVE_IPV6 && HAVE_INET_NTOP
+		case AF_INET6:
+			if (evutil_inet_ntop(in_addr->sa_family, &((struct sockaddr_in6 *) in_addr)->sin6_addr,
+						(void *) &buf, sizeof(buf))) {
+				add_next_index_string(out_zarr, (char *)&buf, 1);
+				add_next_index_long(out_zarr,
+						ntohs(((struct sockaddr_in6 *) in_addr)->sin6_port));
+
+				ret = SUCCESS;
+			}
+			break;
+#endif
+#ifdef AF_UNIX
+		case AF_UNIX:
+			{
+				struct sockaddr_un *ua = (struct sockaddr_un *) in_addr;
+
+				if (ua->sun_path[0] == '\0') {
+					/* abstract name */
+ 					zval *tmp;
+					int len = strlen(ua->sun_path + 1) + 1;
+
+    				MAKE_STD_ZVAL(tmp);
+    				ZVAL_STRINGL(tmp, ua->sun_path, len, 1);
+    				Z_STRVAL_P(tmp)[len] = '\0';
+
+    				zend_hash_next_index_insert(Z_ARRVAL_P(out_zarr), &tmp, sizeof(zval *), NULL);
+				} else {
+					add_next_index_string(out_zarr, ua->sun_path, 1);
+				}
+			}
+			break;
+#endif
+	}
+
+	return ret;
+}
+/* }}} */
+
 /* {{{ zval_to_fd
  * Get numeric file descriptor from PHP stream or Socket resource */
 static php_socket_t zval_to_fd(zval **ppfd TSRMLS_DC)
 }
 /* }}} */
 
+/* {{{ _php_event_listener_cb */
+static void _php_event_listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *ctx) {
+
+	php_event_listener_t  *l = (php_event_listener_t *) ctx;
+
+	PHP_EVENT_ASSERT(l);
+
+	zend_fcall_info       *pfci = l->fci;
+	zend_fcall_info_cache *pfcc = l->fcc;
+
+	PHP_EVENT_ASSERT(pfci && pfcc);
+
+	zval  **args[4];
+	zval   *arg_listener;
+	zval   *arg_fd;
+	zval   *arg_address;
+	zval   *arg_data     = l->data;
+	zval   *retval_ptr;
+
+	php_stream *stream;
+
+	TSRMLS_FETCH_FROM_CTX(l->thread_ctx);
+
+	/* Call user function having proto:
+	 * void cb (resource $listener, resource $fd, array $address, mixed $data);
+	 * $address = array ("IP-address", *server* port)
+	 * Note, address contains the server port(not the one user passed to ex.
+	 * evconnlistener_new_bind()!
+	 */
+
+	if (ZEND_FCI_INITIALIZED(*pfci)) {
+		MAKE_STD_ZVAL(arg_listener);
+		ZVAL_RESOURCE(arg_listener, l->rsrc_id);
+		zend_list_addref(l->rsrc_id);
+		args[0] = &arg_listener;
+
+		/* Convert the socket created by libevent to PHP stream
+	 	 * and save it's resource ID in l->stream_id */
+
+		if (l->stream_id > 0) {
+			MAKE_STD_ZVAL(arg_fd);
+			ZVAL_RESOURCE(arg_fd, l->stream_id);
+			zend_list_addref(l->stream_id);
+		} else {
+			stream = php_stream_fopen_from_fd(fd, "r", NULL);
+			if (stream) {
+				MAKE_STD_ZVAL(arg_fd);
+				php_stream_to_zval(stream, arg_fd);
+
+				l->stream_id = Z_LVAL_P(arg_fd);
+				zend_list_addref(l->stream_id);
+			} else {
+				l->stream_id = -1;
+				ALLOC_INIT_ZVAL(arg_fd);
+			}
+		}
+		args[1] = &arg_fd;
+
+		MAKE_STD_ZVAL(arg_address);
+		array_init(arg_address);
+		_php_event_sockaddr_parse(address, arg_address);
+		args[2] = &arg_address;
+
+		if (arg_data) {
+			Z_ADDREF_P(arg_data);
+		} else {
+			ALLOC_INIT_ZVAL(arg_data);
+		}
+		args[3] = &arg_data;
+
+ 		/* Prepare callback */
+        pfci->params         = args;
+        pfci->retval_ptr_ptr = &retval_ptr;
+        pfci->param_count    = 4;
+        pfci->no_separation  = 1;
+
+        if (zend_call_function(pfci, pfcc TSRMLS_CC) == SUCCESS && retval_ptr) {
+            zval_ptr_dtor(&retval_ptr);
+        } else {
+            php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                    "An error occurred while invoking the callback");
+        }
+
+        zval_ptr_dtor(&arg_listener);
+        zval_ptr_dtor(&arg_fd);
+        zval_ptr_dtor(&arg_address);
+        zval_ptr_dtor(&arg_data);
+	}
+}
+/* }}} */
+
+/* {{{ listener_error_cb */
+static void listener_error_cb(struct evconnlistener *listener, void *ctx) {
+	php_event_listener_t  *l = (php_event_listener_t *) ctx;
+
+	PHP_EVENT_ASSERT(l);
+
+	zend_fcall_info       *pfci = l->fci_err;
+	zend_fcall_info_cache *pfcc = l->fcc_err;
+
+	PHP_EVENT_ASSERT(pfci && pfcc);
+
+	zval  **args[2];
+	zval   *arg_listener;
+	zval   *arg_data     = l->data;
+	zval   *retval_ptr;
+
+	TSRMLS_FETCH_FROM_CTX(l->thread_ctx);
+
+	/* Call user function having proto:
+	 * void cb (resource $listener, mixed $data); */
+
+	if (ZEND_FCI_INITIALIZED(*pfci)) {
+		MAKE_STD_ZVAL(arg_listener);
+		ZVAL_RESOURCE(arg_listener, l->rsrc_id);
+		zend_list_addref(l->rsrc_id);
+		args[0] = &arg_listener;
+
+		if (arg_data) {
+			Z_ADDREF_P(arg_data);
+		} else {
+			ALLOC_INIT_ZVAL(arg_data);
+		}
+		args[1] = &arg_data;
+
+ 		/* Prepare callback */
+        pfci->params         = args;
+        pfci->retval_ptr_ptr = &retval_ptr;
+        pfci->param_count    = 2;
+        pfci->no_separation  = 1;
+
+        if (zend_call_function(pfci, pfcc TSRMLS_CC) == SUCCESS && retval_ptr) {
+            zval_ptr_dtor(&retval_ptr);
+        } else {
+            php_error_docref(NULL TSRMLS_CC, E_WARNING,
+                    "An error occurred while invoking the callback");
+        }
+
+        zval_ptr_dtor(&arg_listener);
+        zval_ptr_dtor(&arg_data);
+	}
+}
+/* }}} */
+
 /* {{{ php_event_dtor */
 static void php_event_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 {
 static void php_event_base_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
 {
 	/* TODO: what if events bound to the event_base are not destroyed? */
-	struct event_base *base = (struct event_base *) rsrc->ptr;
+	php_event_base_t *b= (php_event_base_t *) rsrc->ptr;
 
-	event_base_free(base);
+	if (b) {
+		event_base_free(b->base);
+		efree(b);
+	}
 }
 /* }}} */
 
 }
 /* }}} */
 
+/* {{{ php_event_listener_dtor */
+static void php_event_listener_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+	php_event_listener_t *l = (php_event_listener_t *) rsrc->ptr;
+
+	if (l) {
+		if (l->stream_id >= 0) {
+			zend_list_delete(l->stream_id);
+		}
+
+		if (l->base_id) {
+			zend_list_delete(l->base_id);
+		}
+
+		if (l->data) {
+			zval_ptr_dtor(&l->data);
+		}
+
+		PHP_EVENT_FREE_FCALL_INFO(l->fci, l->fcc);
+		PHP_EVENT_FREE_FCALL_INFO(l->fci_err, l->fcc_err);
+
+		evconnlistener_free(l->listener);
+
+		efree(l);
+	}
+}
+/* }}} */
+
 /* Private functions }}} */
 
 
 	le_event_bevent   = zend_register_list_destructors_ex(php_event_bevent_dtor,   NULL, PHP_EVENT_BEVENT_RES_NAME,   module_number);
 	le_event_buffer   = zend_register_list_destructors_ex(php_event_buffer_dtor,   NULL, PHP_EVENT_BUFFER_RES_NAME,   module_number);
 	le_event_dns_base = zend_register_list_destructors_ex(php_event_dns_base_dtor, NULL, PHP_EVENT_DNS_BASE_RES_NAME, module_number);
+	le_event_listener = zend_register_list_destructors_ex(php_event_listener_dtor, NULL, PHP_EVENT_LISTENER_RES_NAME, module_number);
 
 	/* Loop flags */
 	PHP_EVENT_REG_CONST_LONG(EVENT_LOOP_ONCE,     EVLOOP_ONCE);
 	PHP_EVENT_REG_CONST_LONG(EVENT_DNS_OPTION_HOSTSFILE,   DNS_OPTION_HOSTSFILE);
 	PHP_EVENT_REG_CONST_LONG(EVENT_DNS_OPTIONS_ALL,        DNS_OPTIONS_ALL);
 
+#if HAVE_EVENT_EXTRA_LIB
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_LEAVE_SOCKETS_BLOCKING, LEV_OPT_LEAVE_SOCKETS_BLOCKING);
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_CLOSE_ON_FREE,          LEV_OPT_CLOSE_ON_FREE);
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_CLOSE_ON_EXEC,          LEV_OPT_CLOSE_ON_EXEC);
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_REUSEABLE,              LEV_OPT_REUSEABLE);
+# if LIBEVENT_VERSION_NUMBER >= 0x02010100
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_DISABLED,               LEV_OPT_DISABLED);
+# endif
+# if LIBEVENT_VERSION_NUMBER >= 0x02000800
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_THREADSAFE,             LEV_OPT_THREADSAFE);
+#endif
+# if LIBEVENT_VERSION_NUMBER >= 0x02010100
+	PHP_EVENT_REG_CONST_LONG(EVENT_LEV_OPT_DEFERRED_ACCEPT,        LEV_OPT_DEFERRED_ACCEPT);
+# endif
+#endif
+
 	/* Handle libevent's error logging more gracefully than it's default
 	 * logging to stderr, or calling abort()/exit() */
 	event_set_fatal_callback(fatal_error_cb);
 PHP_FUNCTION(evtimer_new)
 {
 	zval                  *zbase;
-	php_event_base_t      *base;
+	php_event_base_t      *b;
 	zend_fcall_info        fci   = empty_fcall_info;
 	zend_fcall_info_cache  fcc   = empty_fcall_info_cache;
 	zval                  *arg   = NULL;
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	e = emalloc(sizeof(php_event_t));
 	memset(e, 0, sizeof(php_event_t));
 
-	event = evtimer_new(base, timer_cb, (void *) e);
+	event = evtimer_new(b->base, timer_cb, (void *) e);
 	if (!event) {
 		efree(e);
 		RETURN_FALSE;
 PHP_FUNCTION(evtimer_set)
 {
 	zval                  *zbase;
-	php_event_base_t      *base;
+	php_event_base_t      *b;
 	zval                  *zevent;
 	php_event_t           *e;
 	zend_fcall_info        fci    = empty_fcall_info;
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	if (ZEND_FCI_INITIALIZED(fci)) {
 		if (e->fci && ZEND_FCI_INITIALIZED(*e->fci)) {
 
 	e->stream_id = -1; /* stdin fd = 0 */
 
-    if (evtimer_assign(e->event, base, timer_cb, (void *) e)) {
+    if (evtimer_assign(e->event, b->base, timer_cb, (void *) e)) {
     	RETURN_FALSE;
     }
     RETVAL_TRUE;
 PHP_FUNCTION(evsignal_new)
 {
 	zval                  *zbase;
-	php_event_base_t      *base;
+	php_event_base_t      *b;
 	long                   signum;
 	zend_fcall_info        fci    = empty_fcall_info;
 	zend_fcall_info_cache  fcc    = empty_fcall_info_cache;
 		RETURN_FALSE;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	e = emalloc(sizeof(php_event_t));
 	memset(e, 0, sizeof(php_event_t));
 
-	event = evsignal_new(base, signum, signal_cb, (void *) e);
+	event = evsignal_new(b->base, signum, signal_cb, (void *) e);
 	if (!event) {
 		efree(e);
 		RETURN_FALSE;
 PHP_FUNCTION(event_new)
 {
 	zval                   *zbase;
-	php_event_base_t       *base;
+	php_event_base_t       *b;
 	zval                  **ppzfd;
 	evutil_socket_t         fd;
 	long                    what;
 		}
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	/* TODO: check if a signum bound to different event bases */
 
 	e = emalloc(sizeof(php_event_t));
 	memset(e, 0, sizeof(php_event_t));
 
-	event = event_new(base, fd, what, event_cb, (void *) e);
+	event = event_new(b->base, fd, what, event_cb, (void *) e);
 	if (!event) {
 		efree(e);
 		php_error_docref(NULL TSRMLS_CC, E_ERROR, "event_new failed");
 PHP_FUNCTION(event_set)
 {
 	zval                   *zbase;
-	php_event_base_t       *base;
+	php_event_base_t       *b;
 	zval                   *zevent;
 	php_event_t            *e;
 	zval                  **ppzfd   = NULL;
 		RETURN_FALSE;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	/* TODO: check if a signum bound to different event bases */
 
 		Z_ADDREF_P(arg);
 	}
 
-	event_get_assignment(e->event, &base,
+	event_get_assignment(e->event, &b->base,
 			(ppzfd ? NULL : &fd),
 			(short *) (what == -1 ? &what : NULL),
 			NULL /* ignore old callback */ ,
 			NULL /* ignore old callback argument */);
 
-	if (event_assign(e->event, base, fd, what, event_cb, (void *) e)) {
+	if (event_assign(e->event, b->base, fd, what, event_cb, (void *) e)) {
 		RETURN_FALSE;
 	}
 
 PHP_FUNCTION(event_reinit)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_reinit(base)) {
+	if (event_reinit(b->base)) {
 		RETURN_TRUE;
 	}
 	RETVAL_FALSE;
  * Returns resource representing new event base */
 PHP_FUNCTION(event_base_new)
 {
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters_none() == FAILURE) {
 		return;
 	}
 
-	base = event_base_new();
+	b = emalloc(sizeof(php_event_base_t));
+	memset(b, 0, sizeof(php_event_base_t));
+
+	b->base = event_base_new();
 
-	if (base) {
-		ZEND_REGISTER_RESOURCE(return_value, base, le_event_base);
+	if (b->base) {
+		b->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, b, le_event_base);
 	} else {
+		efree(b);
 		RETVAL_FALSE;
 	}
 }
  * Creates new event base taking the specified configuration under consideration. */
 PHP_FUNCTION(event_base_new_with_config)
 {
-	php_event_base_t   *base;
+	php_event_base_t   *b;
 	php_event_config_t *cfg;
 	zval               *zcfg;
 
 
 	PHP_EVENT_FETCH_CONFIG(cfg, zcfg);
 
-	base = event_base_new_with_config(cfg);
+	b = emalloc(sizeof(php_event_base_t));
+	memset(b, 0, sizeof(php_event_base_t));
+
+	b->base = event_base_new_with_config(cfg);
 
-	if (base) {
-		ZEND_REGISTER_RESOURCE(return_value, base, le_event_base);
+	if (b->base) {
+		b->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, b, le_event_base);
 	} else {
+		efree(b);
 		RETVAL_FALSE;
 	}
 }
 PHP_FUNCTION(event_base_get_method)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	RETVAL_STRING(event_base_get_method(base), 1);
+	RETVAL_STRING(event_base_get_method(b->base), 1);
 }
 /* }}} */
 
 PHP_FUNCTION(event_base_get_features)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase)
+	PHP_EVENT_FETCH_BASE(b, zbase)
 
-	RETVAL_LONG(event_base_get_features(base));
+	RETVAL_LONG(event_base_get_features(b->base));
 }
 /* }}} */
 
 {
 	zval             *zbase;
 	long              n_priorities;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl",
 				&zbase, &n_priorities) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_priority_init(base, n_priorities)) {
+	if (event_base_priority_init(b->base, n_priorities)) {
 		RETURN_FALSE;
 	}
 	RETVAL_TRUE;
 {
 	zval             *zbase;
 	long              flags = -1;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l",
 				&zbase, &flags) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	/* Call event_base_dispatch when flags omitted. */
 	if (flags == -1) {
-		if (event_base_dispatch(base) == -1) {
+		if (event_base_dispatch(b->base) == -1) {
 			RETURN_FALSE;
 		}
-	} else if (event_base_loop(base, flags) == -1) {
+	} else if (event_base_loop(b->base, flags) == -1) {
 		RETURN_FALSE;
 	}
 	RETVAL_TRUE;
 PHP_FUNCTION(event_base_dispatch)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_dispatch(base) == -1) {
+	if (event_base_dispatch(b->base) == -1) {
 		RETURN_FALSE;
 	}
 	RETVAL_TRUE;
 PHP_FUNCTION(event_base_loopexit)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 	double            timeout = -1;
 	int               res;
 
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
 	if (timeout == -1) {
-		res = event_base_loopexit(base, NULL);
+		res = event_base_loopexit(b->base, NULL);
 	} else {
 		struct timeval tv;
 		PHP_EVENT_TIMEVAL_SET(tv, timeout);
 
-		res = event_base_loopexit(base, &tv);
+		res = event_base_loopexit(b->base, &tv);
 	}
 
 	if (res) {
 PHP_FUNCTION(event_base_loopbreak)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_loopbreak(base)) {
+	if (event_base_loopbreak(b->base)) {
 		RETURN_FALSE;
 	}
 	RETVAL_TRUE;
 PHP_FUNCTION(event_base_set)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 	zval             *zevent;
 	php_event_t      *e;
 
 		RETURN_FALSE;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_set(base, e->event)) {
+	if (event_base_set(b->base, e->event)) {
 		RETURN_FALSE;
 	}
 	RETVAL_TRUE;
 PHP_FUNCTION(event_base_got_break)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_got_break(base)) {
+	if (event_base_got_break(b->base)) {
 		RETURN_TRUE;
 	}
 	RETVAL_FALSE;
 PHP_FUNCTION(event_base_got_exit)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_got_exit(base)) {
+	if (event_base_got_exit(b->base)) {
 		RETURN_TRUE;
 	}
 	RETVAL_FALSE;
 PHP_FUNCTION(event_base_gettimeofday_cached)
 {
 	zval                  *zbase;
-	php_event_base_t      *base;
+	php_event_base_t      *b;
 	struct timeval         tv;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_gettimeofday_cached(base, &tv)) {
+	if (event_base_gettimeofday_cached(b->base, &tv)) {
 		RETURN_NULL();
 	}
 	
 PHP_FUNCTION(event_base_update_cache_time)
 {
 	zval             *zbase;
-	php_event_base_t *base;
+	php_event_base_t *b;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
 				&zbase) == FAILURE) {
 		return;
 	}
 
-	PHP_EVENT_FETCH_BASE(base, zbase);
+	PHP_EVENT_FETCH_BASE(b, zbase);
 
-	if (event_base_update_cache_time(base)) {
+	if (event_base_update_cache_time(b->base)) {
 		RETURN_FALSE;
 	}
 	RETVAL_TRUE;
  * Passing NULL to socket parameter means that the socket stream should be created later,
  * e.g. by means of bufferevent_socket_connect().
  *
+ * socket parameter may be created as a stream(not necessarily by means of sockets extension)
+ *
  * Returns bufferevent resource optionally associated with socket resource. */
 PHP_FUNCTION(bufferevent_socket_new)
 {
 	zval               **ppzfd   = NULL;
 	evutil_socket_t      fd;
 	long                 options = 0;
-	php_event_bevent_t  *b;
+	php_event_bevent_t  *bev;
 	struct bufferevent  *bevent;
 
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|Z!l",
 		if (ppzfd) {
 			/* sockets_zval_to_fd reports error
 	 	 	 * in case if it is not a valid socket resource */
-			fd = (evutil_socket_t) sockets_zval_to_fd(ppzfd TSRMLS_CC);
+			/*fd = (evutil_socket_t) sockets_zval_to_fd(ppzfd TSRMLS_CC);*/
+			fd = zval_to_fd(ppzfd TSRMLS_CC);
+
 		}
 
 		if (fd < 0) {
 		}
 		/* Make sure that the socket is in non-blocking mode(libevent's tip) */
 		evutil_make_socket_nonblocking(fd);
+#else
+		fd = -1;
 #endif
 	} else {
  		/* User decided to assign fd later,
 
 	PHP_EVENT_FETCH_BASE(base, zbase);
 
-	b = emalloc(sizeof(php_event_bevent_t));
-	memset(b, 0, sizeof(php_event_bevent_t));
+	bev = emalloc(sizeof(php_event_bevent_t));
+	memset(bev, 0, sizeof(php_event_bevent_t));
 
-	bevent = bufferevent_socket_new(base, fd, options);
+	bevent = bufferevent_socket_new(base->base, fd, options);
 	if (bevent == NULL) {
-		efree(b);
+		efree(bev);
 		php_error_docref(NULL TSRMLS_CC, E_ERROR,
 				"Failed to allocate bufferevent for socket");
 		RETURN_FALSE;
 	}
 
-	b->bevent = bevent;
+	bev->bevent = bevent;
 
 	if (ppzfd) {
 		/* lval of ppzfd is the resource ID */
-		b->stream_id = Z_LVAL_PP(ppzfd);
+		bev->stream_id = Z_LVAL_PP(ppzfd);
 		zend_list_addref(Z_LVAL_PP(ppzfd));
 	} else {
 		/* Should be assigned in bufferevent_socket_connect() later
 		 * (by means of bufferevent_getfd()) */
-		b->stream_id = -1;
+		bev->stream_id = -1;
 	}
 
-	b->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, b, le_event_bevent);
+	/* Make sure base destroyed after the bufferevent */
+	zend_list_addref(base->rsrc_id);
+
+	bev->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, bev, le_event_bevent);
+	/* XXX Normally I wouldn't do it. But if one creates the resource inside a callback,
+	 * particularly called by libevent->event, the resource is destroyed on return automatically.
+	 * See examples/listener.php
+	 * Maybe add some userspace function like bufferevent_ref() */
+	zend_list_addref(bev->rsrc_id);
 }
 /* }}} */
 
 
 	PHP_EVENT_FETCH_BASE(base, zbase);
 
-	if (bufferevent_pair_new(base, options, bevent_pair)) {
+	if (bufferevent_pair_new(base->base, options, bevent_pair)) {
 		RETURN_FALSE;
 	}
 
 
 	if (ZEND_FCI_INITIALIZED(fci_read)) {
 		read_cb = bevent_read_cb;
+		PHP_EVENT_FREE_FCALL_INFO(bev->fci_read, bev->fcc_read);
 		PHP_EVENT_COPY_FCALL_INFO(bev->fci_read, bev->fcc_read, &fci_read, &fcc_read);
 	} else {
 		read_cb = NULL;
 
 	if (ZEND_FCI_INITIALIZED(fci_write)) {
 		write_cb = bevent_write_cb;
+		PHP_EVENT_FREE_FCALL_INFO(bev->fci_write, bev->fcc_write);
 		PHP_EVENT_COPY_FCALL_INFO(bev->fci_write, bev->fcc_write, &fci_write, &fcc_write);
 	} else {
 		write_cb = NULL;
 
 	if (ZEND_FCI_INITIALIZED(fci_event)) {
 		event_cb = bevent_event_cb;
+		PHP_EVENT_FREE_FCALL_INFO(bev->fci_event, bev->fcc_event);
 		PHP_EVENT_COPY_FCALL_INFO(bev->fci_event, bev->fcc_event, &fci_event, &fcc_event);
 	} else {
 		event_cb = NULL;
 }
 /* }}} */
 
+
+
 /* {{{ proto resource evbuffer_new(void);
  * Allocates storage for new event buffer and returns it's resource. */
 PHP_FUNCTION(evbuffer_new)
 }
 /* }}} */
 
+/* {{{ proto bool evbuffer_unfreeze(resource buf, bool at_front);
+ * Re-enable calls that modify an evbuffer. */
+PHP_FUNCTION(evbuffer_unfreeze)
+{
+	php_event_buffer_t *b;
+	zval               *zbuf;
+	zend_bool           at_front;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb",
+				&zbuf, &at_front) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BUFFER(b, zbuf);
+
+	if (evbuffer_unfreeze(b->buf, at_front)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
 /* {{{ proto long evbuffer_get_length(resource buf);
  * Returns the total number of bytes stored in the event buffer. */
 PHP_FUNCTION(evbuffer_get_length)
 }
 /* }}} */
 
-/* {{{ proto long evbuffer_remove(resource buf, string &data, long max_bytes);
+/* {{{ proto bool evbuffer_add_buffer(resource outbuf, inbuf); 
+ * Move all data from one evbuffer into another evbuffer.
+ * This is a destructive add. The data from one buffer moves into the other buffer. However, no unnecessary memory copies occur.
+ */
+PHP_FUNCTION(evbuffer_add_buffer)
+{
+	php_event_buffer_t *b_out     , *b_in;
+	zval               *zbuf_out  , *zbuf_in;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr",
+				&zbuf_out, &zbuf_in) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BUFFER(b_out, zbuf_out);
+	PHP_EVENT_FETCH_BUFFER(b_in, zbuf_in);
+
+	if (evbuffer_add_buffer(b_out->buf, b_in->buf)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int evbuffer_remove(resource buf, string &data, long max_bytes);
  * Read data from an evbuffer and drain the bytes read.  If more bytes are
  * requested than are available in the evbuffer, we only extract as many bytes
  * as were available.
 /* }}} */
 
 
+/* {{{ proto int event_socket_get_last_errno([resource socket = null]);
+ * Returns the most recent socket error number(errno).
+ *
+ * Note: if sockets extension was not available at the time when event
+ * extension built, the socket parameter is ignored. In this case the function
+ * returns the morst recent socket error regardless the argument provided. */
+PHP_FUNCTION(event_socket_get_last_errno)
+{
+
+	zval **ppzfd = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|Z!",
+				&ppzfd) == FAILURE) {
+		return;
+	}
+
+#ifdef PHP_EVENT_SOCKETS_SUPPORT 
+	if (ppzfd) {
+		evutil_socket_t fd = (evutil_socket_t) sockets_zval_to_fd(ppzfd TSRMLS_CC);
+
+		if (fd < 0) {
+			RETURN_FALSE;
+		}
+
+		RETVAL_LONG(evutil_socket_geterror(fd));
+	} else {
+		RETVAL_LONG(EVUTIL_SOCKET_ERROR());
+	}
+#else
+	RETVAL_LONG(EVUTIL_SOCKET_ERROR());
+#endif
+}
+/* }}} */
+
+/* {{{ proto string event_socket_get_last_error([resource socket = null]);
+ * Returns the most recent socket error */
+PHP_FUNCTION(event_socket_get_last_error)
+{
+	zval **ppzfd = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|Z!",
+				&ppzfd) == FAILURE) {
+		return;
+	}
+
+#ifdef PHP_EVENT_SOCKETS_SUPPORT 
+	if (ppzfd) {
+		evutil_socket_t fd = (evutil_socket_t) sockets_zval_to_fd(ppzfd TSRMLS_CC);
+
+		if (fd < 0) {
+			RETURN_FALSE;
+		}
+
+		RETVAL_STRING(evutil_socket_error_to_string(evutil_socket_geterror(fd)), 1);
+	} else {
+		RETVAL_STRING(evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), 1);
+	}
+#else
+	RETVAL_STRING(evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()), 1);
+#endif
+}
+/* }}} */
+
+
 /* API functions END }}} */
 
 
 	dnsb = emalloc(sizeof(php_event_dns_base_t));
 	memset(dnsb, 0, sizeof(php_event_dns_base_t));
 
-	dnsb->dns_base = evdns_base_new(base, initialize);
+	dnsb->dns_base = evdns_base_new(base->base, initialize);
 
 	if (dnsb->dns_base) {
 		dnsb->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, dnsb, le_event_dns_base);
 }
 /* }}} */
 
-/* {{{ proto void evdns_base_free(void);
+/* {{{ proto void evdns_base_free(resource base);
  * Free an evdns base */
 PHP_FUNCTION(evdns_base_free)
 {
 }
 /* }}} */
 
+
+
+/* {{{ proto resource evconnlistener_new_bind(resource base, callable cb, mixed data, int flags, int backlog, string addr);
+ * Creates new connection listener associated with an event base.
+ *
+ * Returns resource representing the event connection listener.
+ */
+PHP_FUNCTION(evconnlistener_new_bind)
+{
+	zval                  *zbase;
+	php_event_base_t      *base;
+	zend_fcall_info        fci      = empty_fcall_info;
+	zend_fcall_info_cache  fcc      = empty_fcall_info_cache;
+	php_event_listener_t  *l;
+	zval                  *zdata    = NULL;
+	zval                  *zaddr;
+	long                   flags;
+	long                   backlog;
+	struct evconnlistener *listener;
+	struct sockaddr        sa;
+	socklen_t              sa_len   = sizeof(struct sockaddr);
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rfz!llz",
+				&zbase, &fci, &fcc, &zdata, &flags, &backlog, &zaddr) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	convert_to_string(zaddr);
+	if (php_network_parse_network_address_with_port(Z_STRVAL_P(zaddr), Z_STRLEN_P(zaddr),
+				&sa, &sa_len TSRMLS_CC) != SUCCESS) {
+		RETURN_FALSE;
+	}
+
+	l = emalloc(sizeof(php_event_listener_t));
+	memset(l, 0, sizeof(php_event_listener_t));
+
+	listener = evconnlistener_new_bind(base->base, _php_event_listener_cb,
+			(void *) l, flags, backlog, &sa, sa_len);
+	if (!listener) {
+		efree(l);
+		RETURN_FALSE;
+	}
+	l->listener = listener;
+
+	if (zdata) {
+		l->data = zdata;
+		Z_ADDREF_P(zdata);
+	}
+
+	PHP_EVENT_COPY_FCALL_INFO(l->fci, l->fcc, &fci, &fcc);
+
+	l->base_id = Z_LVAL_P(zbase);
+	zend_list_addref(l->base_id);
+
+	TSRMLS_SET_CTX(l->thread_ctx);
+
+	l->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, l, le_event_listener);
+
+	PHP_EVENT_ASSERT(l->rsrc_id);
+}
+/* }}} */
+
+/* {{{ proto resource evconnlistener_new(resource base, callable cb, mixed data, int flags, int backlog, resource stream);
+ * Creates new connection listener associated with an event base.
+ *
+ * stream parameter may be a socket resource, or a stream associated with a socket.
+ *
+ * Returns resource representing the event connection listener.
+ */
+PHP_FUNCTION(evconnlistener_new)
+{
+	zval                   *zbase;
+	php_event_base_t       *base;
+	zend_fcall_info         fci       = empty_fcall_info;
+	zend_fcall_info_cache   fcc       = empty_fcall_info_cache;
+	php_event_listener_t   *l;
+	zval                   *zdata     = NULL;
+	long                    flags;
+	long                    backlog;
+	zval                  **ppzfd;
+	evutil_socket_t         fd        = -1;
+	struct evconnlistener  *listener;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rfz!llZ",
+				&zbase, &fci, &fcc, &zdata, &flags, &backlog,
+				&ppzfd) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	/* sockets_zval_to_fd reports error
+	 * in case if it is not a valid socket resource */
+	fd = zval_to_fd(ppzfd TSRMLS_CC);
+	if (fd < 0) {
+		RETURN_FALSE;
+	}
+
+	/* Make sure that the socket is in non-blocking mode(libevent's tip) */
+	evutil_make_socket_nonblocking(fd);
+
+	l = emalloc(sizeof(php_event_listener_t));
+	memset(l, 0, sizeof(php_event_listener_t));
+
+	listener = evconnlistener_new(base->base, _php_event_listener_cb,
+			(void *) l, flags, backlog, fd);
+	if (!listener) {
+		efree(l);
+		RETURN_FALSE;
+	}
+	l->listener = listener;
+
+	if (zdata) {
+		l->data = zdata;
+		Z_ADDREF_P(zdata);
+	}
+
+	PHP_EVENT_COPY_FCALL_INFO(l->fci, l->fcc, &fci, &fcc);
+
+#if 0
+	/* Don't do this, since libevent calls accept() afterwards, thus producing
+	 * new file descriptor. The new descriptor is available in
+	 * _php_event_listener_cb() callback. */
+
+	if (Z_TYPE_PP(ppzfd) == IS_RESOURCE) {
+		/* lval of ppzfd is the resource ID */
+		l->stream_id = Z_LVAL_PP(ppzfd);
+		zend_list_addref(Z_LVAL_PP(ppzfd));
+	} else {
+		l->stream_id = -1;
+	}
+#endif
+	l->stream_id = -1;
+
+	l->base_id = Z_LVAL_P(zbase);
+	zend_list_addref(l->base_id);
+
+	TSRMLS_SET_CTX(l->thread_ctx);
+
+	l->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, l, le_event_listener);
+
+	PHP_EVENT_ASSERT(l->rsrc_id);
+}
+/* }}} */
+
+
+/* {{{ proto void evconnlistener_free(resource listener);
+ * Free an event connect listener resource */
+PHP_FUNCTION(evconnlistener_free)
+{
+	php_event_listener_t *l;
+	zval                 *zlistener;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zlistener) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_LISTENER(l, zlistener);
+
+	zend_list_delete(l->rsrc_id);
+}
+/* }}} */
+
+/* {{{ proto bool evconnlistener_enable(resource listener);
+ * Enable an event connect listener resource */
+PHP_FUNCTION(evconnlistener_enable)
+{
+	php_event_listener_t *l;
+	zval                 *zlistener;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zlistener) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_LISTENER(l, zlistener);
+
+	if (evconnlistener_enable(l->listener)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool evconnlistener_disable(resource listener);
+ * Disable an event connect listener resource */
+PHP_FUNCTION(evconnlistener_disable)
+{
+	php_event_listener_t *l;
+	zval                 *zlistener;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zlistener) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_LISTENER(l, zlistener);
+
+	if (evconnlistener_disable(l->listener)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto void evconnlistener_set_cb(resource listener, callable cb[, mixed arg = NULL]);
+ * Adjust event connect listener's callback and optionally the callback argument.
+ * Both cb and arg may be NULL.
+ */
+PHP_FUNCTION(evconnlistener_set_cb)
+{
+	php_event_listener_t  *l;
+	zval                  *zlistener;
+	zend_fcall_info        fci       = empty_fcall_info;
+	zend_fcall_info_cache  fcc       = empty_fcall_info_cache;
+	zval                  *zarg      = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rf!|z!",
+				&zlistener, &fci, &fcc, &zarg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_LISTENER(l, zlistener);
+
+	if (ZEND_FCI_INITIALIZED(fci)) {
+		PHP_EVENT_FREE_FCALL_INFO(l->fci, l->fcc);
+		PHP_EVENT_COPY_FCALL_INFO(l->fci, l->fcc, &fci, &fcc);
+	}
+
+	if (zarg) {
+		if (l->data) {
+			zval_ptr_dtor(&l->data);
+		}
+
+		Z_ADDREF_P(zarg);
+		l->data = zarg;
+	}
+
+	/*
+	 * No sense in the following call, since the callback and the pointer
+	 * remain the same
+	 * evconnlistener_set_cb(l->listener, _php_event_listener_cb, (void *) l);
+	 */
+}
+/* }}} */
+
+/* {{{ proto void evconnlistener_set_error_cb(resource listener, callable cb);
+ * Set event listener's error callback
+ */
+PHP_FUNCTION(evconnlistener_set_error_cb)
+{
+	php_event_listener_t  *l;
+	zval                  *zlistener;
+	zend_fcall_info        fci;
+	zend_fcall_info_cache  fcc;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rf",
+				&zlistener, &fci, &fcc) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_LISTENER(l, zlistener);
+
+	if (ZEND_FCI_INITIALIZED(fci)) {
+		PHP_EVENT_FREE_FCALL_INFO(l->fci_err, l->fcc_err);
+		PHP_EVENT_COPY_FCALL_INFO(l->fci_err, l->fcc_err, &fci, &fcc);
+	}
+
+	/*
+	 * No much sense in the following call, since the callback and the pointer
+	 * remain the same. However, we have to set it once at least
+	 */
+	 evconnlistener_set_error_cb(l->listener, listener_error_cb);
+}
+/* }}} */
+
+#if LIBEVENT_VERSION_NUMBER >= 0x02000300
+/* {{{ proto resource evconnlistener_get_base(resource listener);
+ * Get event base associated with the connection listener
+ */
+PHP_FUNCTION(evconnlistener_get_base)
+{
+	php_event_listener_t *l;
+	zval                 *zlistener;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zlistener) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_LISTENER(l, zlistener);
+
+	/*
+	   base = evconnlistener_get_base(l->listener);
+	*/
+
+	if (l->base_id) {
+		/* Make sure base wouldn't be inexpectedly destroyed */
+		zend_list_addref(l->base_id);
+
+		RETURN_RESOURCE(l->base_id);
+	} else {
+		RETVAL_FALSE;
+	}
+}
+/* }}} */
+#endif
+
+
 /* Extra API functions END }}} */
 #endif
 

File examples/eio.php

 echo "step 1\n";
 
 $base = event_base_new();
-$fd   = eio_get_event_stream();
 
 echo "step 2\n";
 
+eio_init();
+
 eio_mkdir($dir, 0750, EIO_PRI_DEFAULT, "my_nop_cb");
 
-$event = event_new($base, $fd,
-	EVENT_READ | EVENT_WRITE | EVENT_PERSIST, function ($fd, $events, $base) {
+$event = event_new($base, eio_get_event_stream(),
+	EVENT_READ | EVENT_PERSIST, function ($fd, $events, $base) {
 	echo "step 5\n";
 
 	while (eio_nreqs()) {

File examples/listener.php

+<?php
+/*
+ * Simple echo server based on libevent's connection listener.
+ *
+ * Usage:
+ * 1) In one terminal window run:
+ *
+ * $ php listener.php
+ *
+ * 2) In another terminal window open up connection, e.g.:
+ *
+ * $ nc 127.0.0.1 9876
+ *
+ * 3) start typing. The server should repeat the input.
+ */
+
+function echo_read_cb($bev, $ctx) {
+    /* This callback is invoked when there is data to read on $bev. */
+    $input  = bufferevent_get_input($bev);
+    $output = bufferevent_get_output($bev);
+
+    /* Copy all the data from the input buffer to the output buffer. */
+    evbuffer_add_buffer($output, $input);
+}
+
+function echo_event_cb($bev, $events, $ctx) {
+    if ($events & EVENT_BEV_EVENT_ERROR)
+        echo "Error from bufferevent";
+    if ($events & (EVENT_BEV_EVENT_EOF | EVENT_BEV_EVENT_ERROR)) {
+        bufferevent_free($bev);
+    }
+}
+
+function accept_conn_cb($listener, $fd, $address, $ctx) {
+    /* We got a new connection! Set up a bufferevent for it. */
+
+    //$base = $ctx;
+    $base = evconnlistener_get_base($listener);
+
+    $bev = bufferevent_socket_new($base, $fd, EVENT_BEV_OPT_CLOSE_ON_FREE);
+
+    bufferevent_setcb($bev, "echo_read_cb", NULL, "echo_event_cb", NULL);
+
+    bufferevent_enable($bev, EVENT_READ | EVENT_WRITE);
+}
+
+function accept_error_cb($listener, $ctx) {
+    $base = evconnlistener_get_base($listener);
+
+    fprintf(STDERR, "Got an error %d (%s) on the listener. "
+        ."Shutting down.\n",
+		event_socket_get_last_errno(),
+		event_socket_get_last_error());
+
+    event_base_loopexit($base, NULL);
+}
+
+
+$port = 9876;
+
+if ($argc > 1) {
+    $port = (int) $argv[1];
+}
+if ($port <= 0 || $port > 65535) {
+    puts("Invalid port");
+    return 1;
+}
+
+$base = event_base_new();
+if (!$base) {
+    echo "Couldn't open event base";
+	exit(1);
+}
+
+/* Variant #1 */
+$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
+if (!socket_bind($socket, '0.0.0.0', $port)) {
+	echo "Unable to bind socket\n";
+	exit(1);
+}
+$listener = evconnlistener_new($base, "accept_conn_cb", $base,
+    EVENT_LEV_OPT_CLOSE_ON_FREE | EVENT_LEV_OPT_REUSEABLE, -1, $socket);
+
+/* Variant #2 */
+/*
+$listener = evconnlistener_new_bind($base, "accept_conn_cb", $base,
+    EVENT_LEV_OPT_CLOSE_ON_FREE | EVENT_LEV_OPT_REUSEABLE, -1, "0.0.0.0:$port");
+ */
+
+if (!$listener) {
+    echo "Couldn't create listener";
+	exit(1);
+}
+evconnlistener_set_error_cb($listener, "accept_error_cb");
+
+event_base_dispatch($base);
 	ZEND_ARG_INFO(0, data) 
 ZEND_END_ARG_INFO();
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evbuffer_add_buffer, 0, 0, 2)
+	ZEND_ARG_INFO(0, outbuf)
+	ZEND_ARG_INFO(0, inbuf) 
+ZEND_END_ARG_INFO();
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_evbuffer_remove, 0, 0, 3)
 	ZEND_ARG_INFO(0, buf)
 	ZEND_ARG_INFO(1, data)
 	ZEND_ARG_INFO(0, max_bytes)
 ZEND_END_ARG_INFO();
 
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_socket_1, 0, 0, 1)
+	ZEND_ARG_INFO(0, socket)
+ZEND_END_ARG_INFO();
+
 /* ARGINFO END }}} */
 
 
 	ZEND_ARG_INFO(0, initialize)
 ZEND_END_ARG_INFO();
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evconnlistener_1, 0, 0, 1)
+	ZEND_ARG_INFO(0, listener)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evconnlistener_new, 0, 0, 5)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, data)
+	ZEND_ARG_INFO(0, flags)
+	ZEND_ARG_INFO(0, backlog)
+	ZEND_ARG_INFO(0, stream)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evconnlistener_new_bind, 0, 0, 5)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, data)
+	ZEND_ARG_INFO(0, flags)
+	ZEND_ARG_INFO(0, backlog)
+	ZEND_ARG_INFO(0, addr)
+	ZEND_ARG_INFO(0, port)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evconnlistener_set_cb, 0, 0, 2)
+	ZEND_ARG_INFO(0, listener)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evconnlistener_set_error_cb, 0, 0, 2)
+	ZEND_ARG_INFO(0, listener)
+	ZEND_ARG_INFO(0, cb)
+ZEND_END_ARG_INFO();
+
+
 /* ARGINFO for extra API END }}} */
 #endif
 
 	PHP_FE(evbuffer_new,            arginfo_event__void)
 	PHP_FE(evbuffer_free,           arginfo_evbuffer_1)
 	PHP_FE(evbuffer_freeze,         arginfo_evbuffer_freeze)
+	PHP_FE(evbuffer_unfreeze,       arginfo_evbuffer_freeze)
 	PHP_FE(evbuffer_get_length,     arginfo_evbuffer_1)
 	PHP_FE(evbuffer_lock,           arginfo_evbuffer_1)
 	PHP_FE(evbuffer_unlock,         arginfo_evbuffer_1)
 	PHP_FE(evbuffer_enable_locking, arginfo_evbuffer_1)
 	PHP_FE(evbuffer_add,            arginfo_evbuffer_add)
+	PHP_FE(evbuffer_add_buffer,     arginfo_evbuffer_add_buffer)
 	PHP_FE(evbuffer_remove,         arginfo_evbuffer_remove)
 
+	PHP_FE(event_socket_get_last_errno, arginfo_event_socket_1)
+	PHP_FE(event_socket_get_last_error, arginfo_event_socket_1)
+
 	/* These aliases are for compatibility with libevent extension */
 
 	PHP_FALIAS(event_timer_new,            evtimer_new,               arginfo_evtimer_new)
 #if HAVE_EVENT_EXTRA_LIB
 /* {{{ Extra API */
 
-	PHP_FE(evdns_base_new, arginfo_evdns_base_new)
+	PHP_FE(evdns_base_new,  arginfo_evdns_base_new)
 	PHP_FE(evdns_base_free, arginfo_evdns_base_1)
+
+	PHP_FE(evconnlistener_new,          arginfo_evconnlistener_new)
+	PHP_FE(evconnlistener_new_bind,     arginfo_evconnlistener_new_bind)
+	PHP_FE(evconnlistener_free,         arginfo_evconnlistener_1)
+	PHP_FE(evconnlistener_enable,       arginfo_evconnlistener_1)
+	PHP_FE(evconnlistener_disable,      arginfo_evconnlistener_1)
+	PHP_FE(evconnlistener_set_cb,       arginfo_evconnlistener_set_cb)
+	PHP_FE(evconnlistener_set_error_cb, arginfo_evconnlistener_set_error_cb)
+#if LIBEVENT_VERSION_NUMBER >= 0x02000300
+	PHP_FE(evconnlistener_get_base,     arginfo_evconnlistener_1)
+#endif
 	
 /* Extra API END}}} */
 #endif
 PHP_FUNCTION(evbuffer_new);
 PHP_FUNCTION(evbuffer_free);
 PHP_FUNCTION(evbuffer_freeze);
+PHP_FUNCTION(evbuffer_unfreeze);
 PHP_FUNCTION(evbuffer_get_length);
 PHP_FUNCTION(evbuffer_lock);
 PHP_FUNCTION(evbuffer_unlock);
 PHP_FUNCTION(evbuffer_enable_locking);
 PHP_FUNCTION(evbuffer_add);
 PHP_FUNCTION(evbuffer_remove);
+PHP_FUNCTION(evbuffer_add_buffer);
 
+PHP_FUNCTION(event_socket_get_last_errno);
+PHP_FUNCTION(event_socket_get_last_error);
 
 #if HAVE_EVENT_EXTRA_LIB
 /* {{{ Extra API */
 PHP_FUNCTION(evdns_base_new);
 PHP_FUNCTION(evdns_base_free);
 
+PHP_FUNCTION(evconnlistener_new);
+PHP_FUNCTION(evconnlistener_new_bind);
+PHP_FUNCTION(evconnlistener_free);
+PHP_FUNCTION(evconnlistener_enable);
+PHP_FUNCTION(evconnlistener_disable);
+PHP_FUNCTION(evconnlistener_set_cb);
+PHP_FUNCTION(evconnlistener_set_error_cb);
+#if LIBEVENT_VERSION_NUMBER >= 0x02000300
+PHP_FUNCTION(evconnlistener_get_base);
+#endif
+
 /* Extra API END }}} */
 #endif
 
 
 #if HAVE_EVENT_EXTRA_LIB
 # define PHP_EVENT_DNS_BASE_RES_NAME "Event DNS Base"
+# define PHP_EVENT_LISTENER_RES_NAME "Event Connection Listener"
 #endif
 
 PHP_MINIT_FUNCTION(event);
 	int                rsrc_id;    /* Resource ID of the dns base */
 } php_event_dns_base_t;
 
+typedef struct {
+	struct evconnlistener *listener;
+	int                    stream_id;   /* Resource ID of the socket file descriptor */
+	int                    base_id;     /* Resource ID of the event base      */
+	int                    rsrc_id;     /* Resource ID of the evconnlistener         */
+	zval                  *data;        /* User custom data passed to callback       */
+	/* Accept callback */
+	zend_fcall_info       *fci;
+	zend_fcall_info_cache *fcc;
+	/* Error callback */
+	zend_fcall_info       *fci_err;
+	zend_fcall_info_cache *fcc_err;
+
+	PHP_EVENT_COMMON_THREAD_CTX;
+} php_event_listener_t;
+
 #endif/* HAVE_EVENT_EXTRA_LIB }}} */
 
-typedef struct event_base php_event_base_t;
+typedef struct {
+	struct event_base *base;
+	int                rsrc_id;   /* Resource ID of the event base */
+} php_event_base_t;
 typedef struct event_config php_event_config_t;
 typedef double php_event_timestamp_t;