Ruslan Osmanov avatar Ruslan Osmanov committed e7089dc

Add: EvLoop::once
Add: sockets support

Comments (0)

Files changed (11)

 #include <ext/standard/php_string.h>
 #include <Zend/zend_extensions.h>
 
+#include <php_network.h>
+#include <php_streams.h>
+#ifdef PHP_EV_USE_SOCKETS
+# include <ext/sockets/php_sockets.h>
+#endif
+
 #ifdef ZTS
 # include "TSRM.h"
 #endif
 PHP_ARG_ENABLE(ev-libevent-api, for libevent compatibility API support,
 [  --enable-ev-libevent-api       Enable libevent compatibility API support], yes, no)
 
+PHP_ARG_ENABLE(ev-sockets, for sockets support,
+[  --enable-ev-sockets     Enable sockets support in Ev], yes, no)
+
 if test "$PHP_EV" != "no"; then
   export OLD_CPPFLAGS="$CPPFLAGS"
   export CPPFLAGS="$CPPFLAGS $INCLUDES -DHAVE_EV"
     AC_DEFINE(PHP_EV_DEBUG, 1, [Enable ev debug support])
   fi
 
+  if test "$PHP_EV_SOCKETS" != "no"; then
+    PHP_ADD_EXTENSION_DEP(ev, sockets, true)
+    AC_DEFINE([PHP_EV_USE_SOCKETS], 1, [Whether to enable sockets support])
+  fi
+
   AC_DEFINE(EV_H, "embed.h", [Wrapper for libev/ev.h])
   AC_DEFINE(HAVE_EV, 1, [ ])
   m4_include([libev/libev.m4])
 
-  ev_src="libev/ev.c ev.c watcher.c fe.c pe.c"
+  ev_src="libev/ev.c util.c ev.c watcher.c fe.c pe.c"
   PHP_NEW_EXTENSION(ev, $ev_src, $ext_shared,,$CFLAGS)
 fi
 
 
 static zend_object_handlers ev_object_handlers;
 
+static const zend_module_dep ev_deps[] = {
+	ZEND_MOD_OPTIONAL("sockets")
+	{NULL, NULL, NULL}
+};
+
 /* {{{ ev_module_entry */
 zend_module_entry ev_module_entry = {
 #if ZEND_MODULE_API_NO >= 20050922
-	STANDARD_MODULE_HEADER_EX, NULL,
+	STANDARD_MODULE_HEADER_EX,
 	NULL,
+	ev_deps,
 #elif ZEND_MODULE_API_NO >= 20010901
 	STANDARD_MODULE_HEADER,
 #endif
 ZEND_BEGIN_ARG_INFO_EX(arginfo_ev_feed_signal_event, 0, 0, 1)
 	ZEND_ARG_INFO(0, signum)
 ZEND_END_ARG_INFO();
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ev_loop_once, 0, 0, 4)
+	ZEND_ARG_INFO(0, fd)
+	ZEND_ARG_INFO(0, events)
+	ZEND_ARG_INFO(0, timeout)
+	ZEND_ARG_INFO(0, callback)
+	ZEND_ARG_INFO(0, data)
+ZEND_END_ARG_INFO();
 /* EvLoop }}} */
 
 /* {{{ EvWatcher */
 	PHP_ME(EvLoop, break,                arginfo_ev_break,             ZEND_ACC_PUBLIC)
 	PHP_ME(EvLoop, feed_signal,          arginfo_ev_feed_signal,       ZEND_ACC_PUBLIC  | ZEND_ACC_STATIC)
 	PHP_ME(EvLoop, feed_signal_event,    arginfo_ev_feed_signal_event, ZEND_ACC_PUBLIC  | ZEND_ACC_STATIC)
+	PHP_ME(EvLoop, once,                 arginfo_ev_loop_once,         ZEND_ACC_PUBLIC)
 
 	{ NULL, NULL, NULL }
 };
 PHP_METHOD(EvLoop, break);
 PHP_METHOD(EvLoop, feed_signal);
 PHP_METHOD(EvLoop, feed_signal_event);
+PHP_METHOD(EvLoop, once);
 /* }}} */
 
 /* {{{ EvWatcher */
    | Author: Ruslan Osmanov <osmanov@php.net>                             |
    +----------------------------------------------------------------------+
 */
+#include "util.h"
 #include "watcher.h"
 
