Index: ext/standard/basic_functions.c
===================================================================
--- ext/standard/basic_functions.c	(revision 315244)
+++ ext/standard/basic_functions.c	(working copy)
@@ -118,11 +118,6 @@
 
 static zend_class_entry *incomplete_class_entry = NULL;
 
-typedef struct _php_shutdown_function_entry {
-	zval **arguments;
-	int arg_count;
-} php_shutdown_function_entry;
-
 typedef struct _user_tick_function_entry {
 	zval **arguments;
 	int arg_count;
@@ -5078,6 +5073,38 @@
 }
 /* }}} */
 
+PHPAPI zend_bool register_user_shutdown_function(char *function_name, php_shutdown_function_entry shutdown_function_entry) /* {{{ */
+{
+	if (!BG(user_shutdown_function_names)) {
+		ALLOC_HASHTABLE(BG(user_shutdown_function_names));
+		zend_hash_init(BG(user_shutdown_function_names), 0, NULL, (void (*)(void *)) user_shutdown_function_dtor, 0);
+	}
+
+	return zend_hash_update(BG(user_shutdown_function_names), function_name, sizeof(function_name), &shutdown_function_entry, sizeof(php_shutdown_function_entry), NULL) != FAILURE;
+}
+/* }}} */
+
+PHPAPI zend_bool remove_user_shutdown_function(char *function_name) /* {{{ */
+{
+	if (BG(user_shutdown_function_names)) {
+		return zend_hash_del_key_or_index(BG(user_shutdown_function_names), function_name, sizeof(function_name), 0, HASH_DEL_KEY) != FAILURE;
+	}
+
+	return 0;
+}
+/* }}} */
+
+PHPAPI zend_bool append_user_shutdown_function(php_shutdown_function_entry shutdown_function_entry) /* {{{ */
+{
+	if (!BG(user_shutdown_function_names)) {
+		ALLOC_HASHTABLE(BG(user_shutdown_function_names));
+		zend_hash_init(BG(user_shutdown_function_names), 0, NULL, (void (*)(void *)) user_shutdown_function_dtor, 0);
+	}
+
+	return zend_hash_next_index_insert(BG(user_shutdown_function_names), &shutdown_function_entry, sizeof(php_shutdown_function_entry), NULL) != FAILURE;
+}
+/* }}} */
+
 ZEND_API void php_get_highlight_struct(zend_syntax_highlighter_ini *syntax_highlighter_ini) /* {{{ */
 {
 	syntax_highlighter_ini->highlight_comment = INI_STR("highlight.comment");
Index: ext/standard/basic_functions.h
===================================================================
--- ext/standard/basic_functions.h	(revision 315244)
+++ ext/standard/basic_functions.h	(working copy)
@@ -251,4 +251,13 @@
 PHPAPI double php_get_nan(void);
 PHPAPI double php_get_inf(void);
 
+typedef struct _php_shutdown_function_entry {
+	zval **arguments;
+	int arg_count;
+} php_shutdown_function_entry;
+
+PHPAPI extern zend_bool register_user_shutdown_function(char *function_name, php_shutdown_function_entry shutdown_function_entry);
+PHPAPI extern zend_bool remove_user_shutdown_function(char *function_name);
+PHPAPI extern zend_bool append_user_shutdown_function(php_shutdown_function_entry shutdown_function_entry);
+
 #endif /* BASIC_FUNCTIONS_H */
Index: ext/session/config.w32
===================================================================
--- ext/session/config.w32	(revision 315244)
+++ ext/session/config.w32	(working copy)
@@ -4,7 +4,7 @@
 ARG_ENABLE("session", "session support", "yes");
 
 if (PHP_SESSION == "yes") {
-	EXTENSION("session", "session.c mod_files.c mod_mm.c mod_user.c", false /* never shared */);
+	EXTENSION("session", "mod_user_class.c session.c mod_files.c mod_mm.c mod_user.c", false /* never shared */);
 	AC_DEFINE("HAVE_PHP_SESSION", 1, "Session support");
 	PHP_INSTALL_HEADERS("ext/session/", "mod_mm.h php_session.h mod_files.h mod_user.h");
 }
Index: ext/session/php_session.h
===================================================================
--- ext/session/php_session.h	(revision 315244)
+++ ext/session/php_session.h	(working copy)
@@ -129,6 +129,7 @@
 	zend_bool  cookie_secure;
 	zend_bool  cookie_httponly;
 	ps_module *mod;
+	ps_module *default_mod;
 	void *mod_data;
 	php_session_status session_status;
 	long gc_probability;
@@ -147,6 +148,8 @@
 			zval *ps_gc;
 		} name;
 	} mod_user_names;
