Commits

Ruslan Osmanov  committed 7da9ee6

Initial commit

  • Participants

Comments (0)

Files changed (14)

+CVS
+acinclude.m4
+aclocal.m4
+autom4te.cache
+build
+install-sh
+libtool
+ltmain.sh
+missing
+mkinstalldirs
+modules
+reconf.sh
+run-tests.php
+tags
+tmp-php.ini
+
+*.tgz
+*.in
+*.l[a,o]
+*.sw[p,o]
+*~
+*.deps
+*.libs
+*.diff
+*.log
+*.out
+*.exp
+Makefile*
+
+tests/*
+!tests/*.phpt
+
+config*
+!config.[m4,w32]
+event
+Ruslan Osmanov

File EXPERIMENTAL

Empty file added.
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+#ifndef PHP_EVENT_COMMON_H
+#define PHP_EVENT_COMMON_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <php.h>
+#include <php_ini.h>
+#include <ext/standard/info.h>
+#include <php_network.h>
+#include <php_streams.h>
+
+#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
+#else
+# error "No sockets!"
+#endif
+
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+#include <event2/buffer.h>
+#include <event2/util.h>
+
+#ifdef HAVE_HAVE_LIBEVENT_EXTRA
+# include <event2/dns.h>
+# include <event2/http.h>
+# include <event2/rpc.h>
+# include <event2/tag.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
+
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "common.h"
+
+extern const zend_function_entry event_functions[];
+
+/*
+ZEND_DECLARE_MODULE_GLOBALS(event)
+*/
+
+/* True global resources - no need for thread safety here */
+/* Represents an event returned by event_new */
+static int le_event;
+static int le_event_base;
+/* Represents the config returned by event_config_new */
+static int le_event_config;
+
+static const zend_module_dep event_deps[] = {
+	ZEND_MOD_OPTIONAL("sockets")
+	{NULL, NULL, NULL}
+};
+
+/* {{{ event_module_entry */
+zend_module_entry event_module_entry = {
+#if ZEND_MODULE_API_NO >= 20050922
+	STANDARD_MODULE_HEADER_EX,
+	NULL,
+	event_deps,
+#elif ZEND_MODULE_API_NO >= 20010901
+	STANDARD_MODULE_HEADER,
+#endif
+	"event",
+	event_functions,
+	PHP_MINIT(event),
+	PHP_MSHUTDOWN(event),
+	NULL,
+	NULL,
+	PHP_MINFO(event),
+#if ZEND_MODULE_API_NO >= 20010901
+	PHP_EVENT_VERSION,
+#endif
+	STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_EVENT
+ZEND_GET_MODULE(event)
+#endif
+
+#define PHP_EVENT_FETCH_BASE(base, zbase) \
+	ZEND_FETCH_RESOURCE((base), php_event_base_t *, &(zbase), -1, PHP_EVENT_BASE_RES_NAME, le_event_base)
+
+#define PHP_EVENT_FETCH_EVENT(event, zevent) \
+	ZEND_FETCH_RESOURCE((event), php_event_t *, &(zevent), -1, PHP_EVENT_RES_NAME, le_event)
+
+#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_TIMEVAL_SET(tv, t)                     \
+        do {                                             \
+            tv.tv_sec  = (long) t;                       \
+            tv.tv_usec = (long) ((t - tv.tv_sec) * 1e6); \
+        } while (0)
+
+/* {{{ Private functions */
+
+/* {{{ fatal_error_cb
+ * Is called when Libevent detects a non-recoverable internal error. */
+static void fatal_error_cb(int err)
+{
+	TSRMLS_FETCH();
+
+	php_error_docref(NULL TSRMLS_CC, E_ERROR,
+			"libevent detected a non-recoverable internal error, code: %d", err);
+}
+/* }}} */
+
+/* {{{ log_cb
+ * Overrides libevent's default error logging(it logs to stderr) */
+static void log_cb(int severity, const char *msg)
+{
+	/* TSRMLS_FETCH consumes a fair amount of resources.  But a ready-to-use
+	 * program shouldn't get any error logs. Nevertheless, we have no other way
+	 * to fetch TSRMLS. */
+	TSRMLS_FETCH();
+
+	int error_type;
+
+	switch (severity) {
+		case EVENT_LOG_DEBUG:
+			error_type = E_STRICT;
+		case EVENT_LOG_MSG:
+			error_type = E_NOTICE;
+		case EVENT_LOG_WARN:
+			error_type = E_WARNING;
+		case EVENT_LOG_ERR:
+			error_type = E_ERROR;
+		default:
+			error_type = E_NOTICE;
+	}
+
+	php_error_docref(NULL TSRMLS_CC, error_type, "%s", msg);
+}
+/* }}} */
+
+/* {{{ 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_socket_t  file_desc = -1;
+	php_stream   *stream;
+#ifdef PHP_EVENT_SOCKETS_SUPPORT 
+	php_socket   *php_sock;
+#endif
+
+	if (Z_TYPE_PP(ppfd) == IS_RESOURCE) {
+		/* PHP stream or PHP socket resource  */
+		if (ZEND_FETCH_RESOURCE_NO_RETURN(stream, php_stream *, ppfd, -1, NULL, php_file_le_stream())) {
+			/* PHP stream */
+			if (php_stream_can_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL)) {
+				if (php_stream_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL,
+							(void*) &file_desc, 1) != SUCCESS || file_desc < 0) {
+					return -1;
+				}
+			} else if (php_stream_can_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_INTERNAL)) {
+				if (php_stream_cast(stream, PHP_STREAM_AS_STDIO | PHP_STREAM_CAST_INTERNAL,
+							(void*) &file_desc, 1) != SUCCESS || file_desc < 0) {
+					return -1;
+				}
+			} else { /* STDIN, STDOUT, STDERR etc. */
+				php_stream_from_zval_no_verify(stream, ppfd);
+				if (stream == NULL) {
+					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed obtaining fd");
+					return -1;
+				}
+				file_desc = Z_LVAL_P(*ppfd);
+			}
+		} else {
+			/* PHP socket resource */
+#ifdef PHP_EVENT_SOCKETS_SUPPORT
+			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) {
+#ifdef PHP_EVENT_DEBUG
+						php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Operation in progress");
+#endif
+					} else
+						return -1;
+				}
+
+				return php_sock->bsd_socket;
+			} else {
+				php_error_docref(NULL TSRMLS_CC, E_WARNING,
+						"either valid PHP stream or valid PHP socket resource expected");
+			}
+#else
+			php_error_docref(NULL TSRMLS_CC, E_WARNING,
+					"valid PHP stream resource expected");
+#endif
+			return -1;
+		}
+	} else if (Z_TYPE_PP(ppfd) == IS_LONG) {
+		/* Numeric fd */
+		file_desc = Z_LVAL_PP(ppfd);
+		if (file_desc < 0) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid file descriptor passed");
+			return -1;
+		}
+	} else {
+		/* Invalid fd */
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid file descriptor passed");
+		return -1;
+	}
+
+	return file_desc;
+}
+/* }}} */
+
+/* {{{ zval_to_signum */
+static zend_always_inline evutil_socket_t zval_to_signum(zval **ppzfd)
+{
+	evutil_socket_t fd;
+
+	convert_to_long_ex(ppzfd);
+
+	fd = Z_LVAL_PP(ppzfd);
+
+	if (fd < 0 || fd >= NSIG) {
+		return -1;
+	}
+
+	return fd;
+}
+/* }}} */
+
+/* {{{ is_pending 
+Don't allow for pending or active event
+See http://www.wangafu.net/~nickm/libevent-book/Ref4_event.html */
+static zend_always_inline zend_bool is_pending(const struct event *e)
+{
+	return event_pending(e, EV_READ | EV_WRITE | EV_SIGNAL | EV_TIMEOUT, NULL);
+}
+/* }}} */
+
+/* {{{ timer_is_pending 
+Whether timer event is pending */
+static zend_always_inline zend_bool timer_is_pending(const struct event *e)
+{
+	return evtimer_pending(e, NULL);
+}
+/* }}} */
+
+/* {{{ event_cb */
+static void event_cb(evutil_socket_t fd, short what, void *arg)
+{
+	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_cb_arg_t  *cbarg      = e->arg;
+	zend_fcall_info     *pfci       = cbarg->fci;
+	zval                *arg_data   = cbarg->data;
+	zval                *arg_fd;
+	zval                *arg_what;
+	zval               **args[3];
+	zval                *retval_ptr;
+
+	TSRMLS_FETCH_FROM_CTX(cbarg->thread_ctx);
+
+	if (ZEND_FCI_INITIALIZED(*pfci)) {
+		/* Setup callback arguments */
+		MAKE_STD_ZVAL(arg_fd);
+		if (what & EV_SIGNAL) {
+			ZVAL_LONG(arg_fd, fd);
+		} else if (e->stream_id >= 0) {
+			ZVAL_RESOURCE(arg_fd, e->stream_id);
+			zend_list_addref(e->stream_id);
+		} else {
+			ZVAL_NULL(arg_fd);
+		}
+		args[0] = &arg_fd;
+
+		MAKE_STD_ZVAL(arg_what);
+		args[1] = &arg_what;
+		ZVAL_LONG(arg_what, what);
+
+		if (arg_data) {
+			Z_ADDREF_P(arg_data);
+		} else {
+			ALLOC_INIT_ZVAL(arg_data);
+		}
+		args[2] = &arg_data;
+
+ 		/* Prepare callback */
+        pfci->params         = args;
+        pfci->retval_ptr_ptr = &retval_ptr;
+        pfci->param_count    = 3;
+        pfci->no_separation  = 1;
+
+        if (zend_call_function(pfci, cbarg->fcc 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_fd);
+        zval_ptr_dtor(&arg_what);
+        zval_ptr_dtor(&arg_data);
+	}
+}
+/* }}} */
+
+/* {{{ timer_cb */
+static void timer_cb(evutil_socket_t fd, short what, void *arg)
+{
+	php_event_t *e = (php_event_t *) arg;
+
+	PHP_EVENT_ASSERT(e && e->arg);
+	PHP_EVENT_ASSERT(what & EV_TIMEOUT);
+	PHP_EVENT_ASSERT(e->arg->fci && e->arg->fcc);
+
+	php_event_cb_arg_t  *cbarg      = e->arg;
+	zend_fcall_info     *pfci       = cbarg->fci;
+	zval                *arg_data   = cbarg->data;
+	zval               **args[1];
+	zval                *retval_ptr;
+
+	TSRMLS_FETCH_FROM_CTX(cbarg->thread_ctx);
+
+	if (ZEND_FCI_INITIALIZED(*pfci)) {
+		/* Setup callback arg */
+		if (arg_data) {
+			Z_ADDREF_P(arg_data);
+		} else {
+			ALLOC_INIT_ZVAL(arg_data);
+		}
+		args[0] = &arg_data;
+
+ 		/* Prepare callback */
+        pfci->params         = args;
+        pfci->retval_ptr_ptr = &retval_ptr;
+        pfci->param_count    = 1;
+        pfci->no_separation  = 1;
+
+        if (zend_call_function(pfci, cbarg->fcc 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_data);
+	}
+}
+/* }}} */
+
+/* {{{ 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)
+{
+	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);
+
+	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)
+{
+	if (cbarg) {
+		if (cbarg->data) {
+			zval_ptr_dtor(&cbarg->data);
+		}
+
+		PHP_EVENT_FREE_FCALL_INFO(cbarg->fci, cbarg->fcc);
+		efree(cbarg);
+	}
+}
+/* }}} */
+
+/* {{{ php_event_dtor */
+static void php_event_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+	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);
+	}
+	event_free(e->event);
+	efree(e);
+}
+/* }}} */
+
+/* {{{ php_event_base_dtor */
+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;
+
+	event_base_free(base);
+}
+/* }}} */
+
+/* {{{ php_event_config_dtor */
+static void php_event_config_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+	struct event_config *cfg = (struct event_config *) rsrc->ptr;
+
+	event_config_free(cfg);
+}
+/* }}} */
+
+/* Private functions }}} */
+
+#define PHP_EVENT_REG_CONST_LONG(name, real_name) \
+    REGISTER_LONG_CONSTANT(#name, real_name, CONST_CS | CONST_PERSISTENT);
+
+/* {{{ PHP_MINIT_FUNCTION */
+PHP_MINIT_FUNCTION(event)
+{
+	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);
+
+	/* Loop flags */
+	PHP_EVENT_REG_CONST_LONG(EVENT_LOOP_ONCE,     EVLOOP_ONCE);
+	PHP_EVENT_REG_CONST_LONG(EVENT_LOOP_NONBLOCK, EVLOOP_NONBLOCK);
+
+	/* Event flags */
+	PHP_EVENT_REG_CONST_LONG(EVENT_ET,      EV_ET);
+	PHP_EVENT_REG_CONST_LONG(EVENT_PERSIST, EV_PERSIST);
+	PHP_EVENT_REG_CONST_LONG(EVENT_READ,    EV_READ);
+	PHP_EVENT_REG_CONST_LONG(EVENT_SIGNAL,  EV_SIGNAL);
+	PHP_EVENT_REG_CONST_LONG(EVENT_TIMEOUT, EV_TIMEOUT);
+
+	/* Features of event_base usually passed to event_config_require_features */
+	PHP_EVENT_REG_CONST_LONG(EVENT_FEATURE_ET,  EV_FEATURE_ET);
+	PHP_EVENT_REG_CONST_LONG(EVENT_FEATURE_O1,  EV_FEATURE_O1);
+	PHP_EVENT_REG_CONST_LONG(EVENT_FEATURE_FDS, EV_FEATURE_FDS);
+
+	/* Run-time flags of event base usually passed to event_config_set_flag */
+	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_NOLOCK,               EVENT_BASE_FLAG_NOLOCK);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_STARTUP_IOCP,         EVENT_BASE_FLAG_STARTUP_IOCP);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_NO_CACHE_TIME,        EVENT_BASE_FLAG_NO_CACHE_TIME);
+	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);
+#ifdef EVENT_BASE_FLAG_IGNORE_ENV
+	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_IGNORE_ENV,           EVENT_BASE_FLAG_IGNORE_ENV);
+#endif
+#ifdef EVENT_BASE_FLAG_PRECISE_TIMER
+	PHP_EVENT_REG_CONST_LONG(EVENT_BASE_FLAG_PRECISE_TIMER,        EVENT_BASE_FLAG_PRECISE_TIMER);
+#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);
+	event_set_log_callback(log_cb);
+
+	return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION */
+PHP_MSHUTDOWN_FUNCTION(event)
+{
+#if LIBEVENT_VERSION_NUMBER >= 0x02010000
+	/* libevent_global_shutdown is available since libevent 2.1.0-alpha.
+	 *
+	 * Make sure that libevent has released all internal library-global data
+	 * structures. Don't call any of libevent functions below! */
+	libevent_global_shutdown();
+#endif
+
+	return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION */
+PHP_MINFO_FUNCTION(event)
+{
+	php_info_print_table_start();
+	php_info_print_table_header(2, "event support", "enabled");
+#ifdef PHP_EVENT_DEBUG 
+	php_info_print_table_row(2, "Debug support", "enabled");
+#else
+	php_info_print_table_row(2, "Debug support", "disabled");
+#endif
+	php_info_print_table_row(2, "Version", PHP_EVENT_VERSION);
+	php_info_print_table_end();
+}
+/* }}} */
+
+/* {{{ 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 */
+PHP_FUNCTION(evtimer_new)
+{
+	zval                  *zbase;
+	php_event_base_t      *base;
+	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, "rf|z",
+				&zbase, &fci, &fcc, &arg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	e = emalloc(sizeof(php_event_t));
+	memset(e, 0, sizeof(php_event_t));
+
+	event = evtimer_new(base, timer_cb, (void *) e);
+	if (!event) {
+		efree(e);
+		php_error_docref(NULL TSRMLS_CC, E_ERROR, "evtimer_new failed");
+		RETURN_FALSE;
+	}
+
+	e->event = event;
+	e->arg   = event_cb_arg_new(arg, &fci, &fcc TSRMLS_CC);
+
+	e->stream_id = -1; /* stdin fd = 0 */
+
+	ZEND_REGISTER_RESOURCE(return_value, e, le_event);
+}
+/* }}} */
+
+/* {{{ proto bool evtimer_set(resource event, resource base, callable cb[, zval arg = NULL]);
+ * Re-configures timer event */
+PHP_FUNCTION(evtimer_set)
+{
+	zval                  *zbase;
+	php_event_base_t      *base;
+	zval                  *zevent;
+	php_event_t           *e;
+	zend_fcall_info        fci    = empty_fcall_info;
+	zend_fcall_info_cache  fcc    = empty_fcall_info_cache;
+	zval                  *arg    = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrf|z!",
+				&zevent, &zbase, &fci, &fcc, &arg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_EVENT(e, zevent);
+
+	if (timer_is_pending(e->event)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't modify pending timer");
+		RETURN_FALSE;
+		return;
+	}
+
+	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);
+		}
+
+		PHP_EVENT_COPY_FCALL_INFO(e->arg->fci, e->arg->fcc, &fci, &fcc);
+	}
+
+	if (arg) {
+		if (e->arg->data) {
+			zval_ptr_dtor(&e->arg->data);
+		}
+		e->arg->data = arg;
+		Z_ADDREF_P(arg);
+	}
+
+	e->stream_id = -1; /* stdin fd = 0 */
+
+    if (evtimer_assign(e->event, base, timer_cb, (void *) e)) {
+    	RETURN_FALSE;
+    }
+    RETVAL_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto bool event_set(resource event, resource base, mixed fd,[ int what = NULL[, callable cb = NULL[, zval arg = NULL]]]);
+ * Re-configures event */
+PHP_FUNCTION(event_set)
+{
+	zval                   *zbase;
+	php_event_base_t       *base;
+	zval                   *zevent;
+	php_event_t            *e;
+	zval                  **ppzfd   = NULL;
+	evutil_socket_t         fd;
+	long                    what    = -1;
+	zend_fcall_info         fci     = empty_fcall_info;
+	zend_fcall_info_cache   fcc     = empty_fcall_info_cache;
+	zval                   *arg     = NULL;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrZ!|lfz!",
+				&zevent, &zbase, &ppzfd, &what, &fci, &fcc, &arg) == FAILURE) {
+		return;
+	}
+
+	if (what != -1) {
+		if (what & ~(EV_TIMEOUT | EV_READ | EV_WRITE | EV_SIGNAL | EV_PERSIST | EV_ET)) {
+			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid events 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_EVENT(e, zevent);
+
+	if (is_pending(e->event)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't modify pending event");
+		RETURN_FALSE;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	/* TODO: check if a signum bound to different event bases */
+
+	if (ppzfd) {
+		if (what != -1 && what & EV_SIGNAL) {
+			e->stream_id = -1; /* stdin fd = 0 */
+		} else {
+			if (e->stream_id != Z_LVAL_PP(ppzfd)) {
+				zend_list_delete(e->stream_id);
+				/* lval of ppzfd is the resource ID */
+				e->stream_id = Z_LVAL_PP(ppzfd);
+				zend_list_addref(Z_LVAL_PP(ppzfd));
+			}
+		}
+	}
+
+	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);
+	}
+
+	if (arg) {
+		if (e->arg->data) {
+			zval_ptr_dtor(&e->arg->data);
+		}
+		e->arg->data = arg;
+		Z_ADDREF_P(arg);
+	}
+
+    event_get_assignment(e->event, &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)) {
+    	RETURN_FALSE;
+    }
+
+    RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array event_get_supported_methods(void);
+ * Returns array with of the names of the methods supported in this version of Libevent */
+PHP_FUNCTION(event_get_supported_methods)
+{
+	int i;
+	const char **methods;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	methods = event_get_supported_methods();
+
+	if (methods == NULL) {
+		RETURN_FALSE;
+	}
+
+	array_init(return_value);
+
+	for (i = 0; methods[i] != NULL; ++i) {
+    	add_next_index_string(return_value, methods[i], 1);
+	}
+}
+
+/* }}} */
+
+/* {{{ proto bool event_add(resource event[, double timeout]);
+ * Make event pending. */
+PHP_FUNCTION(event_add)
+{
+	zval        *zevent;
+	php_event_t *e;
+	double       timeout = -1;
+	int          res;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|d",
+				&zevent, &timeout) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_EVENT(e, zevent);
+
+	if (timeout == -1) {
+		res = event_add(e->event, NULL);
+	} else {
+		struct timeval tv;
+		PHP_EVENT_TIMEVAL_SET(tv, timeout);
+
+		res = event_add(e->event, &tv);
+	}
+
+	if (res) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed adding event");
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_del(resource event);
+ * Remove an event from the set of monitored events. */
+PHP_FUNCTION(event_del)
+{
+	zval        *zevent;
+	php_event_t *e;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zevent) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_EVENT(e, zevent);
+
+	if (event_del(e->event)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed deletting event");
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+#if LIBEVENT_VERSION_NUMBER >= 0x02010200
+/* {{{ proto bool event_remove_timer(resource event);
+ * Remove a pending event’s timeout completely without deleting its IO or signal components.
+ * Available since libevent 2.1.2-alpha. */
+PHP_FUNCTION(event_remove_timer)
+{
+	zval        *zevent;
+	php_event_t *e;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zevent) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_EVENT(e, zevent);
+
+	if (event_remove_timer(e->event)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed deletting event");
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+#endif
+
+/* {{{ proto bool event_priority_set(resource event, int priority);
+ * Make event pending. */
+PHP_FUNCTION(event_priority_set)
+{
+	zval        *zevent;
+	php_event_t *e;
+	long         priority;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl",
+				&zevent, &priority) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_EVENT(e, zevent);
+
+	if (event_priority_set(e->event, priority)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to set event priority: %ld", priority);
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+
+
+/* {{{ proto resource event_base_new(void);
+ * Returns resource representing new event base */
+PHP_FUNCTION(event_base_new)
+{
+	php_event_base_t *base;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	base = event_base_new();
+
+	if (base) {
+		ZEND_REGISTER_RESOURCE(return_value, base, le_event_base);
+	} else {
+		RETVAL_FALSE;
+	}
+}
+/* }}} */
+
+/* {{{ proto resource event_base_new_with_config(resource config);
+ * Creates new event base taking the specified configuration under consideration. */
+PHP_FUNCTION(event_base_new_with_config)
+{
+	php_event_base_t   *base;
+	php_event_config_t *cfg;
+	zval               *zcfg;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zcfg) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_CONFIG(cfg, zcfg);
+
+	base = event_base_new_with_config(cfg);
+
+	if (base) {
+		ZEND_REGISTER_RESOURCE(return_value, base, le_event_base);
+	} else {
+		RETVAL_FALSE;
+	}
+}
+/* }}} */
+
+/* {{{ proto string event_base_get_method(resource base);
+ * Returns event method in use. */
+PHP_FUNCTION(event_base_get_method)
+{
+	zval             *zbase;
+	php_event_base_t *base;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zbase) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	RETVAL_STRING(event_base_get_method(base), 1);
+}
+/* }}} */
+
+/* {{{ proto int event_base_get_features(resource base);
+ * Returns bitmask of features supported. See EVENT_FEATURE_* constants. */
+PHP_FUNCTION(event_base_get_features)
+{
+	zval             *zbase;
+	php_event_base_t *base;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zbase) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase)
+
+	RETVAL_LONG(event_base_get_features(base));
+}
+/* }}} */
+
+/* {{{ proto bool event_base_priority_init(resource base, int n_priorities);
+ * Sets number of priorities per event base. Returns &true; on success, otherwise &false; */
+PHP_FUNCTION(event_base_priority_init)
+{
+	zval             *zbase;
+	long              n_priorities;
+	php_event_base_t *base;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl",
+				&zbase, &n_priorities) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	if (event_base_priority_init(base, n_priorities)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_base_loop(resource base[, int flags]);
+ * Wait for events to become active, and run their callbacks. */
+PHP_FUNCTION(event_base_loop)
+{
+	zval             *zbase;
+	long              flags = -1;
+	php_event_base_t *base;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l",
+				&zbase, &flags) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	/* Call event_base_dispatch when flags omitted. */
+	if (flags == -1) {
+		if (event_base_dispatch(base) == -1) {
+			RETURN_FALSE;
+		}
+	} else if (event_base_loop(base, flags) == -1) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_base_dispatch(resource base);
+ * Wait for events to become active, and run their callbacks.
+ * The same as event_base_loop with no flags set*/
+PHP_FUNCTION(event_base_dispatch)
+{
+	zval             *zbase;
+	php_event_base_t *base;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zbase) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	if (event_base_dispatch(base) == -1) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_base_loopexit(resource base[, double timeout = 0.0]);
+ * Tells event_base to stop optionally after given number of seconds. */
+PHP_FUNCTION(event_base_loopexit)
+{
+	zval             *zbase;
+	php_event_base_t *base;
+	double            timeout = -1;
+	int               res;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|d",
+				&zbase, &timeout) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	if (timeout == -1) {
+		res = event_base_loopexit(base, NULL);
+	} else {
+		struct timeval tv;
+		PHP_EVENT_TIMEVAL_SET(tv, timeout);
+
+		res = event_base_loopexit(base, &tv);
+	}
+
+	if (res) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_base_loopbreak(resource base);
+ * Tells event_base to stop. */
+PHP_FUNCTION(event_base_loopbreak)
+{
+	zval             *zbase;
+	php_event_base_t *base;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+				&zbase) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	if (event_base_loopbreak(base)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_base_set(resource base, resource event);
+ * Associate event base with an event. */
+PHP_FUNCTION(event_base_set)
+{
+	zval             *zbase;
+	php_event_base_t *base;
+	zval             *zevent;
+	php_event_t      *e;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rr",
+				&zbase, &zevent) == FAILURE) {
+		return;
+	}
+
+	PHP_EVENT_FETCH_EVENT(e, zevent);
+
+	if (is_pending(e->event)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't modify pending event");
+		RETURN_FALSE;
+	}
+
+	PHP_EVENT_FETCH_BASE(base, zbase);
+
+	if (event_base_set(base, e->event)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto resource event_config_new(void);
+ * On success returns a valid resource representing an event configuration
+ * which can be passed to <function>event_base_new_with_config</function>. Otherwise returns &false;. */
+PHP_FUNCTION(event_config_new)
+{
+	php_event_config_t *cfg;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+
+	cfg = event_config_new();
+
+	if (cfg) {
+		ZEND_REGISTER_RESOURCE(return_value, cfg, le_event_config);
+	} else {
+		RETURN_FALSE;
+	}
+}
+/* }}} */
+
+/* {{{ proto bool event_config_avoid_method(resource cfg, string method);
+ * Tells libevent to avoid specific event method.
+ * See http://www.wangafu.net/~nickm/libevent-book/Ref2_eventbase.html#_creating_an_event_base
+ * Returns &true; on success, otherwise &false;.*/
+PHP_FUNCTION(event_config_avoid_method)
+{
+	zval               *zcfg;
+	char               *method;
+	int                 method_len;
+	php_event_config_t *cfg;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs",
+				&zcfg, &method, &method_len) == FAILURE) {
+		return;
+	}
+
+	ZEND_FETCH_RESOURCE(cfg, php_event_config_t *, &zcfg, -1,
+		PHP_EVENT_CONFIG_RES_NAME, le_event_config);
+
+	if (event_config_avoid_method(cfg, method)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool event_config_require_features(resource cfg, int feature);
+ * Enters a required event method feature that the application demands. */
+PHP_FUNCTION(event_config_require_features)
+{
+	zval               *zcfg;
+	long                feature;
+	php_event_config_t *cfg;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl",
+				&zcfg, &feature) == FAILURE) {
+		return;
+	}
+
+	ZEND_FETCH_RESOURCE(cfg, php_event_config_t *, &zcfg, -1,
+		PHP_EVENT_CONFIG_RES_NAME, le_event_config);
+
+	if (event_config_require_features(cfg, feature)) {
+		RETURN_FALSE;
+	}
+	RETVAL_TRUE;
+}
+/* }}} */
+
+#if LIBEVENT_VERSION_NUMBER >= 0x02010000
+/* {{{ proto void event_config_set_max_dispatch_interval(resource cfg, int max_interval, int max_callbacks, int min_priority);
+ * Prevents priority inversion by limiting how many low-priority event
+ * callbacks can be invoked before checking for more high-priority events.
+ * Available since libevent 2.1.0-alpha. */
+PHP_FUNCTION(event_config_set_max_dispatch_interval)
+{
+	zval                  *zcfg;
+	php_event_timestamp_t  max_interval;
+	long                   max_callbacks;
+	long                   min_priority;
+	php_event_config_t    *cfg;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rdll",
+				&zcfg, &max_interval, &max_callbacks, min_priority) == FAILURE) {
+		return;
+	}
+
+	ZEND_FETCH_RESOURCE(cfg, php_event_config_t *, &zcfg, -1,
+		PHP_EVENT_CONFIG_RES_NAME, le_event_config);
+
+	if (max_interval > 0) {
+		struct timeval tv;
+		PHP_EVENT_TIMEVAL_SET(tv, max_interval);
+
+		event_config_set_max_dispatch_interval(cfg, &tv, max_callbacks, min_priority);
+	} else {
+		event_config_set_max_dispatch_interval(cfg, NULL, max_callbacks, min_priority);
+	}
+}
+/* }}} */
+#endif
+
+
+/* API functions END }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#include "fe.h"
+
+/* {{{ ARGINFO */
+ZEND_BEGIN_ARG_INFO(arginfo_event__void, 0)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_base_config_1, 0, 0, 1)
+	ZEND_ARG_INFO(0, config)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_base_1, 0, 0, 1)
+	ZEND_ARG_INFO(0, base)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_base_priority_init, 0, 0, 2)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, n_priorities)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_base_loop, 0, 0, 1)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, flags)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_base_loopexit, 0, 0, 1)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_1, 0, 0, 1)
+	ZEND_ARG_INFO(0, event)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_new, 0, 0, 4)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, fd)
+	ZEND_ARG_INFO(0, what)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_set, 0, 0, 3)
+	ZEND_ARG_INFO(0, event)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, fd)
+	ZEND_ARG_INFO(0, what)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_add, 0, 0, 1)
+	ZEND_ARG_INFO(0, event)
+	ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_config_avoid_method, 0, 0, 2)
+	ZEND_ARG_INFO(0, cfg)
+	ZEND_ARG_INFO(0, method)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_config_require_features, 0, 0, 2)
+	ZEND_ARG_INFO(0, cfg)
+	ZEND_ARG_INFO(0, feature)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_priority_set, 0, 0, 2)
+	ZEND_ARG_INFO(0, event)
+	ZEND_ARG_INFO(0, priority)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evtimer_new, 0, 0, 2)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_evtimer_set, 0, 0, 3)
+	ZEND_ARG_INFO(0, event)
+	ZEND_ARG_INFO(0, base)
+	ZEND_ARG_INFO(0, cb)
+	ZEND_ARG_INFO(0, arg)
+ZEND_END_ARG_INFO();
+
+
+#if LIBEVENT_VERSION_NUMBER >= 0x02010000
+ZEND_BEGIN_ARG_INFO_EX(arginfo_event_config_set_max_dispatch_interval, 0, 0, 4)
+	ZEND_ARG_INFO(0, cfg)
+	ZEND_ARG_INFO(0, max_interval)
+	ZEND_ARG_INFO(0, max_callbacks)
+	ZEND_ARG_INFO(0, min_priority)
+ZEND_END_ARG_INFO();
+#endif
+
+/* ARGINFO }}} */
+
+
+/* {{{ event_functions[] */
+const zend_function_entry event_functions[] = {
+	PHP_FE(event_new, arginfo_event_new)
+	PHP_FE(event_set, arginfo_event_set)
+	PHP_FE(event_add, arginfo_event_add)
+	PHP_FE(event_del, arginfo_event_1)
+#if LIBEVENT_VERSION_NUMBER >= 0x02010200
+	PHP_FE(event_remove_timer, arginfo_event_1)
+#endif
+	PHP_FE(event_priority_set, arginfo_event_priority_set)
+	PHP_FE(event_get_supported_methods, arginfo_event__void)
+
+	PHP_FE(evtimer_new, arginfo_evtimer_new)
+	PHP_FE(evtimer_set, arginfo_evtimer_set)
+
+	PHP_FE(event_base_new, arginfo_event__void)
+	PHP_FE(event_base_new_with_config, arginfo_event_base_config_1)
+	PHP_FE(event_base_get_method, arginfo_event_base_1)
+	PHP_FE(event_base_get_features, arginfo_event_base_1)
+	PHP_FE(event_base_priority_init, arginfo_event_base_priority_init)
+	PHP_FE(event_base_loop, arginfo_event_base_loop)
+	PHP_FE(event_base_dispatch, arginfo_event_base_1)
+	PHP_FE(event_base_loopexit, arginfo_event_base_loopexit)
+	PHP_FE(event_base_loopbreak, arginfo_event_base_1)
+
+	PHP_FE(event_config_new, arginfo_event__void)
+	PHP_FE(event_config_avoid_method, arginfo_event_config_avoid_method)
+	PHP_FE(event_config_require_features, arginfo_event_config_require_features)
+#if LIBEVENT_VERSION_NUMBER >= 0x02010000
+	PHP_FE(event_config_set_max_dispatch_interval, arginfo_event_config_set_max_dispatch_interval)
+#endif
+
+	PHP_FE_END
+};
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: fdm=marker
+ * vim: noet sts=4 sw=4 ts=4
+ */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2011 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+#ifndef PHP_EVENT_FE_H
+#define PHP_EVENT_FE_H
+
+#include "common.h"
+
+PHP_FUNCTION(event_new);
+PHP_FUNCTION(event_set);
+PHP_FUNCTION(event_add);
+PHP_FUNCTION(event_del);
+PHP_FUNCTION(event_remove_timer);
+PHP_FUNCTION(event_priority_set);
+
+PHP_FUNCTION(evtimer_new);
+PHP_FUNCTION(evtimer_set);
+
+PHP_FUNCTION(event_base_new);
+PHP_FUNCTION(event_base_new_with_config);
+PHP_FUNCTION(event_base_get_method);
+PHP_FUNCTION(event_base_get_features);
+PHP_FUNCTION(event_base_priority_init);
+PHP_FUNCTION(event_base_loop);
+PHP_FUNCTION(event_base_loopexit);
+PHP_FUNCTION(event_base_loopbreak);
+PHP_FUNCTION(event_base_dispatch);
+
+PHP_FUNCTION(event_get_supported_methods);
+
+PHP_FUNCTION(event_config_new);
+PHP_FUNCTION(event_config_avoid_method);
+PHP_FUNCTION(event_config_require_features);
+#if LIBEVENT_VERSION_NUMBER >= 0x02010000
+PHP_FUNCTION(event_config_set_max_dispatch_interval);
+#endif
+
+#endif /* PHP_EVENT_FE_H */
+
+/* 
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * vim600: fdm=marker
+ * vim: noet sts=4 sw=4 ts=4
+ */
+<?xml version="1.0"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+  <name>event</name>
+  <channel>pecl.php.net</channel>
+  <summary>Provides interface to libevent library</summary>
+  <description>
+  event provides interface to libevent library.
+  </description>
+  <lead>
+    <name>Ruslan Osmanov</name>
+    <user>osmanov</user>
+    <email>osmanov@php.net</email>
+    <active>yes</active>
+  </lead>
+  <date>2013-01-06</date>
+  <!--{{{ Current version -->
+  <version>
+    <release>0.1.0</release>
+    <api>0.1.0</api>
+  </version>
+  <stability>
+    <release>stable</release>
+    <api>stable</api>
+  </stability>
+  <license uri="http://www.php.net/license">PHP</license>
+  <notes><![CDATA[
+  Initial release
+  ]]></notes>
+  <!--}}}-->
+  <!--{{{ Contents -->
+  <contents>
+    <dir name="/">
+      <file role="doc" name="CREDITS"/>
+      <file role="doc" name="EXPERIMENTAL"/>
+      <file role="src" name="common.h"/>
+      <file role="src" name="config.m4"/>
+      <file role="src" name="event.c"/>
+      <file role="src" name="fe.c"/>
+      <file role="src" name="fe.h"/>
+      <file role="src" name="php_event.h"/>
+      <file role="src" name="structs.h"/>
+      <dir name="tests">
+        <file role="src" name="01-load.phpt"/>
+        <file role="src" name="02-features.phpt"/>
+        <file role="src" name="03-event-del.phpt"/>
+      </dir>
+    </dir>
+  </contents>
+  <!--}}}-->
+  <dependencies>
+    <required>
+      <php>
+        <min>5.4.0</min>
+      </php>
+      <pearinstaller>
+        <min>1.4.0a1</min>
+      </pearinstaller>
+    </required>
+  </dependencies>
+  <providesextension>event</providesextension>
+  <extsrcrelease>
+    <configureoption default="no" name="enable-event-debug" prompt="Enable internal debugging in event"/>
+    <configureoption default="yes" name="with-event-extra" prompt="Include libevent protocol-specific functionality support including HTTP, DNS, and RPC"/>
+  </extsrcrelease>
+  <!--{{{ changelog-->
+  <changelog>
+    <!--{{{ Current version -->
+    <version>
+      <release>0.1.0</release>
+      <api>0.1.0</api>
+    </version>
+    <stability>
+      <release>stable</release>
+      <api>stable</api>
+    </stability>
+    <license uri="http://www.php.net/license">PHP</license>
+    <notes><![CDATA[
+  Initial release
+  ]]></notes>
+    <!--}}}-->
+  </changelog>
+  <!--}}}-->
+</package>
+<!-- vim: set et sts=2 ts=2 sw=2 fdm=marker: -->
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_EVENT_H
+#define PHP_EVENT_H
+
+#define PHP_EVENT_VERSION "0.1.0"
+
+#define PHP_EVENT_RES_NAME "Event"
+#define PHP_EVENT_BASE_RES_NAME "Event Base"
+#define PHP_EVENT_CONFIG_RES_NAME "Event Config"
+
+extern zend_module_entry event_module_entry;
+#define phpext_event_ptr &event_module_entry
+
+#include "common.h"
+
+PHP_MINIT_FUNCTION(event);
+PHP_MSHUTDOWN_FUNCTION(event);
+PHP_RINIT_FUNCTION(event);
+PHP_RSHUTDOWN_FUNCTION(event);
+PHP_MINFO_FUNCTION(event);
+
+#if 0
+ZEND_BEGIN_MODULE_GLOBALS(event)
+ZEND_END_MODULE_GLOBALS(event)
+#endif
+
+#ifdef ZTS
+# define EVENT_G(v) TSRMG(event_globals_id, zend_event_globals *, v)
+# define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx
+# define TSRMLS_SET_CTX(ctx)        ctx = (void ***) tsrm_ls
+#else
+# define EVENT_G(v) (event_globals.v)
+# define TSRMLS_FETCH_FROM_CTX(ctx)
+# define TSRMLS_SET_CTX(ctx)
+#endif
+
+#ifdef PHP_EVENT_DEBUG
+# define PHP_EVENT_ASSERT(x) assert(x)
+#else
+# define PHP_EVENT_ASSERT(x)
+#endif
+
+#if PHP_VERSION_ID >= 50300
+# define PHP_EVENT_FCI_ADDREF(pfci)       \
+{                                         \
+    Z_ADDREF_P(pfci->function_name);      \
+    if (pfci->object_ptr) {               \
+        Z_ADDREF_P(pfci->object_ptr);     \
+    }                                     \
+}
+# define PHP_EVENT_FCI_DELREF(pfci)       \
+{                                         \
+    zval_ptr_dtor(&pfci->function_name);  \
+    if (pfci->object_ptr) {               \
+        zval_ptr_dtor(&pfci->object_ptr); \
+    }                                     \
+}
+#else
+# define PHP_EVENT_FCI_ADDREF(pfci) Z_ADDREF_P(pfci_dst->function_name)
+# define PHP_EVENT_FCI_DELREF(pfci) zval_ptr_dtor(&pfci->function_name)
+#endif
+
+#define PHP_EVENT_COPY_FCALL_INFO(pfci_dst, pfcc_dst, pfci, pfcc)                                \
+    if (ZEND_FCI_INITIALIZED(*pfci)) {                                                           \
+        pfci_dst = (zend_fcall_info *) safe_emalloc(1, sizeof(zend_fcall_info), 0);              \
+        pfcc_dst = (zend_fcall_info_cache *) safe_emalloc(1, sizeof(zend_fcall_info_cache), 0);  \
+                                                                                                 \
+        memcpy(pfci_dst, pfci, sizeof(zend_fcall_info));                                         \
+        memcpy(pfcc_dst, pfcc, sizeof(zend_fcall_info_cache));                                   \
+                                                                                                 \
+        PHP_EVENT_FCI_ADDREF(pfci_dst);                                                          \
+    } else {                                                                                     \
+        pfci_dst = NULL;                                                                         \
+        pfcc_dst = NULL;                                                                         \
+    }                                                                                            \
+
+#define PHP_EVENT_FREE_FCALL_INFO(pfci, pfcc)                                                    \
+    if (pfci && pfcc) {                                                                          \
+        efree(pfcc);                                                                             \
+                                                                                                 \
+        if (ZEND_FCI_INITIALIZED(*pfci)) {                                                       \
+            PHP_EVENT_FCI_DELREF(pfci);                                                          \
+        }                                                                                        \
+        efree(pfci);                                                                             \
+    }                                                                                            \
+
+
+#endif	/* PHP_EVENT_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2013 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 3.01 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available through the world-wide-web at the following url:           |
+   | http://www.php.net/license/3_01.txt                                  |
+   | If you did not receive a copy of the PHP license and are unable to   |
+   | obtain it through the world-wide-web, please send a note to          |
+   | license@php.net so we can mail you a copy immediately.               |
+   +----------------------------------------------------------------------+
+   | Author: Ruslan Osmanov <osmanov@php.net>                             |
+   +----------------------------------------------------------------------+
+*/
+
+#ifndef PHP_EVENT_STRUCTS_H
+#define PHP_EVENT_STRUCTS_H
+
+/* Thread context. With it we are getting rid of need 
+ * to call the heavy TSRMLS_FETCH() */
+#ifdef ZTS
+# define PHP_EVENT_COMMON_THREAD_CTX void ***thread_ctx
+#else
+# define PHP_EVENT_COMMON_THREAD_CTX
+#endif
+
+typedef struct {
+	zval                  *data;    /* User custom data                            */
+	zend_fcall_info       *fci;     /* fci and fcc represent userspace callback    */
+	zend_fcall_info_cache *fcc;
+	PHP_EVENT_COMMON_THREAD_CTX;
+} php_event_cb_arg_t;
+
+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;
+
+typedef struct event_base php_event_base_t;
+typedef struct event_config php_event_config_t;
+typedef double php_event_timestamp_t;
+
+#endif	/* PHP_EVENT_STRUCTS_H */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+<?php
+/* {{{ Config & supported stuff */
+echo "Supported methods:\n";
+foreach (event_get_supported_methods() as $m) {
+	echo $m, PHP_EOL;
+}
+
+// Avoiding "select" method
+$cfg = event_config_new();
+if (event_config_avoid_method($cfg, "select")) {
+	echo "`select' method avoided\n";
+}
+
+// Create event_base associated with the config
+$base = event_base_new_with_config($cfg);
+echo "Event method used: ", event_base_get_method($base), PHP_EOL;
+
+echo "Features:\n";
+$features = event_base_get_features($base);
+($features & EVENT_FEATURE_ET) and print("ET - edge-triggered IO\n");
+($features & EVENT_FEATURE_O1) and print("O1 - O(1) operation for adding/deletting events\n");
+($features & EVENT_FEATURE_FDS) and print("FDS - arbitrary file descriptor types, and not just sockets\n");
+
+// Require FDS feature
+if (event_config_require_features($cfg, EVENT_FEATURE_FDS)) {
+	echo "FDS feature is now requried\n";
+
+	$base = event_base_new_with_config($cfg);
+	(event_base_get_features($base) & EVENT_FEATURE_FDS)
+		and print("FDS - arbitrary file descriptor types, and not just sockets\n");
+}
+/* }}} */
+
+/* {{{ Base */
+$base = event_base_new();
+$event = event_new($base, STDIN, EVENT_READ | EVENT_PERSIST, function ($fd, $events, $arg) {
+	static $max_iterations = 0;
+
+    if (++$max_iterations >= 5) {
+		/* exit after 5 iterations with timeout of 2.33 seconds */
+		echo "Stopping...\n";
+        event_base_loopexit($arg[0], 2.33);
+    }
+
+    echo fgets($fd);
+}, array (&$base));
+
+event_add($event);
+event_base_loop($base);
+/* Base }}} */
+?>
+

