Ruslan Osmanov avatar Ruslan Osmanov committed 07c6a7f

Refact: php_event_cb_arg_t removed(it's members merged into php_event_t
Add: bufferevents' draft(bufferevent_socket_new, bufferevent_socket_connect, constants)

Comments (0)

Files changed (6)

 
 #include <signal.h>
 
-#include "php_event.h"
-#include "structs.h"
 #if PHP_VERSION_ID >= 50301 && (HAVE_SOCKETS || defined(COMPILE_DL_SOCKETS))
 # include <ext/sockets/php_sockets.h>
 # define PHP_EVENT_SOCKETS_SUPPORT
 # include <event2/tag.h>
 #endif
 
+#ifdef ZTS
+# include <TSRM.h>
+#endif
+
 #if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000100
 # error "This version of Libevent is not supported; get 2.0.1-alpha or later."
 #endif
 
-#ifdef ZTS
-# include "TSRM.h"
-#endif
+#include "php_event.h"
+#include "structs.h"
 
 #endif
 
 static int le_event_base;
 /* Represents the config returned by event_config_new */
 static int le_event_config;
+/* Represents bufferevent returned by ex. bufferevent_socket_new() */
+static int le_event_bevent;
 
 static const zend_module_dep event_deps[] = {
 	ZEND_MOD_OPTIONAL("sockets")
 #define PHP_EVENT_FETCH_CONFIG(cfg, zcfg) \
 	ZEND_FETCH_RESOURCE((cfg), php_event_config_t *, &(zcfg), -1, PHP_EVENT_CONFIG_RES_NAME, le_event_config)
 
+#define PHP_EVENT_FETCH_BEVENT(b, zb) \
+	ZEND_FETCH_RESOURCE((b), php_event_bevent_t *, &(zb), -1, PHP_EVENT_BEVENT_RES_NAME, le_event_bevent)
+
 #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                                   \
+    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;                                      \
+    RETURN_FALSE
+
 
 /* {{{ Private functions */
 
 }
 /* }}} */
 
+#ifdef PHP_EVENT_SOCKETS_SUPPORT 
+/* {{{ sockets_zval_to_fd 
+ * Get numeric file descriptor from PHP stream or Socket resource */
+static php_socket_t sockets_zval_to_fd(zval **ppfd TSRMLS_DC)
+{
+	php_socket *php_sock;
+
+	if (ZEND_FETCH_RESOURCE_NO_RETURN(php_sock, php_socket *, ppfd, -1, NULL, php_sockets_le_socket())) {
+		if (php_sock->error) {
+			if (!php_sock->blocking && php_sock->error == EINPROGRESS) {
+				/* Skip, no messages, since we should provide non-blocking
+				 * sockets to bufferevent functions */
+			} else {
+				return -1;
+			}
+		}
+
+		return php_sock->bsd_socket;
+	} 
+
+	php_error_docref(NULL TSRMLS_CC, E_WARNING,
+			"valid PHP socket resource expected");
+	return -1;
+}
+/* }}} */
+#endif
+
+
 /* {{{ zval_to_signum */
 static zend_always_inline evutil_socket_t zval_to_signum(zval **ppzfd)
 {
 {
 	php_event_t *e = (php_event_t *) arg;
 
-	PHP_EVENT_ASSERT(e && e->arg);
-	PHP_EVENT_ASSERT(e->arg->fci && e->arg->fcc);
+	PHP_EVENT_ASSERT(e);
+	PHP_EVENT_ASSERT(e->fci && e->fcc);
 
-	php_event_cb_arg_t  *cbarg      = e->arg;
-	zend_fcall_info     *pfci       = cbarg->fci;
-	zval                *arg_data   = cbarg->data;
+	zend_fcall_info     *pfci       = e->fci;
+	zval                *arg_data   = e->data;
 	zval                *arg_fd;
 	zval                *arg_what;
 	zval               **args[3];
 	zval                *retval_ptr;
 
-	TSRMLS_FETCH_FROM_CTX(cbarg->thread_ctx);
+	TSRMLS_FETCH_FROM_CTX(e->thread_ctx);
 
 	if (ZEND_FCI_INITIALIZED(*pfci)) {
 		/* Setup callback arguments */
         pfci->param_count    = 3;
         pfci->no_separation  = 1;
 
-        if (zend_call_function(pfci, cbarg->fcc TSRMLS_CC) == SUCCESS
+        if (zend_call_function(pfci, e->fcc TSRMLS_CC) == SUCCESS
                 && retval_ptr) {
             zval_ptr_dtor(&retval_ptr);
         } else {
 {
 	php_event_t *e = (php_event_t *) arg;
 
-	PHP_EVENT_ASSERT(e && e->arg);
+	PHP_EVENT_ASSERT(e);
 	PHP_EVENT_ASSERT(what & EV_TIMEOUT);
-	PHP_EVENT_ASSERT(e->arg->fci && e->arg->fcc);
+	PHP_EVENT_ASSERT(e->fci && e->fcc);
 
-	php_event_cb_arg_t  *cbarg      = e->arg;
-	zend_fcall_info     *pfci       = cbarg->fci;
-	zval                *arg_data   = cbarg->data;
+	zend_fcall_info     *pfci       = e->fci;
+	zval                *arg_data   = e->data;
 	zval               **args[1];
 	zval                *retval_ptr;
 
-	TSRMLS_FETCH_FROM_CTX(cbarg->thread_ctx);
+	TSRMLS_FETCH_FROM_CTX(e->thread_ctx);
 
 	if (ZEND_FCI_INITIALIZED(*pfci)) {
 		/* Setup callback arg */
 		pfci->param_count	 = 1;
 		pfci->no_separation  = 1;
 
-        if (zend_call_function(pfci, cbarg->fcc TSRMLS_CC) == SUCCESS
+        if (zend_call_function(pfci, e->fcc TSRMLS_CC) == SUCCESS
                 && retval_ptr) {
             zval_ptr_dtor(&retval_ptr);
         } else {
 }
 /* }}} */
 
-/* {{{ event_cb_arg_new
- * Allocates memory for php_event_cb_arg_t and returns ptr to it, fills in the passed fields */
-static zend_always_inline php_event_cb_arg_t *event_cb_arg_new(zval *arg, const zend_fcall_info *pfci, const zend_fcall_info_cache *pfcc TSRMLS_DC)
+/* {{{ bevent_read_cb */
+static void bevent_read_cb(struct bufferevent *bev, void *ptr)
 {
-	php_event_cb_arg_t *cbarg = (php_event_cb_arg_t *) emalloc(sizeof(php_event_cb_arg_t));
-	memset(cbarg, 0, sizeof(php_event_cb_arg_t));
 
-	if (arg) {
-		Z_ADDREF_P(arg);
-	}
-	cbarg->data  = arg;
-
-	PHP_EVENT_COPY_FCALL_INFO(cbarg->fci, cbarg->fcc, pfci, pfcc);
+}
+/* }}} */
 
-	TSRMLS_SET_CTX(cbarg->thread_ctx);
+/* {{{ bevent_write_cb */
+static void bevent_write_cb(struct bufferevent *bev, void *ptr)
+{
 
-	return cbarg;
 }
 /* }}} */
 
-/* {{{ event_cb_arg_free
- * Frees memory allocated with event_cb_arg_new */
-static zend_always_inline void event_cb_arg_free(php_event_cb_arg_t *cbarg)
+/* {{{ bevent_event_cb */
+static void bevent_event_cb(struct bufferevent *bev, short events, void *ptr)
 {
-	if (cbarg) {
-		if (cbarg->data) {
-			zval_ptr_dtor(&cbarg->data);
-		}
 
-		PHP_EVENT_FREE_FCALL_INFO(cbarg->fci, cbarg->fcc);
-		efree(cbarg);
-	}
 }
 /* }}} */
 
 {
 	php_event_t *e = (php_event_t *) rsrc->ptr;
 
-	event_cb_arg_free(e->arg);
-	if (e->stream_id >= 0) { /* stdin fd == 0 */
-		zend_list_delete(e->stream_id);
+	if (e) {
+		if (e->data) {
+			zval_ptr_dtor(&e->data);
+		}
+
+		PHP_EVENT_FREE_FCALL_INFO(e->fci, e->fcc);
+
+		if (e->stream_id >= 0) { /* stdin fd == 0 */
+			zend_list_delete(e->stream_id);
+		}
+		event_free(e->event);
+		efree(e);
 	}
-	event_free(e->event);
-	efree(e);
 }
 /* }}} */
 
 }
 /* }}} */
 
+/* {{{ php_event_bevent_dtor */
+static void php_event_bevent_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+	php_event_bevent_t *b = (php_event_bevent_t *) rsrc->ptr;
+
+	if (b->data) {
+		zval_ptr_dtor(&b->data);
+	}
+
+	PHP_EVENT_FREE_FCALL_INFO(b->fci_read,  b->fcc_read);
+	PHP_EVENT_FREE_FCALL_INFO(b->fci_write, b->fcc_write);
+	PHP_EVENT_FREE_FCALL_INFO(b->fci_event, b->fcc_event);
+
+	if (b->stream_id >= 0) { /* stdin fd == 0 */
+		zend_list_delete(b->stream_id);
+	}
+
+	bufferevent_free(b->bevent);
+
+	efree(b);
+}
+/* }}} */
+
 /* Private functions }}} */
 
 #define PHP_EVENT_REG_CONST_LONG(name, real_name) \
 	le_event        = zend_register_list_destructors_ex(php_event_dtor, NULL, PHP_EVENT_RES_NAME, module_number);
 	le_event_base   = zend_register_list_destructors_ex(php_event_base_dtor, NULL, PHP_EVENT_BASE_RES_NAME, module_number);
 	le_event_config = zend_register_list_destructors_ex(php_event_config_dtor, NULL, PHP_EVENT_CONFIG_RES_NAME, module_number);
+	le_event_bevent = zend_register_list_destructors_ex(php_event_bevent_dtor, NULL, PHP_EVENT_BEVENT_RES_NAME, module_number);
 
 	/* Loop flags */
 	PHP_EVENT_REG_CONST_LONG(EVENT_LOOP_ONCE,     EVLOOP_ONCE);
 #ifdef EVENT_BASE_FLAG_PRECISE_TIMER
 	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_PRECISE_TIMER,        EVENT_BASE_FLAG_PRECISE_TIMER);
 #endif
-	
+
+	/* Buffer event flags */
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_EVENT_READING,   BEV_EVENT_READING);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_EVENT_WRITING,   BEV_EVENT_WRITING);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_EVENT_EOF,       BEV_EVENT_EOF);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_EVENT_ERROR,     BEV_EVENT_ERROR);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_EVENT_TIMEOUT,   BEV_EVENT_TIMEOUT);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_EVENT_CONNECTED, BEV_EVENT_CONNECTED);
+
+	/* Option flags for bufferevents */
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_OPT_CLOSE_ON_FREE,    BEV_OPT_CLOSE_ON_FREE);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_OPT_THREADSAFE,       BEV_OPT_THREADSAFE);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_OPT_DEFER_CALLBACKS,  BEV_OPT_DEFER_CALLBACKS);
+#if LIBEVENT_VERSION_NUMBER >= 0x02000500
+	PHP_EVENT_REG_CONST_LONG(EVENT_BEV_OPT_UNLOCK_CALLBACKS, BEV_OPT_UNLOCK_CALLBACKS);
+#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);
 
 /* {{{ API functions */
 
