]> granicus.if.org Git - php/commitdiff
WIP - test passes
authorYasuo Ohgaki <yohgaki@php.net>
Wed, 21 Jan 2015 10:13:59 +0000 (19:13 +0900)
committerYasuo Ohgaki <yohgaki@php.net>
Thu, 22 Jan 2015 04:34:58 +0000 (13:34 +0900)
21 files changed:
ext/session/mod_files.c
ext/session/mod_files.h
ext/session/mod_user.c
ext/session/mod_user.h
ext/session/mod_user_class.c
ext/session/php_session.h
ext/session/session.c
ext/session/tests/save_handler.inc
ext/session/tests/session_basic1.phpt [new file with mode: 0644]
ext/session/tests/session_basic2.phpt [new file with mode: 0644]
ext/session/tests/session_commit_variation3.phpt
ext/session/tests/session_commit_variation4.phpt
ext/session/tests/session_commit_variation5.phpt [new file with mode: 0644]
ext/session/tests/session_decode_variation3.phpt
ext/session/tests/session_set_save_handler_basic.phpt
ext/session/tests/session_set_save_handler_class_001.phpt
ext/session/tests/session_set_save_handler_class_018.phpt [new file with mode: 0644]
ext/session/tests/session_set_save_handler_variation4.phpt
ext/session/tests/session_set_save_handler_variation5.phpt [new file with mode: 0644]
ext/session/tests/session_set_save_handler_variation6.phpt [moved from ext/session/tests/session_set_save_handler_write_short_circuit.phpt with 88% similarity]
ext/session/tests/session_start_error.phpt

index 1d6686a5d9fbbaf06e4e35875032cf30f4362d06..36c9a3354e36042f66f0d6e208e15cad2190ffa4 100644 (file)
    +----------------------------------------------------------------------+
  */
 
-/* $Id$ */
+/**************************************************************************
+ * Files save handler should be used as reference implementations of session
+ * save handlers. PS_* functions are called as follows with standard usage.
+ *
+ *    PS_OPEN_FUNC()  - Create module data that manages save handler.
+ *    PS_CREATE_SID() and/or PS_VALIDATE_SID()
+ *                    - PS_CREATE_ID() is called if session ID(key) is not
+ *                      provided or invalid. PS_VALIDATE_SID() is called to
+ *                      verify session ID already exists or not to mitigate
+ *                      session adoption vulunerabilty risk.
+ *    PS_READ_FUNC()  - Read data from storage.
+ *    PS_GC_FUNC()    - Perform GC. Called by probability
+ *                                (session.gc_probability/session.gc_divisor).
+ *    PS_WRITE_FUNC() or PS_UPDATE_TIMESTAMP() 
+ *                    - Write session data or update session data timestamp.
+ *                      It depends on session data change.
+ *    PS_CLOSE_FUNC() - Clean up module data created by PS_OPEN_FUNC().
+ *
+ * Session module gurantees PS_OPEN_FUNC() is called before calling other
+ * PS_*_FUNC() functions. Other than this, session module may call any
+ * PS_*_FUNC() at any time. You may assume non null *mod_data created by
+ * PS_OPEN_FUNC() is passed to PS_*_FUNC().
+ *
+ * NOTE:
+ *  - Save handlers _MUST_NOT_ change/refer PS() values.
+ *    i.e. PS(id), PS(session_status), PS(mod) and any other PS() values.
+ *    Use only function parameters passed from session module.
+ *  - Save handler _MUST_ use PS_GET_MOD_DATA()/PS_SET_MOD_DATA() macro to
+ *    set/get save handler module data(mod_data). mod_data contains
+ *    data required by PS modules. It will not be NULL except PS_OPEN_FUNC().
+ *  - Refer to PS_* macros in php_session.h for function/parameter defitions.
+ *  - Returning FAILURE state from PS_* function results in raising errors.
+ *    Avoid failure state as much as possible.
+ *  - Use static ps_[module name]_[function name] functions for internal use.
+ *************************************************************************/
 
 #include "php.h"
 
@@ -67,7 +101,8 @@ typedef struct {
 } ps_files;
 
 ps_module ps_mod_files = {
-       PS_MOD_SID(files)
+       /* New save handlers MUST use PS_MOD_UPDATE_TIMESTAMP macro */
+       PS_MOD_UPDATE_TIMESTAMP(files)
 };
 
 
@@ -182,6 +217,43 @@ static void ps_files_open(ps_files *data, const char *key)
        }
 }
 
+static int ps_files_write(ps_files *data, zend_string *key, zend_string *val)
+{
+       zend_long n;
+       zend_stat_t sbuf;
+
+       /* PS(id) may be changed by calling session_regenerate_id().
+          Re-initialization should be tried here. ps_files_open() checks
+       data->lastkey and reopen when it is needed. */
+       ps_files_open(data, key->val);
+       if (data->fd < 0) {
+               return FAILURE;
+       }
+
+       /* Truncate file if the amount of new data is smaller than the existing data set. */
+       if (val->len < (int)data->st_size) {
+               php_ignore_value(ftruncate(data->fd, 0));
+       }
+
+#if defined(HAVE_PWRITE)
+       n = pwrite(data->fd, val->val, val->len, 0);
+#else
+       lseek(data->fd, 0, SEEK_SET);
+       n = write(data->fd, val->val, val->len);
+#endif
+
+       if (n != val->len) {
+               if (n == -1) {
+                       php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
+               } else {
+                       php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested");
+               }
+               return FAILURE;
+       }
+
+       return SUCCESS;
+}
+
 static int ps_files_cleanup_dir(const char *dirname, int maxlifetime)
 {
        DIR *dir;
@@ -252,6 +324,18 @@ static int ps_files_key_exists(ps_files *data, const char *key)
 
 #define PS_FILES_DATA ps_files *data = PS_GET_MOD_DATA()
 
+
+/*
+ * Open save handler. Setup resources that are needed by the handler.
+ * PARAMETERS: PS_OPEN_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL valid module data
+ * (void **mod_data) with SUCCESS, NULL(default) for FAILUREs.
+ *
+ * Files save handler checks/create save_path directory and setup ps_files data.
+ * Note that files save handler supports splitting session data into multiple
+ * directories.
+ * *mod_data, *save_path, *session_name are guranteed to have non-NULL values.
+ */
 PS_OPEN_FUNC(files)
 {
        ps_files *data;
@@ -316,6 +400,17 @@ PS_OPEN_FUNC(files)
        return SUCCESS;
 }
 
+
+/*
+ * Clean up opened resources.
+ * PARAMETERS: PS_CLOSE_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS. Must set PS module data(void **mod_data) to NULL.
+ *
+ * Files save handler closes open files and it's memory.
+ * *mod_data is guranteed to have non-NULL value.
+ * PS_CLOSE_FUNC() must set *mod_data to NULL. PS_CLOSE_FUNC() should not
+ * fail.
+ */
 PS_CLOSE_FUNC(files)
 {
        PS_FILES_DATA;
@@ -333,32 +428,24 @@ PS_CLOSE_FUNC(files)
        return SUCCESS;
 }
 
+
+/*
+ * Read session data from opened resource.
+ * PARAMETERS: PS_READ_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE. Must set non-NULL session data to (zend_string **val)
+ * for SUCCESS. NULL(default) for FAILUREs.
+ *
+ * Files save handler supports splitting session data into multiple
+ * directories.
+ * *mod_data, *key are guranteed to have non-NULL values.
+ */
 PS_READ_FUNC(files)
 {
        zend_long n;
        zend_stat_t sbuf;
        PS_FILES_DATA;
 
-       /* If strict mode, check session id existence */
-       if (PS(use_strict_mode) &&
-               ps_files_key_exists(data, key? key->val : NULL) == FAILURE) {
-               /* key points to PS(id), but cannot change here. */
-               if (key) {
-                       zend_string_release(PS(id));
-                       PS(id) = NULL;
-               }
-               PS(id) = PS(mod)->s_create_sid((void **)&data);
-               if (!PS(id)) {
-                       return FAILURE;
-               }
-               if (PS(use_cookies)) {
-                       PS(send_cookie) = 1;
-               }
-               php_session_reset_id();
-               PS(session_status) = php_session_active;
-       }
-
-       ps_files_open(data, PS(id)->val);
+       ps_files_open(data, key->val);
        if (data->fd < 0) {
                return FAILURE;
        }
@@ -390,47 +477,82 @@ PS_READ_FUNC(files)
                        php_error_docref(NULL, E_WARNING, "read returned less bytes than requested");
                }
                zend_string_release(*val);
+               *val =  STR_EMPTY_ALLOC();
                return FAILURE;
        }
 
        return SUCCESS;
 }
 
+
+/*
+ * Write session data.
+ * PARAMETERS: PS_WRITE_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE.
+ *
+ * PS_WRITE_FUNC() must write session data(zend_string *val) unconditionally.
+ * *mod_data, *key, *val are guranteed to have non-NULL values.
+ */
 PS_WRITE_FUNC(files)
 {
-       zend_long n;
        PS_FILES_DATA;
 
-       ps_files_open(data, key->val);
-       if (data->fd < 0) {
-               return FAILURE;
-       }
+       return ps_files_write(data, key, val);
+}
 
-       /* Truncate file if the amount of new data is smaller than the existing data set. */
 