+	int mod_user_implemented;
+	int mod_user_is_open;
 	const struct ps_serializer_struct *serializer;
 	zval *http_session_vars;
 	zend_bool auto_start;
@@ -268,4 +271,14 @@
 void php_session_auto_start(void *data);
 void php_session_shutdown(void *data);
 
+#define PS_CLASS_NAME "SessionHandler"
+extern zend_class_entry *php_session_class_entry;
+
+extern PHP_METHOD(SessionHandler, open);
+extern PHP_METHOD(SessionHandler, close);
+extern PHP_METHOD(SessionHandler, read);
+extern PHP_METHOD(SessionHandler, write);
+extern PHP_METHOD(SessionHandler, destroy);
+extern PHP_METHOD(SessionHandler, gc);
+
 #endif
Index: ext/session/mod_user_class.c
===================================================================
--- ext/session/mod_user_class.c	(revision 0)
+++ ext/session/mod_user_class.c	(revision 0)
@@ -0,0 +1,144 @@
+/*
+   +----------------------------------------------------------------------+
+   | 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: Arpad Ray <arpad@php.net>                                    |
+   +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#include "php.h"
+#include "php_session.h"
+
+#define PS_SANITY_CHECK						\
+	if (PS(default_mod) == NULL) {				\
+		php_error_docref(NULL TSRMLS_CC, E_CORE_ERROR, "Called default SessionHandler but session.save_handler is user"); \
+		RETURN_FALSE;						\
+	}							
+
+#define PS_SANITY_CHECK_IS_OPEN				\
+	PS_SANITY_CHECK; \
+	if (!PS(mod_user_is_open)) {			\
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Parent session handler is not open");	\
+		RETURN_FALSE;						\
+	}							
+
+/* {{{ proto bool SessionHandler::open(string save_path, string session_name)
+   Wraps the old open handler */
+PHP_METHOD(SessionHandler, open)
+{
+	char *save_path = NULL, *session_name = NULL;
+	int save_path_len, session_name_len;
+
+	PS_SANITY_CHECK;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &save_path, &save_path_len, &session_name, &session_name_len) == FAILURE) {
+		return;
+	}
+
+	PS(mod_user_is_open) = 1;
+	RETVAL_LONG(PS(default_mod)->s_open(&PS(mod_data), save_path, session_name TSRMLS_CC));
+}
+/* }}} */
+
+/* {{{ proto bool SessionHandler::close()
+   Wraps the old close handler */
+PHP_METHOD(SessionHandler, close)
+{
+	PS_SANITY_CHECK_IS_OPEN;
+
+	// don't return on failure, since not closing the default handler
+	// could result in memory leaks or other nasties
+	zend_parse_parameters_none();
+	
+	PS(mod_user_is_open) = 0;
+	RETVAL_LONG(PS(default_mod)->s_close(&PS(mod_data) TSRMLS_CC));
+}
+/* }}} */
+
+/* {{{ proto bool SessionHandler::read(string id)
+   Wraps the old read handler */
+PHP_METHOD(SessionHandler, read)
+{
+	char *key, *val;
+	int key_len, val_len;
+
+	PS_SANITY_CHECK_IS_OPEN;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) == FAILURE) {
+		return;
+	}
+
+	if (PS(default_mod)->s_read(&PS(mod_data), key, &val, &val_len TSRMLS_CC) == FAILURE) {
+		RETVAL_FALSE;
+		return;
+	}
+
+	RETVAL_STRINGL(val, val_len, 1);
+	efree(val);
+	return;
+}
+/* }}} */
+
+/* {{{ proto bool SessionHandler::write(string id, string data)
+   Wraps the old write handler */
+PHP_METHOD(SessionHandler, write)
+{
+	char *key, *val;
+	int key_len, val_len;
+
+	PS_SANITY_CHECK_IS_OPEN;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &val, &val_len) == FAILURE) {
+		return;
+	}
+
+	RETVAL_LONG(PS(default_mod)->s_write(&PS(mod_data), key, val, val_len TSRMLS_CC));
+}
+/* }}} */
+
+/* {{{ proto bool SessionHandler::destroy(string id)
+   Wraps the old destroy handler */
+PHP_METHOD(SessionHandler, destroy)
+{
+	char *key;
+	int key_len;
+
+	PS_SANITY_CHECK_IS_OPEN;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len) == FAILURE) {
+		return;
+	}
+	
+	PS(mod_user_is_open) = 0;
+	RETVAL_LONG(PS(default_mod)->s_destroy(&PS(mod_data), key TSRMLS_CC));
+}
+/* }}} */
+
+/* {{{ proto bool SessionHandler::gc(int maxlifetime)
+   Wraps the old gc handler */
+PHP_METHOD(SessionHandler, gc)
+{
+	long maxlifetime;
+	int nrdels;
+
+	PS_SANITY_CHECK;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &maxlifetime) == FAILURE) {
+		return;
+	}
+	
+	RETVAL_LONG(PS(default_mod)->s_gc(&PS(mod_data), maxlifetime, &nrdels TSRMLS_CC));
+}
+/* }}} */
Index: ext/session/config.m4
===================================================================
--- ext/session/config.m4	(revision 315244)
+++ ext/session/config.m4	(working copy)
@@ -11,7 +11,7 @@
 if test "$PHP_SESSION" != "no"; then
   PHP_PWRITE_TEST
   PHP_PREAD_TEST