-/* {{{ proto EvIo::__construct(resource fd, int events, EvLoop loop, callable callback[, mixed data = NULL[, int priority = 0]]) */
+/* {{{ proto EvIo::__construct(mixed fd, int events, EvLoop loop, callable callback[, mixed data = NULL[, int priority = 0]]) */
 PHP_METHOD(EvIo, __construct)
 {
 	zval                  *self;
-	zval                  *z_stream;
+	zval                  *z_fd;
 	php_ev_object         *o_self;
 	php_ev_object         *o_loop;
 	ev_io                 *io_watcher;
+	php_socket_t          fd;
 
 	zval                  *loop;
 	zval                  *data       = NULL;
-	php_stream            *fd_stream;
 	zend_fcall_info        fci        = empty_fcall_info;
 	zend_fcall_info_cache  fcc        = empty_fcall_info_cache;
 	long                   priority   = 0;
 	long                   events;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlOf|z!l",
-				&z_stream, &events, &loop, ev_loop_class_entry_ptr, &fci, &fcc,
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zlOf|z!l",
+				&z_fd, &events, &loop, ev_loop_class_entry_ptr, &fci, &fcc,
 				&data, &priority) == FAILURE) {
 		return;
 	}
 		return;
 	}
 
+	fd = php_ev_zval_to_fd(&z_fd TSRMLS_CC);
+	if (fd < 0) {
+		/* php_ev_zval_to_fd reports errors if necessary */
+		return;
+	}
+
 	self    = getThis();
 	o_self  = (php_ev_object *) zend_object_store_get_object(self TSRMLS_CC);
 	o_loop  = (php_ev_object *) zend_object_store_get_object(loop TSRMLS_CC);
 
 	io_watcher->type = EV_IO;
 	
-	php_stream_from_zval_no_verify(fd_stream, &z_stream);
+#if 0
+	php_stream *fd_stream;
+	php_stream_from_zval_no_verify(fd_stream, &z_fd);
 	if (fd_stream == NULL) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed obtaining fd");
 		return;
 	}
-	ev_io_set(io_watcher, Z_LVAL_P(z_stream), events);
+	ev_io_set(io_watcher, Z_LVAL_P(z_fd), events);
+#endif
+	ev_io_set(io_watcher, fd, events);
 
-	o_self->ptr = (void *)io_watcher;
+	o_self->ptr = (void *) io_watcher;
 }
 /* }}} */
 
 /* {{{ proto void EvIo::set(resource fd, int events) */
 PHP_METHOD(EvIo, set)
 {
-	zval       *z_stream;
-	long        events;
-	php_stream *fd_stream;
-	ev_io      *io_watcher;
+	zval         *z_fd;
+	ev_io        *io_watcher;
+	long          events;
+	php_socket_t  fd;
 
-	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl",
-				&z_stream, &events) == FAILURE) {
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zl",
+				&z_fd, &events) == FAILURE) {
 		return;
 	}
 
 		return;
 	}
 
-	php_stream_from_zval_no_verify(fd_stream, &z_stream);
+#if 0
+	php_stream *fd_stream;
+	php_stream_from_zval_no_verify(fd_stream, &z_fd);
 	if (fd_stream == NULL) {
 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed obtaining fd");
 		return;
 	}
+#endif
+	fd = php_ev_zval_to_fd(&z_fd TSRMLS_CC);
 
 	io_watcher = (ev_io *) PHP_EV_WATCHER_FETCH_FROM_THIS();
 
-	PHP_EV_WATCHER_RESET(ev_io, io_watcher, (io_watcher, Z_LVAL_P(z_stream), events));
+#if 0
+	PHP_EV_WATCHER_RESET(ev_io, io_watcher, (io_watcher, Z_LVAL_P(z_fd), events));
+#endif
+	PHP_EV_WATCHER_RESET(ev_io, io_watcher, (io_watcher, fd, events));
 }
 /* }}} */
 
 }
 /* }}} */
 