-/* {{{ proto resource event_new(resource base, mixed fd, int what, callable cb[, zval arg = NULL]);
- * Creates new event */
-PHP_FUNCTION(event_new)
-{
-	zval                   *zbase;
-	php_event_base_t       *base;
-	zval                  **ppzfd;
-	evutil_socket_t         fd;
-	long                    what;
-	zend_fcall_info         fci     = empty_fcall_info;
-	zend_fcall_info_cache   fcc     = empty_fcall_info_cache;
-	zval                   *arg     = NULL;
-	php_event_t            *e;
-	struct event           *event;
-
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rZlf|z",
-				&zbase, &ppzfd, &what, &fci, &fcc, &arg) == FAILURE) {
-		return;
-	}
-
-	if (what & ~(EV_TIMEOUT | EV_READ | EV_WRITE | EV_SIGNAL | EV_PERSIST | EV_ET)) {
-		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid mask");
-		RETURN_FALSE;
-	}
-
-	if (what & EV_SIGNAL) {
-		if (zval_to_signum(ppzfd) == -1) {
-			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid signal passed");
-			RETURN_FALSE;
-		}
-	} else {
-		fd = (evutil_socket_t) zval_to_fd(ppzfd TSRMLS_CC);
-		if (fd < 0) {
-			RETURN_FALSE;
-		}
-	}
-
-	PHP_EVENT_FETCH_BASE(base, 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);
-	if (!event) {
-		efree(e);
-		php_error_docref(NULL TSRMLS_CC, E_ERROR, "event_new failed");
-		RETURN_FALSE;
-	}
-
-	e->event = event;
-	e->arg   = event_cb_arg_new(arg, &fci, &fcc TSRMLS_CC);
-
-	if (what & EV_SIGNAL) {
-		e->stream_id = -1; /* stdin fd = 0 */
-	} else {
-		/* lval of ppzfd is the resource ID */
-		e->stream_id = Z_LVAL_PP(ppzfd);
-		zend_list_addref(Z_LVAL_PP(ppzfd));
-	}
-
-	ZEND_REGISTER_RESOURCE(return_value, e, le_event);
-}
-/* }}} */
-
 
 /* {{{ proto resource evtimer_new(resource base, callable cb[, zval arg = NULL]);
  * Creates new event */
 	}
 
 	e->event = event;