-  PHP_NEW_EXTENSION(session, session.c mod_files.c mod_mm.c mod_user.c, $ext_shared)
+  PHP_NEW_EXTENSION(session, mod_user_class.c session.c mod_files.c mod_mm.c mod_user.c, $ext_shared)
   PHP_ADD_EXTENSION_DEP(session, hash, true)
   PHP_ADD_EXTENSION_DEP(session, spl)
   PHP_SUBST(SESSION_SHARED_LIBADD)
Index: ext/session/package.xml
===================================================================
--- ext/session/package.xml	(revision 315244)
+++ ext/session/package.xml	(working copy)
@@ -40,6 +40,7 @@
    <file role="src" name="mod_mm.c"/>
    <file role="src" name="mod_mm.h"/>
    <file role="src" name="mod_user.c"/>
+   <file role="src" name="mod_user_class.c"/>
    <file role="src" name="mod_user.h"/>
    <file role="src" name="php_session.h"/>
    <file role="src" name="session.c"/>
Index: ext/session/mod_user.c
===================================================================
--- ext/session/mod_user.c	(revision 315244)
+++ ext/session/mod_user.c	(working copy)
@@ -62,15 +62,10 @@
 	return retval;
 }
 
-#define STDVARS1							\
+#define STDVARS								\
 	zval *retval;							\
 	int ret = FAILURE
 
-#define STDVARS								\
-	STDVARS1;								\
-	char *mdata = PS_GET_MOD_DATA();		\
-	if (!mdata) { return FAILURE; }
-
 #define PSF(a) PS(mod_user_names).name.ps_##a
 
 #define FINISH								\
