Index: ext/session/config.w32 =================================================================== --- ext/session/config.w32 (revision 291124) +++ 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"); } Index: ext/session/php_session.h =================================================================== --- ext/session/php_session.h (revision 291124) +++ ext/session/php_session.h (working copy) @@ -128,6 +128,7 @@ zend_bool cookie_secure; zend_bool cookie_httponly; ps_module *mod; + ps_module *old_mod; void *mod_data; php_session_status session_status; long gc_probability; @@ -146,6 +147,7 @@ zval *ps_gc; } name; } mod_user_names; + int mod_active; const struct ps_serializer_struct *serializer; zval *http_session_vars; zend_bool auto_start; @@ -285,4 +287,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,123 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 6 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2009 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 + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#include "php.h" +#include "php_session.h" + +#define PS_SANITY_CHECK \ + if (PS(old_mod) == NULL) { \ + RETURN_FALSE; \ + } + + +/* {{{ proto bool SessionHandler::open(string save_path, string session_name) + Wraps the old open handler */ +PHP_METHOD(SessionHandler, open) +{ + PS_SANITY_CHECK; + + RETVAL_LONG(PS(old_mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name) TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto bool SessionHandler::close() + Wraps the old close handler */ +PHP_METHOD(SessionHandler, close) +{ + PS_SANITY_CHECK; + + RETVAL_LONG(PS(old_mod)->s_close(&PS(mod_data) TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto bool SessionHandler::read(save_path, session_name) + Wraps the old read handler */ +PHP_METHOD(SessionHandler, read) +{ + char *key, *val; + int key_len, val_len; + + PS_SANITY_CHECK; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len, UG(utf8_conv)) == FAILURE) { + return; + } + + if (PS(old_mod)->s_read(&PS(mod_data), key, &val, &val_len TSRMLS_CC) == FAILURE) { + RETURN_FALSE; + } + + RETVAL_STRINGL(val, val_len, 1); + efree(val); + return; +} +/* }}} */ + +/* {{{ proto bool SessionHandler::write(save_path, session_name) + Wraps the old write handler */ +PHP_METHOD(SessionHandler, write) +{ + char *key, *val; + int key_len, val_len; + + PS_SANITY_CHECK; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &key, &key_len, &val, &val_len, UG(utf8_conv)) == FAILURE) { + return; + } + + RETVAL_LONG(PS(old_mod)->s_write(&PS(mod_data), key, val, val_len TSRMLS_CC)); +} +/* }}} */ + +/* {{{ proto bool SessionHandler::destroy() + Wraps the old destroy handler */ +PHP_METHOD(SessionHandler, destroy) +{ + char *key; + int key_len; + + PS_SANITY_CHECK; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_len, UG(utf8_conv)) == FAILURE) { + return; + } + + RETVAL_LONG(PS(old_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, UG(utf8_conv)) == FAILURE) { + return; + } + + RETVAL_LONG(PS(old_mod)->s_gc(&PS(mod_data), maxlifetime, &nrdels TSRMLS_CC)); +} +/* }}} */ + Index: ext/session/config.m4 =================================================================== --- ext/session/config.m4 (revision 291124) +++ 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_SUBST(SESSION_SHARED_LIBADD) PHP_INSTALL_HEADERS(ext/session, [php_session.h mod_files.h mod_user.h]) Index: ext/session/package.xml =================================================================== --- ext/session/package.xml (revision 291124) +++ ext/session/package.xml (working copy) @@ -40,6 +40,7 @@ + Index: ext/session/mod_user.c =================================================================== --- ext/session/mod_user.c (revision 291124) +++ 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,24 @@ 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_active) = 1; FINISH; } PS_CLOSE_FUNC(user) { - STDVARS1; + STDVARS; retval = ps_call_handler(PSF(close), 0, NULL TSRMLS_CC); + PS(mod_active) = 0; - PS_SET_MOD_DATA(NULL); - FINISH; } Index: ext/session/tests/session_set_save_handler_class.phpt =================================================================== --- ext/session/tests/session_set_save_handler_class.phpt (revision 0) +++ ext/session/tests/session_set_save_handler_class.phpt (revision 0) @@ -0,0 +1,197 @@ +--TEST-- +Test session_set_save_handler() function : class handler +--INI-- +session.save_path= +session.name=PHPSESSID +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +*** Testing session_set_save_handler() : class handler *** +*** Testing session_set_save_handler() : basic class wrapping existing handler *** +Open +Read %x +string(32) "%x" +unicode(5) "files" +unicode(4) "user" +int(2) +array(0) { +} +Open %x +Read %x +array(1) { + [u"foo"]=> + unicode(1) "☺" +} +*** Testing session_set_save_handler() : full handler implementation *** +string(32) "%x" +unicode(4) "user" +array(1) { + [u"foo"]=> + unicode(1) "☺" +} +array(1) { + [u"foo"]=> + unicode(1) "☺" +} +*** Testing session_set_save_handler() : inheritance *** +array(1) { + [u"foo"]=> + unicode(1) "☺" +} +string(32) "%x" +array(1) { + [u"foo"]=> + unicode(1) "☺" +} +unicode(3) "hai" Index: ext/session/session.c =================================================================== --- ext/session/session.c (revision 291124) +++ ext/session/session.c (working copy) @@ -94,9 +94,10 @@ PS(http_session_vars) = NULL; } /* Do NOT destroy PS(mod_user_names) here! */ - if (PS(mod_data)) { + if (PS(mod_active)) { zend_try { PS(mod)->s_close(&PS(mod_data) TSRMLS_CC); + PS(mod_active) = 0; } zend_end_try(); } if (PS(id)) { @@ -411,6 +412,8 @@ return; } + PS(mod_active) = 1; + /* Open session handler first */ if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name) TSRMLS_CC) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path)); @@ -451,7 +454,7 @@ IF_SESSION_VARS() { - if (PS(mod_data)) { + if (PS(mod_active)) { char *val; int vallen; @@ -473,8 +476,9 @@ } } - if (PS(mod_data)) { + if (PS(mod_active)) { PS(mod)->s_close(&PS(mod_data) TSRMLS_CC); + PS(mod_active) = 0; } } /* }}} */ @@ -505,6 +509,7 @@ } return FAILURE; } + PS(old_mod) = PS(mod); PS(mod) = tmp; return SUCCESS; @@ -1381,7 +1386,7 @@ php_session_cache_limiter(TSRMLS_C); - if (PS(mod_data) && PS(gc_probability) > 0) { + if (PS(mod_active) && PS(gc_probability) > 0) { int nrdels = -1; nrand = (int) ((float) PS(gc_divisor) * php_combined_lcg(TSRMLS_C)); @@ -1516,8 +1521,9 @@ zval_dtor(return_value); RETURN_FALSE; } - if (PS(mod_data)) { + if (PS(mod_active)) { PS(mod)->s_close(&PS(mod_data) TSRMLS_CC); + PS(mod_active) = 0; } PS(mod_data) = NULL; @@ -1526,7 +1532,20 @@ } /* }}} */ -/* {{{ proto void session_set_save_handler(string open, string close, string read, string write, string destroy, string gc) U +#define PS_METHOD_CALLBACK(x) \ + callback = PS(mod_user_names).name.ps_##x; \ + if (callback != NULL) { \ + zval_ptr_dtor(&callback); \ + callback = NULL; \ + } \ + \ + MAKE_STD_ZVAL(callback); \ + array_init(callback); \ + add_next_index_zstrl(callback, IS_UNICODE, class, class_len, 1); \ + add_next_index_string(callback, #x, 1); \ + PS(mod_user_names).name.ps_##x = callback; + +/* {{{ proto void session_set_save_handler(string open [, string close, string read, string write, string destroy, string gc]) U Sets user-level functions */ static PHP_FUNCTION(session_set_save_handler) { @@ -1538,10 +1557,51 @@ RETURN_FALSE; } - if (argc != 6) { + if (argc != 1 && argc != 6) { WRONG_PARAM_COUNT; } + if (argc == 1) { + zstr class; + uint class_len; + zend_class_entry **pce, *ce = NULL; + zval *callback; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "u", &class, &class_len) == FAILURE) { + return; + } + + if (zend_u_lookup_class(IS_UNICODE, class, class_len, &pce TSRMLS_CC) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "The provided class (%s) does not exist", class); + RETURN_FALSE; + } + + ce = *pce; + while (ce->parent) { + ce = ce->parent; + } + + if (ce != php_session_class_entry) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "The session handler class must descend from %s", PS_CLASS_NAME); + RETURN_FALSE; + } + + PS_METHOD_CALLBACK(open); + PS_METHOD_CALLBACK(close); + PS_METHOD_CALLBACK(read); + PS_METHOD_CALLBACK(write); + PS_METHOD_CALLBACK(destroy); + PS_METHOD_CALLBACK(gc); + + if (PS(mod) && PS(session_status) == php_session_none && PS(mod) != &ps_mod_user) { + PS(old_mod) = PS(mod); + zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); + } + + PS(mod_active) = 1; + RETURN_TRUE; + } + if (zend_parse_parameters(argc TSRMLS_CC, "+", &args, &num_args) == FAILURE) { return; } @@ -1555,7 +1615,8 @@ } zval_dtor(&name); } - + + PS(mod_active) = 1; 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++) { @@ -1885,6 +1946,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[] @@ -1911,6 +1997,22 @@ }; /* }}} */ +/* 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 | ZEND_ACC_STATIC) + PHP_ME(SessionHandler, close, arginfo_session_class_close, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(SessionHandler, read, arginfo_session_class_read, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(SessionHandler, write, arginfo_session_class_write, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(SessionHandler, destroy, arginfo_session_class_destroy, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(SessionHandler, gc, arginfo_session_class_gc, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + { NULL, NULL, NULL } +}; +/* }}} */ + /* ******************************** * Module Setup and Destruction * ******************************** */ @@ -1986,15 +2088,19 @@ ps_globals->serializer = NULL; ps_globals->mod_data = NULL; ps_globals->session_status = php_session_none; + ps_globals->mod_active = 0; for (i = 0; i < 6; i++) { ps_globals->mod_user_names.names[i] = NULL; } + ps_globals->http_session_vars = NULL; } /* }}} */ static PHP_MINIT_FUNCTION(session) /* {{{ */ { + zend_class_entry ce; + zend_register_auto_global("_SESSION", sizeof("_SESSION")-1, NULL TSRMLS_CC); PS(module_number) = module_number; /* if we really need this var we need to init it in zts mode as well! */ @@ -2007,6 +2113,10 @@ #endif php_session_rfc1867_orig_callback = php_rfc1867_callback; php_rfc1867_callback = php_session_rfc1867_callback; + + INIT_CLASS_ENTRY(ce, PS_CLASS_NAME, php_session_class_functions); + php_session_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + return SUCCESS; } /* }}} */