-       if (val->len < (int)data->st_size) {
-               php_ignore_value(ftruncate(data->fd, 0));
+/*
+ * Update session data modification/access time stamp.
+ * PARAMETERS: PS_UPDATE_TIMESTAMP_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE.
+ *
+ * PS_UPDATE_TIMESTAMP_FUNC() updates time stamp(mtime) so that active session
+ * data files will not be purged by GC. If session data storage does not need to
+ * update timestamp, it should return SUCCESS simply. (e.g. Memcache)
+ * *mod_data, *key, *val are guranteed to have non-NULL values.
+ *
+ * NOTE: Updating access timestamp at PS_READ_FUNC() may extend life of obsolete
+ * session data. Use of PS_UPDATE_TIMESTAMP_FUNC() is prefered whenenver it is
+ * possible.
+ */
+PS_UPDATE_TIMESTAMP_FUNC(files)
+{
+       char buf[MAXPATHLEN];
+       struct utimbuf newtimebuf;
+       struct utimbuf *newtime = &newtimebuf;
+       int ret;
+       PS_FILES_DATA;
+
+       if (!ps_files_path_create(buf, sizeof(buf), data, key->val)) {
+               return FAILURE;
        }
 
-#if defined(HAVE_PWRITE)
-       n = pwrite(data->fd, val->val, val->len, 0);
+       /* Update mtime */
+#ifdef HAVE_UTIME_NULL
+       newtime = NULL;
 #else
-       lseek(data->fd, 0, SEEK_SET);
-       n = write(data->fd, val->val, val->len);
+       newtime->modtime = newtime->actime = time(NULL);
 #endif
-
-       if (n != val->len) {
-               if (n == -1) {
-                       php_error_docref(NULL, E_WARNING, "write failed: %s (%d)", strerror(errno), errno);
-               } else {
-                       php_error_docref(NULL, E_WARNING, "write wrote less bytes than requested");
-               }
-               return FAILURE;
+       ret = VCWD_UTIME(buf, newtime);
+       if (ret == -1) {
+               /* New session ID, create data file */
+               return ps_files_write(data, key, val);
        }
 
        return SUCCESS;
 }
 
+
+/*
+ * Delete session data.
+ * PARAMETERS: PS_DESTROY_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE.
+ *
+ * PS_DESTROY_FUNC() must remove the session data specified by *key from
+ * session data storage unconditionally. It must not return FAILURE for
+ * non-existent session data.
+ * *mod_data, *key are guranteed to have non-NULL values.
+ */
 PS_DESTROY_FUNC(files)
 {
        char buf[MAXPATHLEN];
@@ -455,11 +577,23 @@ PS_DESTROY_FUNC(files)
        return SUCCESS;
 }
 
+
+/*
+ * Cleanup expired session data.
+ * PARAMETERS: PS_GC_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE. Number of deleted records(int *nrdels(default=-1)).
+ *
+ * PS_GC_FUNC() must remove session data that are not accessed
+ * 'session.maxlifetime'(seconds). If storage does not need manual GC, it
+ * may return SUCCESS simply. (e.g. Memcache) It must set number of records
+ * deleted(nrdels).
+ * *mod_data is guranteed to have non-NULL value.
+ */
 PS_GC_FUNC(files)
 {
        PS_FILES_DATA;
 
-       /* we don't perform any cleanup, if dirdepth is larger than 0.
+       /* We don't perform any cleanup, if dirdepth is larger than 0.
           we return SUCCESS, since all cleanup should be handled by
           an external entity (i.e. find -ctime x | xargs rm) */
 
@@ -470,6 +604,20 @@ PS_GC_FUNC(files)
        return SUCCESS;
 }
 
+
+/*
+ * Create session ID.
+ * PARAMETERS: PS_CREATE_SID_ARGS in php_session.h
+ * RETURN VALUE: Valid session ID(zend_string *) or NULL for FAILURE.
+ *
+ * PS_CREATE_SID_FUNC() must check collision. i.e. Check session data if
+ * new sid exists already.
+ * *mod_data is guranteed to have non-NULL value.
+ * NOTE: Default php_session_create_id() does not check collision. If
+ * NULL is returned, session module create new ID by using php_session_create_id().
+ * If php_session_create_id() fails due to invalid configuration, it raises E_ERROR.
+ * NULL return value checks from php_session_create_id() is not required generally.
+ */
 PS_CREATE_SID_FUNC(files)
 {
        zend_string *sid;
@@ -478,13 +626,21 @@ PS_CREATE_SID_FUNC(files)
 
        do {
                sid = php_session_create_id((void**)&data);
+               if (!sid) {
+                       if (--maxfail < 0) {
+                               return NULL;
+                       } else {
+                               continue;
+                       }
+               }
                /* Check collision */
-               if (data && ps_files_key_exists(data, sid? sid->val : NULL) == SUCCESS) {
+               /* FIXME: mod_data(data) should not be NULL (User handler could be NULL) */
+               if (data && ps_files_key_exists(data, sid->val) == SUCCESS) {
                        if (sid) {
                                zend_string_release(sid);
                                sid = NULL;
                        }
-                       if (!(maxfail--)) {
+                       if (--maxfail < 0) {
                                return NULL;
                        }
                }
@@ -494,6 +650,22 @@ PS_CREATE_SID_FUNC(files)
 }
 
 
+/*
+ * Check session ID existence for use_strict_mode support.
+ * PARAMETERS: PS_VALIDATE_SID_ARGS in php_session.h
+ * RETURN VALUE: SUCCESS or FAILURE.
+ *
+ * Return SUCCESS for valid key(already exsting session).
+ * Return FAILURE for invalid key(non-existing session).
+ * *mod_data, *key are guranteed to have non-NULL values.
+ */
+PS_VALIDATE_SID_FUNC(files)
+{
+       PS_FILES_DATA;
+
+       return ps_files_key_exists(data, key->val);
+}
+
 /*
  * Local variables:
  * tab-width: 4
index 9b068e68283ef88143f603da5c17e0479cedaa34..e0a706ff31d4d9e891a6a2d9417ccd689150aa94 100644 (file)
@@ -24,6 +24,6 @@
 extern ps_module ps_mod_files;
 #define ps_files_ptr &ps_mod_files
 
-PS_FUNCS_SID(files);
+PS_FUNCS_UPDATE_TIMESTAMP(files);
 
 #endif
index 47aafc8417a7799c61dc9226fd75c5373aaf6e5c..bb32a957dccbcb7ba55435c8311fe0741a3e85d2 100644 (file)
@@ -23,7 +23,7 @@
 #include "mod_user.h"
 
 ps_module ps_mod_user = {
-       PS_MOD_SID(user)
+       PS_MOD_UPDATE_TIMESTAMP(user)
 };
 
 #define SESS_ZVAL_LONG(val, a)                                         \
@@ -227,6 +227,42 @@ PS_CREATE_SID_FUNC(user)
        return php_session_create_id(mod_data);
 }
 
+PS_VALIDATE_SID_FUNC(user)
+{
+       /* maintain backwards compatibility */
+       if (!Z_ISUNDEF(PSF(validate_sid))) {
+               zval args[1];
+               STDVARS;
+
+               SESS_ZVAL_STR(key, &args[0]);
+
+               ps_call_handler(&PSF(validate_sid), 1, args, &retval);
+
+               FINISH;
+       }
+
+       /* dummy function defined by PS_MOD */
+       return php_session_validate_sid(mod_data, key TSRMLS_CC);
+}
+
+PS_UPDATE_TIMESTAMP_FUNC(user)
+{
+       zval args[2];
+       STDVARS;
+
+       SESS_ZVAL_STR(key, &args[0]);
+       SESS_ZVAL_STR(val, &args[1]);
+
+       /* maintain backwards compatibility */
+       if (!Z_ISUNDEF(PSF(update_timestamp))) {
+               ps_call_handler(&PSF(update_timestamp), 2, args, &retval);
+       } else {
+               ps_call_handler(&PSF(write), 2, args, &retval);
+       }
+
+       FINISH;
+}
+
 /*
  * Local variables:
  * tab-width: 4
index 1a315b70daaf2aa870fce1ef74a602697bbddf35..592b223c60422ac0b38579eeff7049d1817b91d2 100644 (file)
@@ -24,6 +24,6 @@
 extern ps_module ps_mod_user;
 #define ps_user_ptr &ps_mod_user
 
-PS_FUNCS_SID(user);
+PS_FUNCS_UPDATE_TIMESTAMP(user);
 
 #endif
index 419fd03f61e4b90e5d68ece840da3cebd1c89df0..328416c02e16194a50c2bdd5afb3c471d8cc2ca8 100644 (file)
@@ -155,3 +155,37 @@ PHP_METHOD(SessionHandler, create_sid)
        RETURN_STR(id);
 }
 /* }}} */
+
+/* {{{ proto char SessionUpdateTimestampHandler::validateId(string id)
+   Simply return TRUE */
+PHP_METHOD(SessionHandler, validateId)
+{
+       zend_string *key;
+
+       PS_SANITY_CHECK_IS_OPEN;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &key) == FAILURE) {
+               return;
+       }
+
+       /* Legacy save handler may not support validate_sid API. Return TRUE. */
+       RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool SessionUpdateTimestampHandler::updateTimestamp(string id, string data)
+   Simply call update_timestamp */
+PHP_METHOD(SessionHandler, updateTimestamp)
+{
+       zend_string *key, *val;
+
+       PS_SANITY_CHECK_IS_OPEN;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &key, &val) == FAILURE) {
+               return;
+       }
+
+       /* Legacy save handler may not support update_timestamp API. Just write. */
+       RETVAL_BOOL(SUCCESS == PS(default_mod)->s_write(&PS(mod_data), key, val));
+}
+/* }}} */
index c384b1f97bd5f2e522b06c193124f5fef4ae6db2..c5750066fd5c80eccbc011b249f52d2cd8c07db4 100644 (file)
 # include "ext/hash/php_hash.h"
 #endif
 
-#define PHP_SESSION_API 20020330
-
-/* To check php_session_valid_key()/php_session_reset_id() */
-#define PHP_SESSION_STRICT 1
-
-#define PS_OPEN_ARGS void **mod_data, const char *save_path, const char *session_name
-#define PS_CLOSE_ARGS void **mod_data
-#define PS_READ_ARGS void **mod_data, zend_string *key, zend_string **val
-#define PS_WRITE_ARGS void **mod_data, zend_string *key, zend_string *val
-#define PS_DESTROY_ARGS void **mod_data, zend_string *key
-#define PS_GC_ARGS void **mod_data, int maxlifetime, int *nrdels
+#define PHP_SESSION_API 20150121
+
+/* save handler macros */
+#define PS_NUM_APIS      9
+#define PS_OPEN_ARGS     void **mod_data, const char *save_path, const char *session_name
+#define PS_CLOSE_ARGS    void **mod_data
+#define PS_READ_ARGS     void **mod_data, zend_string *key, zend_string **val
+#define PS_WRITE_ARGS    void **mod_data, zend_string *key, zend_string *val
+#define PS_DESTROY_ARGS  void **mod_data, zend_string *key
+#define PS_GC_ARGS       void **mod_data, int maxlifetime, int *nrdels
 #define PS_CREATE_SID_ARGS void **mod_data
-
-/* default create id function */
-PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS);
+#define PS_VALIDATE_SID_ARGS void **mod_data, zend_string *key
+#define PS_UPDATE_TIMESTAMP_ARGS void **mod_data, zend_string *key, zend_string *val
 
 typedef struct ps_module_struct {
        const char *s_name;
@@ -52,6 +50,8 @@ typedef struct ps_module_struct {
        int (*s_destroy)(PS_DESTROY_ARGS);
        int (*s_gc)(PS_GC_ARGS);
        zend_string *(*s_create_sid)(PS_CREATE_SID_ARGS);
+       int (*s_validate_sid)(PS_VALIDATE_SID_ARGS);
+       int (*s_update_timestamp)(PS_UPDATE_TIMESTAMP_ARGS);
 } ps_module;
 
 #define PS_GET_MOD_DATA() *mod_data
@@ -64,7 +64,10 @@ typedef struct ps_module_struct {
 #define PS_DESTROY_FUNC(x)     int ps_delete_##x(PS_DESTROY_ARGS)
 #define PS_GC_FUNC(x)          int ps_gc_##x(PS_GC_ARGS)
 #define PS_CREATE_SID_FUNC(x)  zend_string *ps_create_sid_##x(PS_CREATE_SID_ARGS)
+#define PS_VALIDATE_SID_FUNC(x)        int ps_validate_sid_##x(PS_VALIDATE_SID_ARGS)
+#define PS_UPDATE_TIMESTAMP_FUNC(x)    int ps_update_timestamp_##x(PS_UPDATE_TIMESTAMP_ARGS)
 
+/* Legacy save handler module definitions */
 #define PS_FUNCS(x) \
        PS_OPEN_FUNC(x); \
        PS_CLOSE_FUNC(x); \
@@ -76,9 +79,10 @@ typedef struct ps_module_struct {
 
 #define PS_MOD(x) \
        #x, ps_open_##x, ps_close_##x, ps_read_##x, ps_write_##x, \
-        ps_delete_##x, ps_gc_##x, php_session_create_id
+        ps_delete_##x, ps_gc_##x, php_session_create_id, \
+        php_session_validate_sid, php_session_update_timestamp
 
-/* SID creation enabled module handler definitions */
+/* Legacy SID creation enabled save handler module definitions */
 #define PS_FUNCS_SID(x) \
        PS_OPEN_FUNC(x); \
        PS_CLOSE_FUNC(x); \
@@ -86,11 +90,33 @@ typedef struct ps_module_struct {
        PS_WRITE_FUNC(x); \
        PS_DESTROY_FUNC(x); \
        PS_GC_FUNC(x); \
-       PS_CREATE_SID_FUNC(x)
+       PS_CREATE_SID_FUNC(x); \
+       PS_VALIDATE_SID_FUNC(x); \
+       PS_UPDATE_TIMESTAMP_FUNC(x);
 
 #define PS_MOD_SID(x) \
        #x, ps_open_##x, ps_close_##x, ps_read_##x, ps_write_##x, \
-        ps_delete_##x, ps_gc_##x, ps_create_sid_##x
+        ps_delete_##x, ps_gc_##x, ps_create_sid_##x, \
+        php_session_validate_sid, php_session_update_timestamp
+
+/* Update timestamp enabled save handler module definitions
+   New save handlers should use this API */
+#define PS_FUNCS_UPDATE_TIMESTAMP(x) \
+       PS_OPEN_FUNC(x); \
+       PS_CLOSE_FUNC(x); \
+       PS_READ_FUNC(x); \
+       PS_WRITE_FUNC(x); \
+       PS_DESTROY_FUNC(x); \
+       PS_GC_FUNC(x); \
+       PS_CREATE_SID_FUNC(x); \
+       PS_VALIDATE_SID_FUNC(x); \
+       PS_UPDATE_TIMESTAMP_FUNC(x);
+
+#define PS_MOD_UPDATE_TIMESTAMP(x) \
+       #x, ps_open_##x, ps_close_##x, ps_read_##x, ps_write_##x, \
+        ps_delete_##x, ps_gc_##x, ps_create_sid_##x, \
+        ps_validate_sid_##x, ps_update_timestamp_##x
+
 
 typedef enum {
        php_session_disabled,
@@ -99,7 +125,6 @@ typedef enum {
 } php_session_status;
 
 typedef struct _php_session_rfc1867_progress {
-
        size_t    sname_len;
        zval      sid;
        smart_str key;
@@ -141,7 +166,7 @@ typedef struct _php_ps_globals {
        int module_number;
        zend_long cache_expire;
        union {
-               zval names[7];
+               zval names[PS_NUM_APIS];
                struct {
                        zval ps_open;
                        zval ps_close;
@@ -150,6 +175,8 @@ typedef struct _php_ps_globals {
                        zval ps_destroy;
                        zval ps_gc;
                        zval ps_create_sid;
+                       zval ps_validate_sid;
+                       zval ps_update_timestamp;
                } name;
        } mod_user_names;
        int mod_user_implemented;
@@ -169,7 +196,6 @@ typedef struct _php_ps_globals {
        zend_long hash_bits_per_character;
        int send_cookie;
        int define_sid;
-       zend_bool invalid_session_id;   /* allows the driver to report about an invalid session id and request id regeneration */
 
        php_session_rfc1867_progress *rfc1867_progress;
        zend_bool rfc1867_enabled; /* session.upload_progress.enabled */
@@ -180,7 +206,8 @@ typedef struct _php_ps_globals {
        double rfc1867_min_freq;   /* session.upload_progress.min_freq */
 
        zend_bool use_strict_mode; /* whether or not PHP accepts unknown session ids */
-       unsigned char session_data_hash[16]; /* binary MD5 hash length */
+       zend_bool lazy_write; /* omit session write when it is possible */
+       zend_string *session_vars; /* serialized original session data */
 } php_ps_globals;
 
 typedef php_ps_globals zend_ps_globals;
@@ -221,6 +248,12 @@ typedef struct ps_serializer_struct {
 #define PS_SERIALIZER_ENTRY(x) \
        { #x, PS_SERIALIZER_ENCODE_NAME(x), PS_SERIALIZER_DECODE_NAME(x) }
 
+/* default create id function */
+PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS);
+/* Dummy PS module functions */
+PHPAPI int php_session_validate_sid(PS_VALIDATE_SID_ARGS);
+PHPAPI int php_session_update_timestamp(PS_UPDATE_TIMESTAMP_ARGS);
+
 PHPAPI void session_adapt_url(const char *, size_t, char **, size_t *);
 
 PHPAPI void php_add_session_var(zend_string *name);
@@ -288,6 +321,9 @@ extern zend_class_entry *php_session_iface_entry;
 #define PS_SID_IFACE_NAME "SessionIdInterface"
 extern zend_class_entry *php_session_id_iface_entry;
 
+#define PS_UPDATE_TIMESTAMP_IFACE_NAME "SessionUpdateTimestampHandlerInterface"
+extern zend_class_entry *php_session_update_timestamp_iface_entry;
+
 extern PHP_METHOD(SessionHandler, open);
 extern PHP_METHOD(SessionHandler, close);
 extern PHP_METHOD(SessionHandler, read);
@@ -295,5 +331,7 @@ extern PHP_METHOD(SessionHandler, write);
 extern PHP_METHOD(SessionHandler, destroy);
 extern PHP_METHOD(SessionHandler, gc);
 extern PHP_METHOD(SessionHandler, create_sid);
+extern PHP_METHOD(SessionHandler, validateId);
+extern PHP_METHOD(SessionHandler, updateTimestamp);
 
 #endif
index ab328573be6772eaa50ea941e5d6402b073dcb94..c83191f05f619ed5e15592ff2266151210cd1a46 100644 (file)
@@ -74,6 +74,12 @@ zend_class_entry *php_session_iface_entry;
 /* SessionIdInterface */
 zend_class_entry *php_session_id_iface_entry;
 
+/* SessionUpdateTimestampHandler class */
+zend_class_entry *php_session_update_timestamp_class_entry;
+
+/* SessionUpdateTimestampInterface */
+zend_class_entry *php_session_update_timestamp_iface_entry;
+
 /* ***********
    * Helpers *
    *********** */
@@ -92,11 +98,13 @@ static void php_session_send_cookie(void);
 /* Dispatched by RINIT and by php_session_destroy */
 static inline void php_rinit_session_globals(void) /* {{{ */
 {
+       /* Do NOT init PS(mod_user_names) here! */
        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(define_sid) = 1;
+       PS(session_vars) = NULL;
        ZVAL_UNDEF(&PS(http_session_vars));
 }
 /* }}} */
@@ -104,11 +112,11 @@ static inline void php_rinit_session_globals(void) /* {{{ */
 /* Dispatched by RSHUTDOWN and by php_session_destroy */
 static inline void php_rshutdown_session_globals(void) /* {{{ */
 {
+       /* Do NOT destroy PS(mod_user_names) here! */
        if (!Z_ISUNDEF(PS(http_session_vars))) {
                zval_ptr_dtor(&PS(http_session_vars));
                ZVAL_UNDEF(&PS(http_session_vars));
        }
-       /* Do NOT destroy PS(mod_user_names) here! */
        if (PS(mod_data) || PS(mod_user_implemented)) {
                zend_try {
                        PS(mod)->s_close(&PS(mod_data));
@@ -117,6 +125,9 @@ static inline void php_rshutdown_session_globals(void) /* {{{ */
        if (PS(id)) {
                zend_string_release(PS(id));
        }
+       if (PS(session_vars)) {
+               zend_string_release(PS(session_vars));
+       }
 }
 /* }}} */
 
@@ -212,13 +223,13 @@ static zend_string *php_session_encode(void) /* {{{ */
 }
 /* }}} */
 
-static void php_session_decode(const char *val, int vallen) /* {{{ */
+static void php_session_decode(zend_string *data) /* {{{ */
 {
        if (!PS(serializer)) {
                php_error_docref(NULL, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object");
                return;
        }
-       if (PS(serializer)->decode(val, vallen) == FAILURE) {
+       if (PS(serializer)->decode(data->val, data->len) == FAILURE) {
                php_session_destroy();
                php_error_docref(NULL, E_WARNING, "Failed to decode session object. Session has been destroyed");
        }
@@ -321,8 +332,8 @@ PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
 #if defined(HAVE_HASH_EXT) && !defined(COMPILE_DL_HASH)
                case PS_HASH_FUNC_OTHER:
                        if (!PS(hash_ops)) {
-                               php_error_docref(NULL, E_ERROR, "Invalid session hash function");
                                efree(buf);
+                               php_error_docref(NULL, E_ERROR, "Invalid session hash function");
                                return NULL;
                        }
 
@@ -333,8 +344,8 @@ PHPAPI zend_string *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */
                        break;
 #endif /* HAVE_HASH_EXT */
                default:
-                       php_error_docref(NULL, E_ERROR, "Invalid session hash function");
                        efree(buf);
+                       php_error_docref(NULL, E_ERROR, "Invalid session hash function");
                        return NULL;
        }
        efree(buf);
@@ -468,7 +479,9 @@ static void php_session_initialize(void) /* {{{ */
        }
 
        /* Open session handler first */
-       if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE) {
+       if (PS(mod)->s_open(&PS(mod_data), PS(save_path), PS(session_name)) == FAILURE
+               /* || PS(mod_data) == NULL */ /* FIXME: open must set valid PS(mod_data) with success */
+       ) {
                php_error_docref(NULL, E_ERROR, "Failed to initialize storage module: %s (path: %s)", PS(mod)->s_name, PS(save_path));
                return;
        }
@@ -483,8 +496,20 @@ static void php_session_initialize(void) /* {{{ */
                if (PS(use_cookies)) {
                        PS(send_cookie) = 1;
                }
+       } else if (PS(use_strict_mode) && PS(mod)->s_validate_sid &&
+               PS(mod)->s_validate_sid(&PS(mod_data), PS(id)) == FAILURE) {
+               if (PS(id)) {
+                       zend_string_release(PS(id));
+               }
+               PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
+               if (!PS(id)) {
+                       PS(id) = php_session_create_id(NULL);
+               }
+               if (PS(use_cookies)) {
+                       PS(send_cookie) = 1;
+               }
        }
-
+       
        /* Set session ID for compatibility for older/3rd party save handlers */
        if (!PS(use_strict_mode)) {
                php_session_reset_id();
@@ -505,8 +530,15 @@ static void php_session_initialize(void) /* {{{ */
                php_session_reset_id();
                PS(session_status) = php_session_active;
        }
+       if (PS(session_vars)) {
+               zend_string_release(PS(session_vars));
+               PS(session_vars) = NULL;
+       }
        if (val) {
-               php_session_decode(val->val, val->len);
+               if (PS(lazy_write)) {
+                       PS(session_vars) = zend_string_copy(val);
+               }
+               php_session_decode(val);
                zend_string_release(val);
        }
 
@@ -529,7 +561,16 @@ static void php_session_save_current_state(void) /* {{{ */
 
                        val = php_session_encode();
                        if (val) {
-                               ret = PS(mod)->s_write(&PS(mod_data), PS(id), val);
+                               if (PS(lazy_write) && PS(session_vars)
+                                       && PS(mod)->s_update_timestamp
+                                       && PS(mod)->s_update_timestamp != php_session_update_timestamp
+                                       && val->len == PS(session_vars)->len
+                                       && !memcmp(val->val, PS(session_vars)->val, val->len)
+                               ) {
+                                       ret = PS(mod)->s_update_timestamp(&PS(mod_data), PS(id), val);
+                               } else {
+                                       ret = PS(mod)->s_write(&PS(mod_data), PS(id), val);
+                               }
                                zend_string_release(val);
                        } else {
                                ret = PS(mod)->s_write(&PS(mod_data), PS(id), STR_EMPTY_ALLOC());
@@ -786,6 +827,7 @@ PHP_INI_BEGIN()
        PHP_INI_ENTRY("session.use_trans_sid",          "0",         PHP_INI_ALL, OnUpdateTransSid)
        PHP_INI_ENTRY("session.hash_function",          "0",         PHP_INI_ALL, OnUpdateHashFunc)
        STD_PHP_INI_ENTRY("session.hash_bits_per_character", "4",    PHP_INI_ALL, OnUpdateLong,   hash_bits_per_character, php_ps_globals, ps_globals)
+       STD_PHP_INI_BOOLEAN("session.lazy_write",       "1",         PHP_INI_ALL, OnUpdateBool,   lazy_write,         php_ps_globals,    ps_globals)
 
        /* Upload progress */
        STD_PHP_INI_BOOLEAN("session.upload_progress.enabled",
@@ -813,9 +855,11 @@ PS_SERIALIZER_ENCODE_FUNC(php_serialize) /* {{{ */
        smart_str buf = {0};
        php_serialize_data_t var_hash;
 
-       PHP_VAR_SERIALIZE_INIT(var_hash);
-       php_var_serialize(&buf, Z_REFVAL(PS(http_session_vars)), &var_hash);
-       PHP_VAR_SERIALIZE_DESTROY(var_hash);
+       IF_SESSION_VARS() {
+               PHP_VAR_SERIALIZE_INIT(var_hash);
+               php_var_serialize(&buf, Z_REFVAL(PS(http_session_vars)), &var_hash);
+               PHP_VAR_SERIALIZE_DESTROY(var_hash);
+       }
        return buf.s;
 }
 /* }}} */
@@ -829,7 +873,7 @@ PS_SERIALIZER_DECODE_FUNC(php_serialize) /* {{{ */
 
        ZVAL_NULL(&session_vars);
        PHP_VAR_UNSERIALIZE_INIT(var_hash);
-       php_var_unserialize(&session_vars, (const unsigned char **)&val, endptr, &var_hash);
+       php_var_unserialize(&session_vars, (const unsigned char **)&val, (const unsigned char *)endptr, &var_hash);
        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
        if (!Z_ISUNDEF(PS(http_session_vars))) {
                zval_ptr_dtor(&PS(http_session_vars));
@@ -1053,7 +1097,7 @@ PHPAPI int php_session_register_serializer(const char *name, zend_string *(*enco
    * Storage Modules *
    ******************* */
 
-#define MAX_MODULES 10
+#define MAX_MODULES 32
 #define PREDEFINED_MODULES 2
 
 static ps_module *ps_modules[MAX_MODULES + 1] = {
@@ -1077,6 +1121,17 @@ PHPAPI int php_session_register_module(ps_module *ptr) /* {{{ */
 }
 /* }}} */
 
+/* Dummy PS module function */
+PHPAPI int php_session_validate_sid(PS_VALIDATE_SID_ARGS) {
+       return SUCCESS;
+}
+
+/* Dummy PS module function */
+PHPAPI int php_session_update_timestamp(PS_UPDATE_TIMESTAMP_ARGS) {
+       return SUCCESS;
+}
+
+
 /* ******************
    * Cache Limiters *
    ****************** */
@@ -1762,6 +1817,7 @@ static PHP_FUNCTION(session_set_save_handler)
                        RETURN_FALSE;
                }
 
+               /* For compatibility reason, implemeted interface is not checked */
                /* Find implemented methods - SessionHandlerInterface */
                i = 0;
                ZEND_HASH_FOREACH_STR_KEY(&php_session_iface_entry->function_table, func_name) {
@@ -1788,16 +1844,39 @@ static PHP_FUNCTION(session_set_save_handler)
                                if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
                                        zval_ptr_dtor(&PS(mod_user_names).names[i]);
                                }
-
                                array_init_size(&PS(mod_user_names).names[i], 2);
                                Z_ADDREF_P(obj);
                                add_next_index_zval(&PS(mod_user_names).names[i], obj);
                                add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
+                       } else {
+                               if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
+                                       zval_ptr_dtor(&PS(mod_user_names).names[i]);
+                                       ZVAL_UNDEF(&PS(mod_user_names).names[i]);
+                               }
                        }
 
                        ++i;
                } ZEND_HASH_FOREACH_END();
 
+               /* Find implemented methods - SessionUpdateTimestampInterface (optional) */
+               ZEND_HASH_FOREACH_STR_KEY(&php_session_update_timestamp_iface_entry->function_table, func_name) {
+                       if ((current_mptr = zend_hash_find_ptr(&Z_OBJCE_P(obj)->function_table, func_name))) {
+                               if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
+                                       zval_ptr_dtor(&PS(mod_user_names).names[i]);
+                               }
+                               array_init_size(&PS(mod_user_names).names[i], 2);
+                               Z_ADDREF_P(obj);
+                               add_next_index_zval(&PS(mod_user_names).names[i], obj);
+                               add_next_index_str(&PS(mod_user_names).names[i], zend_string_copy(func_name));
+                       } else {
+                               if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
+                                       zval_ptr_dtor(&PS(mod_user_names).names[i]);
+                                       ZVAL_UNDEF(&PS(mod_user_names).names[i]);
+                               }
+                       }
+                       ++i;
+               } ZEND_HASH_FOREACH_END();
+
                if (register_shutdown) {
                        /* create shutdown function */
                        php_shutdown_function_entry shutdown_function_entry;
@@ -1818,7 +1897,7 @@ static PHP_FUNCTION(session_set_save_handler)
                        remove_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1);
                }
 
-               if (PS(mod) && PS(session_status) == php_session_none && PS(mod) != &ps_mod_user) {
+               if (PS(mod) && PS(session_status) != php_session_active && PS(mod) != &ps_mod_user) {
                        ini_name = zend_string_init("session.save_handler", sizeof("session.save_handler") - 1, 0);
                        ini_val = zend_string_init("user", sizeof("user") - 1, 0);
                        zend_alter_ini_entry(ini_name, ini_val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
@@ -1829,7 +1908,8 @@ static PHP_FUNCTION(session_set_save_handler)
                RETURN_TRUE;
        }
 
-       if (argc != 6 && argc != 7) {
+       /* Set procedural save handler functions */
+       if (argc < 6 || PS_NUM_APIS < argc) {
                WRONG_PARAM_COUNT;
        }
 
@@ -1840,7 +1920,7 @@ static PHP_FUNCTION(session_set_save_handler)
        /* remove shutdown function */
        remove_user_shutdown_function("session_shutdown", sizeof("session_shutdown") - 1);
 
-       /* at this point argc can only be 6 or 7 */
+       /* At this point argc can only be between 6 and PS_NUM_APIS */
        for (i = 0; i < argc; i++) {
                if (!zend_is_callable(&args[i], 0, &name)) {
                        php_error_docref(NULL, E_WARNING, "Argument %d is not a valid callback", i+1);
@@ -1950,6 +2030,7 @@ static PHP_FUNCTION(session_regenerate_id)
                                RETURN_FALSE;
                        }
                        zend_string_release(PS(id));
+                       PS(id) = NULL;
                }
 
                PS(id) = PS(mod)->s_create_sid(&PS(mod_data));
@@ -1965,6 +2046,47 @@ static PHP_FUNCTION(session_regenerate_id)
 }
 /* }}} */
 
+/* {{{ proto void session_create_id([string prefix])
+   Generate new session ID. Intended for user save handlers. */
+static PHP_FUNCTION(session_create_id)
+{
+       zend_string *prefix = NULL, *new_id;
+       smart_str id = {0};
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S", &prefix) == FAILURE) {
+               return;
+       }
+
+       if (prefix && prefix->len) {
+               if (php_session_valid_key(prefix->val) == FAILURE) {
+                       /* E_ERROR raised for security reason. */
+                       php_error_docref(NULL, E_WARNING, "Prefix cannot contain special characters. Only aphanumeric, ',', '-' are allowed");
+                       RETURN_FALSE;
+               } else {
+                       smart_str_append(&id, prefix);
+               }
+       }
+
+       if (PS(session_status) == php_session_active) {
+               new_id = PS(mod)->s_create_sid(&PS(mod_data));
+       } else {
+               new_id = php_session_create_id(NULL);
+       }
+
+       if (new_id) {
+               smart_str_append(&id, new_id);
+               zend_string_release(new_id);
+       } else {
+               smart_str_free(&id);
+               php_error_docref(NULL, E_WARNING, "Failed to create new ID");
+               RETURN_FALSE;
+       }
+       smart_str_0(&id);
+       RETVAL_STR(id.s);
+       smart_str_free(&id);
+}
+/* }}} */
+
 /* {{{ proto string session_cache_limiter([string new_cache_limiter])
    Return the current cache limiter. If new_cache_limited is given, the current cache_limiter is replaced with new_cache_limiter */
 static PHP_FUNCTION(session_cache_limiter)
@@ -2031,33 +2153,83 @@ static PHP_FUNCTION(session_encode)
    Deserializes data and reinitializes the variables */
 static PHP_FUNCTION(session_decode)
 {
-       char *str;
-       size_t str_len;
+       zend_string *str = NULL;
 
-       if (PS(session_status) == php_session_none) {
+       if (PS(session_status) != php_session_active) {
                RETURN_FALSE;
        }
 
-       if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len) == FAILURE) {
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
                return;
        }
 
-       php_session_decode(str, str_len);
+       php_session_decode(str);
 
        RETURN_TRUE;
 }
 /* }}} */
 
-/* {{{ proto bool session_start(void)
-   Begin session - reinitializes freezed variables, registers browsers etc */
+static int php_session_start_set_ini(zend_string *varname, zend_string *new_value) {
+       int ret;
+       smart_str buf ={0};
+       smart_str_appends(&buf, "session");
+       smart_str_appendc(&buf, '.');
+       smart_str_append(&buf, varname);
+       smart_str_0(&buf);
+       ret = zend_alter_ini_entry_ex(buf.s, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0);
+       smart_str_free(&buf);
+       return ret;
+}
+
+/* {{{ proto bool session_start([array options])
++   Begin session */
 static PHP_FUNCTION(session_start)
 {
-       /* skipping check for non-zero args for performance reasons here ?*/
+       zval *options = NULL;
+       zval *value;
+       zend_ulong num_idx;
+       zend_string *str_idx;
+       int read_and_close = 0;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a", &options) == FAILURE) {
+               RETURN_FALSE;
+       }
+
+       /* set options */
+       if (options) {
+               ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), num_idx, str_idx, value) {
+                       switch(Z_TYPE_P(value)) {
+                               case IS_STRING:
+                               case IS_TRUE:
+                               case IS_FALSE:
+                               case IS_LONG:
+                                       if (!zend_string_equals_literal(str_idx, "read_and_close")) {
+                                               convert_to_boolean(value);
+                                               read_and_close = (Z_TYPE_P(value) == IS_TRUE) ? 1 : 0;
+                                       } else {
+                                               convert_to_string(value);
+                                               if (php_session_start_set_ini(str_idx, Z_STR_P(value)) == FAILURE) {
+                                                       php_error_docref(NULL, E_WARNING, "Setting option '%s' failed", str_idx->val);
+                                               }
+                                       }
+                                       break;
+                               default:
+                                       php_error_docref(NULL, E_WARNING, "Option(%s) value must be string, boolean or long", str_idx->val);
+                                       break;
+                       }
+               } ZEND_HASH_FOREACH_END();
+       }
+
        php_session_start();
 
        if (PS(session_status) != php_session_active) {
                RETURN_FALSE;
        }
+
+       if (read_and_close) {
+               php_session_flush();
+       }
+
        RETURN_TRUE;
 }
 /* }}} */
@@ -2078,7 +2250,7 @@ static PHP_FUNCTION(session_destroy)
    Unset all registered variables */
 static PHP_FUNCTION(session_unset)
 {
-       if (PS(session_status) == php_session_none) {
+       if (PS(session_status) != php_session_active) {
                RETURN_FALSE;
        }
 
@@ -2197,6 +2369,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_session_set_save_handler, 0, 0, 1)
        ZEND_ARG_INFO(0, destroy)
        ZEND_ARG_INFO(0, gc)
        ZEND_ARG_INFO(0, create_sid)
+       ZEND_ARG_INFO(0, validate_sid)
+       ZEND_ARG_INFO(0, update_timestamp)
 ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO_EX(arginfo_session_cache_limiter, 0, 0, 0)
@@ -2242,6 +2416,15 @@ ZEND_END_ARG_INFO()
 
 ZEND_BEGIN_ARG_INFO(arginfo_session_class_create_sid, 0)
 ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_validateId, 0)
+       ZEND_ARG_INFO(0, key)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_session_class_updateTimestamp, 0)
+       ZEND_ARG_INFO(0, key)
+       ZEND_ARG_INFO(0, val)
+ZEND_END_ARG_INFO()
 /* }}} */
 
 /* {{{ session_functions[]
@@ -2293,6 +2476,15 @@ static const zend_function_entry php_session_id_iface_functions[] = {
 };
 /* }}} */
 
+/* {{{ SessionUpdateTimestampHandler functions[]
+ */
+static const zend_function_entry php_session_update_timestamp_iface_functions[] = {
+       PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, validateId, arginfo_session_class_validateId)
+       PHP_ABSTRACT_ME(SessionUpdateTimestampHandlerInterface, updateTimestamp, arginfo_session_class_updateTimestamp)
+       { NULL, NULL, NULL }
+};
+/* }}} */
+
 /* {{{ SessionHandler functions[]
  */
 static const zend_function_entry php_session_class_functions[] = {
@@ -2362,7 +2554,7 @@ static PHP_RSHUTDOWN_FUNCTION(session) /* {{{ */
        php_rshutdown_session_globals();
 
        /* this should NOT be done in php_rshutdown_session_globals() */
-       for (i = 0; i < 7; i++) {
+       for (i = 0; i < PS_NUM_APIS; i++) {
                if (!Z_ISUNDEF(PS(mod_user_names).names[i])) {
                        zval_ptr_dtor(&PS(mod_user_names).names[i]);
                        ZVAL_UNDEF(&PS(mod_user_names).names[i]);
@@ -2391,7 +2583,8 @@ static PHP_GINIT_FUNCTION(ps) /* {{{ */
        ps_globals->default_mod = NULL;
        ps_globals->mod_user_implemented = 0;
        ps_globals->mod_user_is_open = 0;
-       for (i = 0; i < 7; i++) {
+       ps_globals->session_vars = NULL;
+       for (i = 0; i < PS_NUM_APIS; i++) {
                ZVAL_UNDEF(&ps_globals->mod_user_names.names[i]);
        }
        ZVAL_UNDEF(&ps_globals->http_session_vars);
@@ -2424,6 +2617,10 @@ static PHP_MINIT_FUNCTION(session) /* {{{ */
        php_session_id_iface_entry = zend_register_internal_class(&ce);
        php_session_id_iface_entry->ce_flags |= ZEND_ACC_INTERFACE;
 
+       INIT_CLASS_ENTRY(ce, PS_UPDATE_TIMESTAMP_IFACE_NAME, php_session_update_timestamp_iface_functions);
+       php_session_update_timestamp_iface_entry = zend_register_internal_class(&ce TSRMLS_CC);
+       php_session_update_timestamp_iface_entry->ce_flags |= ZEND_ACC_INTERFACE;
+
        /* Register base class */
        INIT_CLASS_ENTRY(ce, PS_CLASS_NAME, php_session_class_functions);
        php_session_class_entry = zend_register_internal_class(&ce);
index d271748259147b86606187e5f7cd53478f026a6e..79ebaaabb0d9f0775125f16a70ee5d44fc9286ee 100644 (file)
@@ -20,6 +20,8 @@ function read($id) {
     $session_id = $id;
     echo "Read [${session_save_path},${id}]\n";
     $session_file = "$session_save_path/".SESSION_FILE_PREFIX.$id;
+    // read MUST create file. Otherwise, strict mode will not work
+    touch($session_file);
     return (string) @file_get_contents($session_file);
 }
 
@@ -31,7 +33,7 @@ function write($id, $session_data) {
     if ($fp = fopen($session_file, "w")) {
         $return = fwrite($fp, $session_data);
         fclose($fp);
-        return (bool)$return;
+        return $return === FALSE ? FALSE : TRUE;
     }
     return false;
 }
@@ -60,5 +62,42 @@ function gc($maxlifetime) {
     return true;
 }
 
+function create_sid() {
+    $id = ('PHPT-'.time());
+    echo "CreateID [${id}]\n";
+    return $id;
+}
+
+function validate_sid($id) {
+    global $session_save_path, $name;
+    echo "ValidateID [${session_save_path},${id}]\n";
+    $session_file = "$session_save_path/".SESSION_FILE_PREFIX.$id;
+    $ret = file_exists($session_file);
+    return $ret;
+}
+
+function update($id, $session_data) {
+    global $session_save_path, $name;
+    echo "Update [${session_save_path},${id}]\n";
+    $session_file = "$session_save_path/".SESSION_FILE_PREFIX.$id;
+    $ret = touch($session_file);
+    return $ret;
+}
+
+
+function feature() {
+    /* NOT IMPLEMENTED YET */
+    /* TYPES: gc, create_sid, use_strict_mode, minizie_lock, lazy_write
+    /* VALUES: 0=unknown, 1=supported, 2=partially supported, 3=unsupported */
+    return array('gc'=>0,
+                 'create_sid'=>1,
+                 'use_strict_mode'=>2,
+                 'minimize_lock'=>3,
+                 'lazy_write'=>4,
+                 'invalid'=>5,
+                 'another invalid'=>6
+                 );
+}
+
 ?>
 
diff --git a/ext/session/tests/session_basic1.phpt b/ext/session/tests/session_basic1.phpt
new file mode 100644 (file)
index 0000000..616fdfc
--- /dev/null
@@ -0,0 +1,67 @@
+--TEST--
+Test basic function : variation1
+--INI--
+session.use_strict_mode=0
+session.save_handler=files
+session.gc_probability=1
+session.gc_divisor=1000
+session.gc_maxlifetime=300
+session.save_path=
+session.name=PHPSESSID
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : session.use_strict_mode=0
+ * Description : Test basic functionality.
+ * Source code : ext/session/session.c, ext/session/mod_files.c
+ */
+
+echo "*** Testing basic session functionality : variation1 ***\n";
+
+$session_id = 'testid';
+session_id($session_id);
+$path = dirname(__FILE__);
+var_dump(session_save_path($path));
+
+echo "*** Without lazy_write ***\n";
+var_dump(session_id($session_id));
+var_dump(session_start(['lazy_write'=>FALSE]));
+var_dump(session_write_close());
+var_dump(session_id());
+
+echo "*** With lazy_write ***\n";
+var_dump(session_id($session_id));
+var_dump(session_start(['lazy_write'=>TRUE]));
+var_dump(session_commit());
+var_dump(session_id());
+
+echo "*** Cleanup ***\n";
+var_dump(session_id($session_id));
+var_dump(session_start());
+var_dump(session_destroy());
+
+ob_end_flush();
+?>
+--EXPECT--
+*** Testing basic session functionality : variation1 ***
+string(0) ""
+*** Without lazy_write ***
+string(6) "testid"
+bool(true)
+NULL
+string(6) "testid"
+*** With lazy_write ***
+string(6) "testid"
+bool(true)
+NULL
+string(6) "testid"
+*** Cleanup ***
+string(6) "testid"
+bool(true)
+bool(true)
+
diff --git a/ext/session/tests/session_basic2.phpt b/ext/session/tests/session_basic2.phpt
new file mode 100644 (file)
index 0000000..179b829
--- /dev/null
@@ -0,0 +1,83 @@
+--TEST--
+Test basic function : variation2
+--INI--
+session.use_strict_mode=1
+session.save_handler=files
+session.hash_bits_per_character=4
+session.hash_function=0
+session.gc_probability=1
+session.gc_divisor=1000
+session.gc_maxlifetime=300
+session.save_path=
+session.name=PHPSESSID
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : session.use_strict_mode=1
+ * Description : Test basic functionality.
+ * Source code : ext/session/session.c, ext/session/mod_files.c
+ */
+
+echo "*** Testing basic session functionality : variation2 ***\n";
+
+$session_id = 'testid';
+session_id($session_id);
+$path = dirname(__FILE__);
+var_dump(session_save_path($path));
+
+echo "*** Without lazy_write ***\n";
+var_dump(session_id($session_id));
+var_dump(session_start(['lazy_write'=>FALSE]));
+$session_id_new1 = session_id();
+var_dump($session_id_new1 !== $session_id);
+var_dump(session_write_close());
+var_dump(session_id());
+
+echo "*** With lazy_write ***\n";
+var_dump(session_id($session_id));
+var_dump(session_start(['lazy_write'=>TRUE]));
+$session_id_new2 = session_id();
+var_dump($session_id_new1 !== $session_id_new2);
+var_dump(session_commit());
+var_dump(session_id());
+
+echo "*** Cleanup ***\n";
+ini_set('session.use_strict_mode',0);
+var_dump(session_id($session_id_new1));
+var_dump(session_start());
+var_dump(session_destroy());
+var_dump(session_id($session_id_new2));
+var_dump(session_start());
+var_dump(session_destroy());
+
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing basic session functionality : variation2 ***
+string(0) ""
+*** Without lazy_write ***
+string(6) "testid"
+bool(true)
+bool(true)
+NULL
+string(32) "%s"
+*** With lazy_write ***
+string(32) "%s"
+bool(true)
+bool(true)
+NULL
+string(32) "%s"
+*** Cleanup ***
+string(32) "%s"
+bool(true)
+bool(true)
+string(0) ""
+bool(true)
+bool(true)
+
+
index 4cee2f4d58010f6a3518efa9a92bfa1148f5dc7c..998e60340f35010033a41568e4df69153b49f709 100644 (file)
@@ -1,5 +1,5 @@
 --TEST--
-Test session_start() function : variation
+Test session_commit() function : variation
 --SKIPIF--
 <?php include('skipif.inc'); ?>
 --INI--
index 69854a6cf99e5fd19c872e7206c9870a5b7590f6..fdc4ca51864a1db7232fda5d1313012aa76b0ec2 100644 (file)
@@ -9,20 +9,22 @@ session.use_strict_mode=0
 
 ob_start();
 
-/* 
+/*
  * Prototype : bool session_commit(void)
  * Description : Write session data and end session
- * Source code : ext/session/session.c 
+ * Source code : ext/session/session.c
  */
 
 echo "*** Testing session_commit() : variation ***\n";
 
+var_dump(ini_get('session.use_strict_mode'));
 var_dump(session_id("test"));
 var_dump(session_start());
 var_dump(session_id());
 var_dump(session_commit());
 var_dump(session_id());
 var_dump(session_start());
+var_dump(ini_get('session.use_strict_mode'));
 var_dump(session_id());
 var_dump(session_commit());
 var_dump(session_id());
@@ -32,18 +34,21 @@ var_dump(session_commit());
 var_dump(session_id());
 var_dump(session_start());
 var_dump(session_destroy());
+var_dump(ini_get('session.use_strict_mode'));
 
 echo "Done";
 ob_end_flush();
 ?>
 --EXPECTF--
 *** Testing session_commit() : variation ***
+string(1) "0"
 string(0) ""
 bool(true)
 string(4) "test"
 NULL
 string(4) "test"
 bool(true)
+string(1) "0"
 string(4) "test"
 NULL
 string(4) "test"
@@ -53,5 +58,6 @@ NULL
 string(4) "test"
 bool(true)
 bool(true)
+string(1) "0"
 Done
 
diff --git a/ext/session/tests/session_commit_variation5.phpt b/ext/session/tests/session_commit_variation5.phpt
new file mode 100644 (file)
index 0000000..62bd1c1
--- /dev/null
@@ -0,0 +1,70 @@
+--TEST--
+Test session_commit() function : variation
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--INI--
+session.use_strict_mode=0
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : bool session_commit(void)
+ * Description : Write session data and end session
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing session_commit() : variation ***\n";
+
+$id = md5(uniqid());
+var_dump(session_id($id));
+var_dump(session_start());
+var_dump(session_id());
+var_dump($id === session_id());
+var_dump(session_commit());
+var_dump($id === session_id());
+var_dump(session_id());
+var_dump(session_start());
+var_dump($id === session_id());
+var_dump(session_id());
+var_dump(session_commit());
+var_dump($id === session_id());
+var_dump(session_id());
+var_dump(session_start());
+var_dump($id === session_id());
+var_dump(session_id());
+var_dump(session_commit());
+var_dump($id === session_id());
+var_dump(session_id());
+var_dump(session_start());
+var_dump(session_destroy());
+
+echo "Done";
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing session_commit() : variation ***
+string(0) ""
+bool(true)
+string(32) "%s"
+bool(true)
+NULL
+bool(true)
+string(32) "%s"
+bool(true)
+bool(true)
+string(32) "%s"
+NULL
+bool(true)
+string(32) "%s"
+bool(true)
+bool(true)
+string(32) "%s"
+NULL
+bool(true)
+string(32) "%s"
+bool(true)
+bool(true)
+Done
+
index 4a6f7687139508e27f41b706c0a61aca459ce61d..3557efa46d645b8143970887bdabb1ad93f8a10e 100644 (file)
@@ -48,8 +48,8 @@ array(3) {
   float(123.456)
 }
 
-Warning: session_decode(): Unknown session.serialize_handler. Failed to decode session object in %s on line %d
-bool(true)
+Warning: session_decode(): Session is not active. You cannot decode session data in %s on line %d
+bool(false)
 array(3) {
   ["foo"]=>
   int(1234567890)
index e8496e8afb72e4782afa5ffaafabb5e28f55e9f9..c8b406e5ab1128cd35954fa6f1fcc82a0b28992a 100644 (file)
@@ -46,6 +46,11 @@ var_dump($_SESSION);
 $_SESSION['Bar'] = 'Foo';
 session_write_close();
 
+echo "Cleanup..\n";
+session_id($session_id);
+session_start();
+session_destroy();
+
 ob_end_flush();
 ?>
 --EXPECTF--
@@ -94,3 +99,8 @@ array(3) {
 }
 Write [%s,%s,Blah|s:12:"Hello World!";Foo|b:0;Guff|i:1234567890;Bar|s:3:"Foo";]
 Close [%s,PHPSESSID]
+Cleanup..
+Open [%s,PHPSESSID]
+Read [%s,%s]
+Destroy [%s,%s]
+Close [%s,PHPSESSID]
index 83e899a2bc1e0df2b5e3c455d7a277834b5cb2ed..f1656c3b5bcd6a0ba166cede7f06489bb96af19c 100644 (file)
@@ -1,6 +1,7 @@
 --TEST--
 Test session_set_save_handler() : basic class wrapping existing handler
 --INI--
+session.use_strict_mode=1
 session.save_handler=files
 session.name=PHPSESSID
 --SKIPIF--
@@ -25,11 +26,51 @@ class MySession extends SessionHandler {
                echo 'Open ', session_id(), "\n";
                return parent::open($path, $name);
        }
+       public function create_sid() {
+               // This method should be removed when 5.5 become unsupported.
+               ++$this->i;
+               echo 'Old Create SID ', session_id(), "\n";
+               return parent::create_sid();
+       }
        public function read($key) {
                ++$this->i;
                echo 'Read ', session_id(), "\n";
                return parent::read($key);
        }
+       public function write($key, $data) {
+               ++$this->i;
+               echo 'Write ', session_id(), "\n";
+               return parent::write($key, $data);
+       }
+       public function close() {
+               ++$this->i;
+               echo 'Close ', session_id(), "\n";
+               return parent::close();
+       }
+       public function createSid() {
+               // User should use this rather than create_sid()
+               // If both create_sid() and createSid() exists,
+               // createSid() is used.
+               ++$this->i;
+               echo 'New Create ID ', session_id(), "\n";
+               return parent::create_sid();
+       }
+       public function validateId($key) {
+               ++$this->i;
+               echo 'Validate ID ', session_id(), "\n";
+               return TRUE;
+               // User must implement their own method and
+               // cannot call parent as follows.
+               // return parent::validateSid($key);
+       }
+       public function updateTimestamp($key, $data) {
+               ++$this->i;
+               echo 'Update Timestamp ', session_id(), "\n";
+               return parent::write($key, $data);
+               // User must implement their own method and
+               // cannot call parent as follows
+               // return parent::updateTimestamp($key, $data);
+       }
 }
 
 $oldHandler = ini_get('session.save_handler');
@@ -49,20 +90,28 @@ var_dump($_SESSION);
 
 session_write_close();
 session_unset();
+var_dump($handler->i);
 
 --EXPECTF--
 *** Testing session_set_save_handler() : basic class wrapping existing handler ***
 Open 
+Old Create SID 
 Read %s
 string(%d) "%s"
 string(5) "files"
 string(4) "user"
-int(2)
+int(3)
 array(0) {
 }
+Write %s
+Close %s
 Open %s
+Validate ID %s
 Read %s
 array(1) {
   ["foo"]=>
   string(5) "hello"
 }
+Update Timestamp %s
+Close %s
+int(10)
diff --git a/ext/session/tests/session_set_save_handler_class_018.phpt b/ext/session/tests/session_set_save_handler_class_018.phpt
new file mode 100644 (file)
index 0000000..7ff06e3
--- /dev/null
@@ -0,0 +1,94 @@
+--TEST--
+Test session_set_save_handler() function: class with validate_sid
+--INI--
+session.save_handler=files
+session.name=PHPSESSID
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : bool session_set_save_handler(SessionHandlerInterface $handler [, bool $register_shutdown_function = true])
+ * Description : Sets user-level session storage functions
+ * Source code : ext/session/session.c
+ */
+
+echo "*** Testing session_set_save_handler() function: class with validate_sid ***\n";
+
+class MySession2 extends SessionHandler {
+       public $path;
+
+       public function open($path, $name) {
+               if (!$path) {
+                       $path = sys_get_temp_dir();
+               }
+               $this->path = $path . '/u_sess_' . $name;
+               return true;
+       }
+
+       public function close() {
+               return true;
+       }
+
+       public function read($id) {
+               return @file_get_contents($this->path . $id);
+       }
+
+       public function write($id, $data) {
+               return file_put_contents($this->path . $id, $data)===FALSE ? FALSE : TRUE ;
+       }
+
+       public function destroy($id) {
+               @unlink($this->path . $id);
+       }
+
+       public function gc($maxlifetime) {
+               foreach (glob($this->path . '*') as $filename) {
+                       if (filemtime($filename) + $maxlifetime < time()) {
+                               @unlink($filename);
+                       }
+               }
+               return true;
+       }
+
+       public function create_sid() {
+               return 'my_sid';
+       }
+
+       public function validate_sid($id) {
+               return 'my_sid'===$id;
+       }
+}
+
+$handler = new MySession2;
+session_set_save_handler($handler);
+session_start();
+
+$_SESSION['foo'] = "hello";
+
+var_dump(session_id(), ini_get('session.save_handler'), $_SESSION);
+
+session_write_close();
+session_unset();
+
+session_start();
+var_dump($_SESSION);
+
+session_write_close();
+session_unset();
+
+--EXPECTF--
+*** Testing session_set_save_handler() function: class with validate_sid ***
+string(%d) "my_sid"
+string(4) "user"
+array(1) {
+  ["foo"]=>
+  string(5) "hello"
+}
+array(1) {
+  ["foo"]=>
+  string(5) "hello"
+}
index c34eb9cd9fb041f03283d620483fbed0774c1c47..56b8a67f2a9db45fc06cdc14d24bdff2f8fe1890 100644 (file)
@@ -23,7 +23,7 @@ echo "*** Testing session_set_save_handler() : variation ***\n";
 
 function noisy_gc($maxlifetime) {
        echo("GC [".$maxlifetime."]\n");
-       gc($maxlifetime);
+       echo gc($maxlifetime)." deleted\n";
        return true;
 }
 
@@ -54,6 +54,7 @@ ob_end_flush();
 Open [%s,PHPSESSID]
 Read [%s,%s]
 GC [0]
+1 deleted
 array(3) {
   ["Blah"]=>
   string(12) "Hello World!"
@@ -68,6 +69,7 @@ NULL
 Open [%s,PHPSESSID]
 Read [%s,%s]
 GC [0]
+1 deleted
 array(3) {
   ["Blah"]=>
   string(12) "Hello World!"
diff --git a/ext/session/tests/session_set_save_handler_variation5.phpt b/ext/session/tests/session_set_save_handler_variation5.phpt
new file mode 100644 (file)
index 0000000..e1c0de4
--- /dev/null
@@ -0,0 +1,100 @@
+--TEST--
+Test session_set_save_handler() function : variation
+--INI--
+session.use_strict_mode=1
+session.gc_probability=1
+session.gc_divisor=1
+session.gc_maxlifetime=0
+session.save_path=
+session.name=PHPSESSID
+--SKIPIF--
+<?php include('skipif.inc'); ?>
+--FILE--
+<?php
+
+ob_start();
+
+/*
+ * Prototype : bool session_set_save_handler(callback $open, callback $close, callback $read, callback $write, callback $destroy, callback $gc)
+ * Description : Sets user-level session storage functions with validate_id() and update()
+ * Source code : ext/session/session.c
+ */
+
+function noisy_gc($maxlifetime) {
+       echo("GC [".$maxlifetime."]\n");
+       echo gc($maxlifetime)." deleted\n";
+       return true;
+}
+
+echo "*** Testing session_set_save_handler() : variation ***\n";
+
+require_once "save_handler.inc";
+$session_id = 'testid';
+$path = dirname(__FILE__);
+var_dump(session_save_path($path));
+
+echo "*** Without lazy_write ***\n";
+var_dump(session_id($session_id));
+var_dump(session_set_save_handler("open", "close", "read", "write", "destroy", "noisy_gc", "create_sid", "validate_sid", "update"));
+var_dump(session_start(['lazy_write'=>FALSE]));
+var_dump(session_write_close());
+var_dump(session_id());
+
+echo "*** With lazy_write ***\n";
+var_dump(session_id($session_id));
+var_dump(session_set_save_handler("open", "close", "read", "write", "destroy", "noisy_gc", "create_sid", "validate_sid", "update"));
+var_dump(session_start(['lazy_write'=>TRUE]));
+var_dump(session_commit());
+var_dump(session_id());
+
+echo "*** Cleanup ***\n";
+var_dump(session_id($session_id));
+var_dump(session_start());
+var_dump(session_destroy());
+
+ob_end_flush();
+?>
+--EXPECTF--
+*** Testing session_set_save_handler() : variation ***
+
+string(0) ""
+*** Without lazy_write ***
+string(0) ""
+bool(true)
+Open [%s,PHPSESSID]
+ValidateID [%s,testid]
+CreateID [PHPT-%d]
+Read [%s,PHPT-%d]
+GC [0]
+1 deleted
+bool(true)
+Write [%s,PHPT-%d,]
+Close [%s,PHPSESSID]
+NULL
+string(15) "PHPT-%d"
+*** With lazy_write ***
+string(15) "PHPT-%d"
+bool(true)
+Open [%s,PHPSESSID]
+ValidateID [%s,PHPT-%d]
+Read [%s,PHPT-%d]
+GC [0]
+1 deleted
+Write [%s,PHPT-%d,]
+Close [%s,PHPSESSID]
+bool(true)
+NULL
+string(15) "PHPT-%d"
+*** Cleanup ***
+string(15) "PHPT-%d"
+Open [%s,PHPSESSID]
+ValidateID [%s,PHPT-%d]
+Read [%s,PHPT-%d]
+GC [0]
+1 deleted
+bool(true)
+Destroy [%s,PHPT-%d]
+
+Warning: unlink(%s/session_test_PHPT-%s): No such file or directory in %s/save_handler.inc on line %d
+Close [%s,PHPSESSID]
+bool(true)
similarity index 88%
rename from ext/session/tests/session_set_save_handler_write_short_circuit.phpt
rename to ext/session/tests/session_set_save_handler_variation6.phpt
index 08da29a8fdeb33bcd95684269cfb4e0a8de32f22..159c85d6315c1828f110ddaf787522ab8a50dfd6 100644 (file)
@@ -1,11 +1,11 @@
 --TEST--
-Test session_set_save_handler() function : test write short circuit
+Test session_set_save_handler() function : test lazy_write
 --INI--
+session.lazy_write=1
 session.save_path=
 session.name=PHPSESSID
 --SKIPIF--
 <?php include('skipif.inc'); ?>
-skip - Waiting RFC patch merge
 --FILE--
 <?php
 
@@ -22,7 +22,7 @@ echo "*** Testing session_set_save_handler() : test write short circuit ***\n";
 require_once "save_handler.inc";
 $path = dirname(__FILE__);
 session_save_path($path);
-session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
+session_set_save_handler("open", "close", "read", "write", "destroy", "gc", "create_sid", "validate_sid", "update");
 
 session_start();
 $session_id = session_id();
@@ -37,7 +37,7 @@ var_dump($_SESSION);
 
 echo "Starting session again..!\n";
 session_id($session_id);
-session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
+session_set_save_handler("open", "close", "read", "write", "destroy", "gc", "create_sid", "validate_sid", "update");
 session_start();
 var_dump($_SESSION);
 $_SESSION['Bar'] = 'Foo';
@@ -45,7 +45,7 @@ session_write_close();
 
 echo "Starting session again..!\n";
 session_id($session_id);
-session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
+session_set_save_handler("open", "close", "read", "write", "destroy", "gc", "create_sid", "validate_sid", "update");
 session_start();
 var_dump($_SESSION);
 // $_SESSION should be the same and should skip write()
@@ -57,6 +57,7 @@ ob_end_flush();
 *** Testing session_set_save_handler() : test write short circuit ***
 
 Open [%s,PHPSESSID]
+CreateID [PHPT-%s]
 Read [%s,%s]
 array(3) {
   ["Blah"]=>
@@ -102,4 +103,5 @@ array(4) {
   ["Bar"]=>
   string(3) "Foo"
 }
+Update [%s,PHPT-%d]
 Close [%s,PHPSESSID]
\ No newline at end of file
index 8cbf42c860d553b4f1ae71aeae7842011cccb9a1..5a0bcefc2a7cbe5146c9cfc41d3274cc205474a7 100644 (file)
@@ -97,99 +97,194 @@ ob_end_flush();
 *** Testing session_start() : error functionality ***
 
 -- Iteration 1 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, integer given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 2 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, integer given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 3 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, integer given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 4 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, integer given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 5 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, float given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 6 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, float given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 7 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, float given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 8 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, float given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 9 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, float given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 10 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, null given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 11 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, null given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 12 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, boolean given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 13 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, boolean given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 14 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, boolean given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 15 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, boolean given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 16 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, string given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 17 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, string given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 18 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, string given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 19 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, string given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 20 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, string given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 21 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, object given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 22 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, null given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 23 --
-bool(true)
-bool(true)
+
+Warning: session_start() expects parameter 1 to be array, null given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
 
 -- Iteration 24 --
-bool(true)
-bool(true)
-Done
 
+Warning: session_start() expects parameter 1 to be array, resource given in %s on line %d
+bool(false)
+
+Warning: session_destroy(): Trying to destroy uninitialized session in %s on line %d
+bool(false)
+Done