@@ -84,33 +79,29 @@
 PS_OPEN_FUNC(user)
 {
 	zval *args[2];
-	static char dummy = 0;
-	STDVARS1;
+	STDVARS;
 
 	SESS_ZVAL_STRING((char*)save_path, args[0]);
 	SESS_ZVAL_STRING((char*)session_name, args[1]);
 
 	retval = ps_call_handler(PSF(open), 2, args TSRMLS_CC);
-	if (retval) {
-		/* This is necessary to fool the session module. Yes, it's safe to
-		 * use a static. Neither mod_user nor the session module itself will
-		 * ever touch this pointer. It could be set to 0xDEADBEEF for all the
-		 * difference it makes, but for the sake of paranoia it's set to some
-		 * valid value. */
-		PS_SET_MOD_DATA(&dummy);
-	}
+	PS(mod_user_implemented) = 1;
 
 	FINISH;
 }
 
 PS_CLOSE_FUNC(user)
 {
-	STDVARS1;
+	STDVARS;
 
+	if (!PS(mod_user_implemented)) {
+		/* already closed */
+		return SUCCESS;
+	}
+
 	retval = ps_call_handler(PSF(close), 0, NULL TSRMLS_CC);
+	PS(mod_user_implemented) = 0;
 
-	PS_SET_MOD_DATA(NULL);
-
 	FINISH;
 }
 
Index: ext/session/session.c
===================================================================
--- ext/session/session.c	(revision 315244)
+++ ext/session/session.c	(working copy)
@@ -50,6 +50,7 @@
 #include "ext/standard/info.h"
 #include "ext/standard/php_smart_str.h"
 #include "ext/standard/url.h"
+#include "ext/standard/basic_functions.h"
 
 #include "mod_files.h"
 #include "mod_user.h"
@@ -82,6 +83,7 @@
 	PS(id) = NULL;
 	PS(session_status) = php_session_none;
 	PS(mod_data) = NULL;
+	PS(mod_user_is_open) = 0;
 	/* Do NOT init PS(mod_user_names) here! */
 	PS(http_session_vars) = NULL;
 }
@@ -95,7 +97,7 @@
 		PS(http_session_vars) = NULL;
 	}
 	/* Do NOT destroy PS(mod_user_names) here! */
-	if (PS(mod_data)) {
+	if (PS(mod_data) || PS(mod_user_implemented)) {
 		zend_try {
 			PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
 		} zend_end_try();
@@ -472,7 +474,7 @@
 	int ret = FAILURE;
 
 	IF_SESSION_VARS() {
-		if (PS(mod_data)) {
+ 		if (PS(mod_data) || PS(mod_user_implemented)) {
 			char *val;
 			int vallen;
 
@@ -494,7 +496,7 @@
 		}
 	}
 
-	if (PS(mod_data)) {
+	if (PS(mod_data) || PS(mod_user_implemented)) {
 		PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
 	}
 }
@@ -526,6 +528,8 @@
 		}
 		return FAILURE;
 	}
+
+	PS(default_mod) = PS(mod);
 	PS(mod) = tmp;
 
 	return SUCCESS;
@@ -1420,7 +1424,7 @@
 
 	php_session_cache_limiter(TSRMLS_C);
 
-	if (PS(mod_data) && PS(gc_probability) > 0) {
+	if ((PS(mod_data) || PS(mod_user_implemented)) && PS(gc_probability) > 0) {
 		int nrdels = -1;
 
 		nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C));
@@ -1555,7 +1559,7 @@
 			zval_dtor(return_value);
 			RETURN_FALSE;
 		}
-		if (PS(mod_data)) {
+		if (PS(mod_data) || PS(mod_user_implemented)) {
 			PS(mod)->s_close(&PS(mod_data) TSRMLS_CC);
 		}
 		PS(mod_data) = NULL;
@@ -1577,14 +1581,86 @@
 		RETURN_FALSE;
 	}
 