+/* {{{ php_ev_once_callback */
+static void php_ev_once_callback(int revents, void *data)
+{
+	zval            **args[2];
+	zval             *key1;
+	zval             *key2;
+	zval             *retval_ptr;
+	zend_fcall_info  *pfci;
+
+	PHP_EV_ASSERT(data);
+	php_ev_once_arg *arg = (php_ev_once_arg *) data;
+
+	TSRMLS_FETCH_FROM_CTX(arg->thread_ctx);
+
+	pfci = arg->fci;
+
+	if (revents & EV_ERROR) {
+		int errorno = errno;
+		if (errorno == EINPROGRESS) {
+			/* Probably non-blocking socket or so */
+#ifdef PHP_EV_DEBUG
+		php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Operation in progress");
+#endif
+			return;
+		}
+		php_error_docref(NULL TSRMLS_CC, E_WARNING,
+				"Got unspecified libev error in revents, errno: %d, err: %s",
+				errorno, strerror(errorno));
+	} else if (ZEND_FCI_INITIALIZED(*pfci)) {
+		/* Setup callback args */
+		key1 = arg->data;
+		args[0] = &key1;
+		zval_add_ref(&key1);
+
+		MAKE_STD_ZVAL(key2);
+		args[1] = &key2;
+		ZVAL_LONG(key2, revents);
+
+		/* Prepare callback */
+		pfci->params         = args;
+		pfci->retval_ptr_ptr = &retval_ptr;
+		pfci->param_count    = 2;
+		pfci->no_separation  = 0;
+
+		if (zend_call_function(pfci, arg->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(&key1);
+		zval_ptr_dtor(&key2);
+	}
+
+	PHP_EV_FREE_FCALL_INFO(arg->fci, arg->fcc);
+
+	if (arg->data) {
+		zval_ptr_dtor(&arg->data);
+	}
+
+	efree(arg);
+}
+/* }}} */
+
+
 /* {{{ proto EvLoop EvLoop::default_loop([int flags = EVLAG_AUTO[, callable callback = NULL[, mixed data = NULL[, double io_collect_interval = 0.[, double timeout_collect_interval = 0.]]]]])
 */
 PHP_METHOD(EvLoop, default_loop)
 }
 /* }}} */
 
+/* {{{ proto void EvLoop::once(mixed fd, int events, double timeout, callable callback[, mixed data = NULL]) */
+PHP_METHOD(EvLoop, once)
+{
+	zval                  *z_fd;
+	php_socket_t           fd;
+	long                   events;
+	double                 timeout;
+	zend_fcall_info        fci     = empty_fcall_info;
+	zend_fcall_info_cache  fcc     = empty_fcall_info_cache;
+	zval                  *data    = NULL;
+	php_ev_once_arg       *arg;
+
+	PHP_EV_LOOP_FETCH_FROM_THIS;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zldf|z!",
+				&z_fd, &events, &timeout, &fci, &fcc, &data) == FAILURE) {
+		return;
+	}
+
+	if (events & ~(EV_READ | EV_WRITE)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid events mask passed");
+		return;
+	}
+
+	fd = php_ev_zval_to_fd(&z_fd TSRMLS_CC);
+	if (fd < 0) {
+		/* php_ev_zval_to_fd reports errors if necessary */
+		return;
+	}
+
+	if (!ZEND_FCI_INITIALIZED(fci)) {
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid callback passed");
+		return;
+	}
+
+	arg = (php_ev_once_arg *) emalloc(sizeof(php_ev_once_arg));
+	memset(arg, 0, sizeof(php_ev_once_arg));
+
+	PHP_EV_COPY_FCALL_INFO(arg->fci, arg->fcc, &fci, &fcc);
+
+	if (data) {
+		Z_ADDREF_P(data);
+	}
+	arg->data = data;
+
+	TSRMLS_SET_CTX(arg->thread_ctx);
+
+	ev_once(EV_A_ fd, events, timeout, php_ev_once_callback, (void *) arg);
+}
+/* }}} */
+
 /*
  * Local variables:
  * tab-width: 4
   <extsrcrelease>
     <configureoption default="no" name="enable-ev-debug" prompt="Enable internal debugging in Ev"/>
     <configureoption default="yes" name="enable-ev-libevent-api" prompt="Enable libevent compatibility API support in Ev"/>
+    <configureoption default="yes" name="enable-ev-sockets" prompt="Enable sockets support in Ev"/>
   </extsrcrelease>
   <changelog>
     <version>
 	php_ev_write_t  write_func;
 } php_ev_prop_handler;
 
+
+/* Type for EvLoop::once(i.e. ev_once) arg.
+ * We've no other way to call userspace callback */
+typedef struct php_ev_once_arg {
+	zval                    *data;         /* user custom data for EvLoop::once() */
+	zend_fcall_info         *fci;          /* fci and fcc serve callbacks         */
+	zend_fcall_info_cache   *fcc;
+	/* Thread context. With it we are getting rid of need
+	 * to call the heavy TSRMLS_FETCH() */
+#ifdef ZTS
+	void                  ***thread_ctx;
+#endif
+} php_ev_once_arg;
+
 #endif /* PHP_EV_TYPES_H */
 
 /*
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2012 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 "php_ev.h"
+#include "util.h"
+
+/* {{{ php_ev_zval_to_fd 
+ * Get numeric file descriptor from PHP stream or Socket resource */
+php_socket_t php_ev_zval_to_fd(zval **ppfd TSRMLS_DC)
+{
+	php_socket_t  file_desc = -1;
+	php_stream   *stream;
+#ifdef PHP_EV_USE_SOCKETS
+	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_cast(stream, PHP_STREAM_AS_FD | PHP_STREAM_CAST_INTERNAL,
+						(void*) &file_desc, 1) != SUCCESS || file_desc < 0) {
+				return -1;
+			}
+		} else {
+			/* PHP socket resource */
+#ifdef PHP_EV_USE_SOCKETS
+			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_EV_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;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4 sts=4
+ */
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 5                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2012 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>                             |
+   +----------------------------------------------------------------------+
+*/
+
+php_socket_t php_ev_zval_to_fd(zval **ppfd TSRMLS_DC);
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 sts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4 sts=4
+ */
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.