File tests/01-load.phpt

+--TEST--
+Check for event presence
+--SKIPIF--
+<?php if (!extension_loaded("event")) print "skip"; ?>
+--FILE--
+<?php 
+echo "event extension is available";
+/*
+	you can add regression tests for your extension here
+
+  the output of your test code has to be equal to the
+  text in the --EXPECT-- section below for the tests
+  to pass, differences between the output and the
+  expected text are interpreted as failure
+
+	see php5/README.TESTING for further information on
+  writing regression tests
+*/
+?>
+--EXPECT--
+event extension is available

File tests/02-features.phpt

+--TEST--
+Check for event configuration features 
+--FILE--
+<?php 
+$cfg = event_config_new();
+
+if (event_config_require_features($cfg, EVENT_FEATURE_FDS)) {
+	$base = event_base_new_with_config($cfg);
+
+	if (event_base_get_features($base) & EVENT_FEATURE_FDS) {
+		echo "FDS\n";
+	}
+}
+
+if (event_config_require_features($cfg, EVENT_FEATURE_ET)) {
+	$base = event_base_new_with_config($cfg);
+
+	if (event_base_get_features($base) & EVENT_FEATURE_ET) {
+		echo "ET\n";
+	}
+}
+
+if (event_config_require_features($cfg, EVENT_FEATURE_O1)) {
+	$base = event_base_new_with_config($cfg);
+
+	if (event_base_get_features($base) & EVENT_FEATURE_O1) {
+		echo "O1\n";
+	}
+}
+
+?>
+--EXPECT--
+FDS
+ET
+O1

File tests/03-event-del.phpt

+--TEST--
+Check for event_add and event_del
+--FILE--
+<?php 
+$base = event_base_new();
+
+$e1 = evtimer_new($base, function () { echo "not ok 1\n"; });
+event_add($e1, 0.1);
+
+$e2 = evtimer_new($base, function () { echo "ok 1\n"; });
+event_add($e2, 0.2);
+
+event_del($e1);
+event_base_loop($base, EVENT_LOOP_ONCE);
+
+evtimer_set($e1, $base, function() { echo "ok 2\n"; });
+event_add($e1, 0.3);
+event_base_loop($base, EVENT_LOOP_ONCE);
+
+event_del($e1);
+event_del($e2);
+event_base_loop($base);
+
+?>
+--EXPECT--
+ok 1
+ok 2