-	e->arg   = event_cb_arg_new(arg, &fci, &fcc TSRMLS_CC);
+
+	if (arg) {
+		Z_ADDREF_P(arg);
+	}
+	e->data = arg;
+
+	PHP_EVENT_COPY_FCALL_INFO(e->fci, e->fcc, &fci, &fcc);
+
+	TSRMLS_SET_CTX(e->thread_ctx);
 
 	e->stream_id = -1; /* stdin fd = 0 */
 
 	PHP_EVENT_FETCH_BASE(base, zbase);
 
 	if (ZEND_FCI_INITIALIZED(fci)) {
-		if (e->arg->fci && ZEND_FCI_INITIALIZED(*e->arg->fci)) {
-			PHP_EVENT_FREE_FCALL_INFO(e->arg->fci, e->arg->fcc);
+		if (e->fci && ZEND_FCI_INITIALIZED(*e->fci)) {
+			PHP_EVENT_FREE_FCALL_INFO(e->fci, e->fcc);
 		}
 
-		PHP_EVENT_COPY_FCALL_INFO(e->arg->fci, e->arg->fcc, &fci, &fcc);
+		PHP_EVENT_COPY_FCALL_INFO(e->fci, e->fcc, &fci, &fcc);
 	}
 
 	if (arg) {
-		if (e->arg->data) {
-			zval_ptr_dtor(&e->arg->data);
+		if (e->data) {
+			zval_ptr_dtor(&e->data);
 		}
-		e->arg->data = arg;
+		e->data = arg;
 		Z_ADDREF_P(arg);
 	}
 
 /* }}} */
 
 
+
+/* {{{ proto resource event_new(resource base, mixed fd, int what, callable cb[, zval arg = NULL]);
+ * Creates new event */
+PHP_FUNCTION(event_new)
+{
+	zval                   *zbase;
+	php_event_base_t       *base;
+	zval                  **ppzfd;
+	evutil_socket_t         fd;
+	long                    what;
+	zend_fcall_info         fci     = empty_fcall_info;
+	zend_fcall_info_cache   fcc     = empty_fcall_info_cache;
+	zval                   *arg     = NULL;
+	php_event_t            *e;
+	struct event           *event;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rZlf|z",
+				&zbase, &ppzfd, &what, &fci, &fcc, &arg) == FAILURE) {
+		return;
+	}
+
+	if (what & ~(EV_TIMEOUT | EV_READ | EV_WRITE | EV_SIGNAL | EV_PERSIST | EV_ET)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid mask");
+		RETURN_FALSE;
+	}
+
+	if (what & EV_SIGNAL) {
+		if (zval_to_signum(ppzfd) == -1) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid signal passed");
+			RETURN_FALSE;
+		}
+	} else {
+		fd = (evutil_socket_t) zval_to_fd(ppzfd TSRMLS_CC);
+		if (fd < 0) {
+			RETURN_FALSE;
+		}
+	}
+
+	PHP_EVENT_FETCH_BASE(base, 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);
+	if (!event) {
+		efree(e);
+		php_error_docref(NULL TSRMLS_CC, E_ERROR, "event_new failed");
+		RETURN_FALSE;
+	}
+
+	e->event = event;
+
+	if (arg) {
+		Z_ADDREF_P(arg);
+	}
+	e->data = arg;
+
+	PHP_EVENT_COPY_FCALL_INFO(e->fci, e->fcc, &fci, &fcc);
+
+	TSRMLS_SET_CTX(e->thread_ctx);
+
+	if (what & EV_SIGNAL) {
+		e->stream_id = -1; /* stdin fd = 0 */
+	} else {
+		/* lval of ppzfd is the resource ID */
+		e->stream_id = Z_LVAL_PP(ppzfd);
+		zend_list_addref(Z_LVAL_PP(ppzfd));
+	}
+
+	ZEND_REGISTER_RESOURCE(return_value, e, le_event);
+}
+/* }}} */
+
 /* {{{ proto bool event_set(resource event, resource base, mixed fd,[ int what = NULL[, callable cb = NULL[, zval arg = NULL]]]);
  * Re-configures event.
  * Note, this function doesn't invoke obsolete libevent's event_set. It calls event_assign instead.  */
 	}
 
 	if (ZEND_FCI_INITIALIZED(fci)) {
-		PHP_EVENT_FREE_FCALL_INFO(e->arg->fci, e->arg->fcc);
-		PHP_EVENT_COPY_FCALL_INFO(e->arg->fci, e->arg->fcc, &fci, &fcc);
+		PHP_EVENT_FREE_FCALL_INFO(e->fci, e->fcc);
+		PHP_EVENT_COPY_FCALL_INFO(e->fci, e->fcc, &fci, &fcc);
 	}
 
 	if (arg) {
-		if (e->arg->data) {
-			zval_ptr_dtor(&e->arg->data);
+		if (e->data) {
+			zval_ptr_dtor(&e->data);
 		}
-		e->arg->data = arg;
+		e->data = arg;
 		Z_ADDREF_P(arg);
 	}
 
 /* }}} */
 
 
+
 /* {{{ proto resource event_base_new(void);
  * Returns resource representing new event base */
 PHP_FUNCTION(event_base_new)
 #endif
 
 
+
+/* {{{ proto resource bufferevent_socket_new(resource base[, mixed fd = NULL[, int options = 0]]);
+ * Create a socket-based bufferevent.
+ * options is one of EVENT_BEV_OPT_* constants, or 0. */
+PHP_FUNCTION(bufferevent_socket_new)
+{
+#ifndef PHP_EVENT_SOCKETS_SUPPORT 
+	PHP_EVENT_RET_SOCKETS_REQUIRED;
+#else
+	zval                *zbase;
+	php_event_base_t    *base;
+	zval               **ppzfd   = NULL;
+	evutil_socket_t      fd;
+	long                 options = 0;
+	php_event_bevent_t  *b;
+	struct bufferevent  *bevent;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|Z!l",
+				&zbase, &ppzfd, &options) == FAILURE) {
+		return;
+	}
+
+	if (ppzfd == NULL) {
+		/* 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);
+		if (fd < 0) {
+			RETURN_FALSE;
+		}
+		/* Make sure that the socket you provide to bufferevent_socket_new is
+		 * in non-blocking mode(libevent's tip). */
+		evutil_make_socket_nonblocking(fd);
+	} else {
+		fd = -1; /* 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));
+
+	bevent = bufferevent_socket_new(base, fd, options);
+	if (bevent == NULL) {
+		efree(b);
+		php_error_docref(NULL TSRMLS_CC, E_ERROR,
+				"Failed to allocate bufferevent for socket");
+		RETURN_FALSE;
+	}
+
+	b->bevent = bevent;
+
+	/* lval of ppzfd is the resource ID */
+	b->stream_id = Z_LVAL_PP(ppzfd);
+	zend_list_addref(Z_LVAL_PP(ppzfd));
+
+	ZEND_REGISTER_RESOURCE(return_value, b, le_event_bevent);
+#endif
+}
+/* }}} */
+
+/* {{{ proto bool bufferevent_socket_connect(resource bevent, string addr);
+ * Connect bufferevent's socket to given address(optionally with port).
+ * Recognized address formats:
+ *    [IPv6Address]:port
+ *    [IPv6Address]
+ *    IPv6Address
+ *    IPv4Address:port
+ *    IPv4Address
+ * The function available since libevent 2.0.2-alpha.
+ */
+PHP_FUNCTION(bufferevent_socket_connect)
+{
+#ifndef PHP_EVENT_SOCKETS_SUPPORT 
+	PHP_EVENT_RET_SOCKETS_REQUIRED;
+#elif LIBEVENT_VERSION_NUMBER < 0x02000200
+	php_error_docref(NULL TSRMLS_CC, E_ERROR,
+			"bufferevent_socket_connect is available since libevent "
+			"2.0.2-alpha. Please upgrade libevent distribution");
+	RETURN_FALSE;
+#else
+	php_event_bevent_t *bev;
+	zval               *zbevent;
+	char               *addr;
+	int                 addr_len;
+	struct sockaddr     sa;
+	int                 sa_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
+				&zbevent, &addr, &addr_len) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BEVENT(bev, zbevent);
+
+	if (evutil_parse_sockaddr_port(addr, &sa, &sa_len)) {
+		php_error_docref(NULL TSRMLS_CC, E_ERROR,
+				"Failed parsing address: the address is not well-formed, "
+				"or the port is out of range");
+		RETURN_FALSE;
+	}
+
+	if (bufferevent_socket_connect(bev->bevent, &sa, sa_len)) {
+		php_error_docref(NULL TSRMLS_CC, E_ERROR,
+				"Failed starting connection");
+		RETURN_FALSE;
+	}
+
+	RETVAL_TRUE;
+#endif
+}
+/* }}} */
+
+/* {{{ proto void bufferevent_setcb(resource bevent, callable readcb, callable writecb, callable eventcb[, mixed arg = NULL]);
+ * Changes one or more of the callbacks of a bufferevent.
+ * A callback may be disabled by passing NULL instead of the callable.
+ * arg is an argument passed to the callbacks.
+ */
+PHP_FUNCTION(bufferevent_setcb)
+{
+#ifndef PHP_EVENT_SOCKETS_SUPPORT 
+	PHP_EVENT_RET_SOCKETS_REQUIRED_NORET;
+#else
+	php_event_bevent_t    *bev;
+	zval                  *zbevent;
+	zend_fcall_info        fci_read;
+	zend_fcall_info_cache  fcc_read;
+	zend_fcall_info        fci_write;
+	zend_fcall_info_cache  fcc_write;
+	zend_fcall_info        fci_event;
+	zend_fcall_info_cache  fcc_event;
+	zval                  *zarg      = NULL;
+	bufferevent_data_cb    read_cb;
+	bufferevent_data_cb    write_cb;
+	bufferevent_event_cb   event_cb;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rf!f!f!|z!",
+				&zbevent,
+				&fci_read, &fcc_read,
+				&fci_write, &fcc_write,
+				&fci_event, &fcc_event,
+				&zarg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BEVENT(bev, zbevent);
+
+	if (ZEND_FCI_INITIALIZED(fci_read)) {
+		read_cb = bevent_read_cb;
+		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_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_COPY_FCALL_INFO(bev->fci_event, bev->fcc_event, &fci_event, &fcc_event);
+	} else {
+		event_cb = NULL;
+	}
+
+	if (zarg) {
+		Z_ADDREF_P(zarg);
+		bev->data = zarg;
+	}
+
+	TSRMLS_SET_CTX(bev->thread_ctx);
+
+	bufferevent_setcb(bev->bevent, read_cb, write_cb, event_cb, (void *) bev);
+#endif
+}
+/* }}} */
+
 /* API functions END }}} */
 
 /*
 ZEND_END_ARG_INFO();
 #endif
 
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_bufferevent_socket_new, 0, 0, 1)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, fd)
+	ZEND_ARG_INFO(0, options)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_bufferevent_socket_connect, 0, 0, 2)
+	ZEND_ARG_INFO(0, bevent)
+	ZEND_ARG_INFO(0, addr)
+ZEND_END_ARG_INFO();
+
 /* ARGINFO }}} */
 
 
 	PHP_FE(event_base_loopexit, arginfo_event_base_loopexit)
 	PHP_FE(event_base_loopbreak, arginfo_event_base_1)
 	PHP_FE(event_base_got_break, arginfo_event_base_1)