-	if (argc != 6) {
+	if (argc != 1 && argc != 2 && argc != 6) {
 		WRONG_PARAM_COUNT;
 	}
 
+	if (argc <= 2) {
+		zval *obj = NULL, *callback = NULL;
+		zend_uint func_name_len;
+		char *func_name;
+		HashPosition pos;
+		zend_function *default_mptr, *current_mptr;
+		ulong func_index;
+		php_shutdown_function_entry shutdown_function_entry;
+		zend_bool register_shutdown = 1;
+
+		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|b", &obj, php_session_class_entry, &register_shutdown) == FAILURE) {
+			return;
+		}
+
+		/* Find implemented methods */
+		zend_hash_internal_pointer_reset_ex(&php_session_class_entry->function_table, &pos);
+		i = 0;
+		while (zend_hash_get_current_data_ex(&php_session_class_entry->function_table, (void **) &default_mptr, &pos) == SUCCESS) {
+			zend_hash_get_current_key_ex(&php_session_class_entry->function_table, &func_name, &func_name_len, &func_index, 0, &pos);
+
+			if (zend_hash_find(&Z_OBJCE_P(obj)->function_table, func_name, func_name_len, (void **)&current_mptr) == SUCCESS) {
+				if (PS(mod_user_names).names[i] != NULL) {
+					zval_ptr_dtor(&PS(mod_user_names).names[i]);
+				}
+
+				MAKE_STD_ZVAL(callback);
+				array_init_size(callback, 2);
+				Z_ADDREF_P(obj);
+				add_next_index_zval(callback, obj);
+				add_next_index_stringl(callback, func_name, func_name_len - 1, 1);
+				PS(mod_user_names).names[i] = callback;
+			} else {
+				php_error_docref(NULL TSRMLS_CC, E_ERROR, "Session handler's function table is corrupt");
+				RETURN_FALSE;
+			}
+
+			zend_hash_move_forward_ex(&php_session_class_entry->function_table, &pos);
+			++i;
+		}
+
+		if (register_shutdown) {
+			/* create shutdown function */
+			shutdown_function_entry.arg_count = 1;
+			shutdown_function_entry.arguments = (zval **) safe_emalloc(sizeof(zval *), 1, 0);
+
+			MAKE_STD_ZVAL(callback);
+			ZVAL_STRING(callback, "session_register_shutdown", 1);
+			shutdown_function_entry.arguments[0] = callback;
+
+			/* add shutdown function, removing the old one if it exists */
+			if (!register_user_shutdown_function("session_shutdown", shutdown_function_entry)) {
+				zval_ptr_dtor(&callback);
+				efree(shutdown_function_entry.arguments);
+				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to register session shutdown function");
+				RETURN_FALSE;
+			}
+		} else {
+			/* remove shutdown function */
+			remove_user_shutdown_function("session_shutdown");
+		}
+
+		PS(mod_user_implemented) = 1;
+		if (PS(mod) && PS(session_status) == php_session_none && PS(mod) != &ps_mod_user) {
+			zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+		}
+
+		RETURN_TRUE;
+	}
+
 	if (zend_parse_parameters(argc TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
 		return;
 	}
 
+	/* remove shutdown function */
+	remove_user_shutdown_function("session_shutdown");
+
 	for (i = 0; i < 6; i++) {
 		if (!zend_is_callable(*args[i], 0, &name TSRMLS_CC)) {
 			efree(args);
@@ -1594,8 +1670,12 @@
 		}
 		efree(name);
 	}
+	
+	PS(mod_user_implemented) = 1;
 
-	zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+	if (PS(mod) && PS(mod) != &ps_mod_user) {
+		zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
+	}
 
 	for (i = 0; i < 6; i++) {
 		if (PS(mod_user_names).names[i] != NULL) {
@@ -1830,6 +1910,43 @@
 }
 /* }}} */
 
+/* {{{ proto void session_register_shutdown(void)
+   Registers session_write_close() as a shutdown function */
+static PHP_FUNCTION(session_register_shutdown)
+{
+	php_shutdown_function_entry shutdown_function_entry;
+	zval *callback;
+
+	/* This function is registered itself as a shutdown function by
+	 * session_set_save_handler($obj). The reason we now register another
+	 * shutdown function is in case the user registered their own shutdown
+	 * function after calling session_set_save_handler(), which expects
+	 * the session still to be available.
+	 */
+
+	shutdown_function_entry.arg_count = 1;
+	shutdown_function_entry.arguments = (zval **) safe_emalloc(sizeof(zval *), 1, 0);
+
+	MAKE_STD_ZVAL(callback);
+	ZVAL_STRING(callback, "session_write_close", 1);
+	shutdown_function_entry.arguments[0] = callback;
+
+	if (!append_user_shutdown_function(shutdown_function_entry)) {
+		zval_ptr_dtor(&callback);
+		efree(shutdown_function_entry.arguments);
+
+		/* Unable to register shutdown function, presumably because of lack
+		 * of memory, so flush the session now. It would be done in rshutdown
+		 * anyway but the handler will have had it's dtor called by then.
+		 * If the user does have a later shutdown function which needs the
+		 * session then tough luck.
+		 */
+		php_session_flush(TSRMLS_C);
+		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to register session flush function");
+	}
+}
+/* }}} */
+
 /* {{{ arginfo */
 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_name, 0, 0, 0)
 	ZEND_ARG_INFO(0, name)
@@ -1882,6 +1999,31 @@
 	ZEND_ARG_INFO(0, secure)
 	ZEND_ARG_INFO(0, httponly)
 ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_open, 0)
+	ZEND_ARG_INFO(0, save_path)
+	ZEND_ARG_INFO(0, session_name)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_close, 0)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_read, 0)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_write, 0)
+	ZEND_ARG_INFO(0, key)
+	ZEND_ARG_INFO(0, val)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_destroy, 0)
+	ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_gc, 0)
+	ZEND_ARG_INFO(0, maxlifetime)
+ZEND_END_ARG_INFO()
 /* }}} */
 
 /* {{{ session_functions[]
@@ -1903,11 +2045,28 @@
 	PHP_FE(session_set_cookie_params, arginfo_session_set_cookie_params)
 	PHP_FE(session_get_cookie_params, arginfo_session_void)
 	PHP_FE(session_write_close,       arginfo_session_void)
+	PHP_FE(session_register_shutdown, arginfo_session_void)
 	PHP_FALIAS(session_commit, session_write_close, arginfo_session_void)
 	PHP_FE_END
 };
 /* }}} */
 
+/* session class */
+zend_class_entry *php_session_class_entry;
+
+/* {{{ session class functions[]
+ */
+static const zend_function_entry php_session_class_functions[] = {
+	PHP_ME(SessionHandler, open, arginfo_session_class_open, ZEND_ACC_PUBLIC)
+	PHP_ME(SessionHandler, close, arginfo_session_class_close, ZEND_ACC_PUBLIC)
+	PHP_ME(SessionHandler, read, arginfo_session_class_read, ZEND_ACC_PUBLIC)
+	PHP_ME(SessionHandler, write, arginfo_session_class_write, ZEND_ACC_PUBLIC)
+	PHP_ME(SessionHandler, destroy, arginfo_session_class_destroy, ZEND_ACC_PUBLIC)
+	PHP_ME(SessionHandler, gc, arginfo_session_class_gc, ZEND_ACC_PUBLIC)
+	{ NULL, NULL, NULL }
+};
+/* }}} */
+
 /* ********************************
    * Module Setup and Destruction *
    ******************************** */
@@ -1983,6 +2142,9 @@
 	ps_globals->serializer = NULL;
 	ps_globals->mod_data = NULL;
 	ps_globals->session_status = php_session_none;
+	ps_globals->default_mod = NULL;
+	ps_globals->mod_user_implemented = 0;
+	ps_globals->mod_user_is_open = 0;
 	for (i = 0; i < 6; i++) {
 		ps_globals->mod_user_names.names[i] = NULL;
 	}
@@ -1992,6 +2154,8 @@
 
 static PHP_MINIT_FUNCTION(session) /* {{{ */
 {
+	zend_class_entry ce;
+
 	zend_register_auto_global("_SESSION", sizeof("_SESSION")-1, 0, NULL TSRMLS_CC);
 
 	PS(module_number) = module_number; /* if we really need this var we need to init it in zts mode as well! */
@@ -2004,6 +2168,11 @@
 #endif
 	php_session_rfc1867_orig_callback = php_rfc1867_callback;
 	php_rfc1867_callback = php_session_rfc1867_callback;
+
+ 	/* Register base class */
+ 	INIT_CLASS_ENTRY(ce, PS_CLASS_NAME, php_session_class_functions);
+ 	php_session_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
+
 	return SUCCESS;
 }
 /* }}} */