-	PHP_FE(event_base_got_exit, arginfo_event_base_1)
-	PHP_FE(event_base_gettimeofday_cached, arginfo_event_base_1)
+	PHP_FE(event_base_got_exit, arginfo_event_base_1) PHP_FE(event_base_gettimeofday_cached, arginfo_event_base_1)
 #if LIBEVENT_VERSION_NUMBER >= 0x02010100
 	PHP_FE(event_base_update_cache_time, arginfo_event_base_1)
 #endif
 	PHP_FE(event_config_set_max_dispatch_interval, arginfo_event_config_set_max_dispatch_interval)
 #endif
 
+	PHP_FE(bufferevent_socket_new, arginfo_bufferevent_socket_new)
+	PHP_FE(bufferevent_socket_connect, arginfo_bufferevent_socket_connect)
+
 	PHP_FE_END
 };
 /* }}} */
 PHP_FUNCTION(event_config_set_max_dispatch_interval);
 #endif
 
+PHP_FUNCTION(bufferevent_socket_new);
+PHP_FUNCTION(bufferevent_socket_connect);
+
 #endif /* PHP_EVENT_FE_H */
 
 /* 
 #define PHP_EVENT_RES_NAME "Event"
 #define PHP_EVENT_BASE_RES_NAME "Event Base"
 #define PHP_EVENT_CONFIG_RES_NAME "Event Config"
+#define PHP_EVENT_BEVENT_RES_NAME "Buffer Event"
 
 extern zend_module_entry event_module_entry;
 #define phpext_event_ptr &event_module_entry
 # define PHP_EVENT_COMMON_THREAD_CTX
 #endif
 
+/* Represents an event */
 typedef struct {
-	zval                  *data;    /* User custom data                            */
-	zend_fcall_info       *fci;     /* fci and fcc represent userspace callback    */
+	struct event          *event;       /* Pointer returned by event_new                        */
+	int                    stream_id;   /* Resource ID of the file descriptor, or signal number */
+	zval                  *data;        /* User custom data                                     */
+	/* fci and fcc represent userspace callback */
+	zend_fcall_info       *fci;
 	zend_fcall_info_cache *fcc;
+
 	PHP_EVENT_COMMON_THREAD_CTX;
-} php_event_cb_arg_t;
+} php_event_t;
 
+/* Represents a bufferevent */
 typedef struct {
-	struct event       *event;     /* Pointer returned by event_new                         */
-	int                 stream_id; /* Resource ID of the file descriptor, or signal number  */
-	php_event_cb_arg_t *arg;       /* For calling userspace func associated with this event */
-} php_event_t;
+	struct bufferevent    *bevent;
+	int                    stream_id;   /* Resource ID of the file descriptor */
+	zval                  *data;        /* User custom data                   */
+
+    /* fci and fcc members represent userspace callbacks */
+	zend_fcall_info       *fci_read;
+	zend_fcall_info_cache *fcc_read;
+	zend_fcall_info       *fci_write;
+	zend_fcall_info_cache *fcc_write;
+	zend_fcall_info       *fci_event;
+	zend_fcall_info_cache *fcc_event;
+
+	PHP_EVENT_COMMON_THREAD_CTX;
+} php_event_bevent_t;
 
 typedef struct event_base php_event_base_t;
 typedef struct event_config php_event_config_t;
 typedef double php_event_timestamp_t;
 
+
+#ifndef LIBEVENT_VERSION_NUMBER
+# error "<event2/*.h> must be included before " ## #__FILE__
+#endif
+
+#if LIBEVENT_VERSION_NUMBER < 0x02000200
+/* These types introduced in libevent 2.0.2-alpha */
+typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
+typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events,
+		void *ctx);
+#endif
+
 #endif	/* PHP_EVENT_STRUCTS_H */
 
 /*
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.