X-Git-Url: https://jasonwoof.com/gitweb/?p=wfpl.git;a=blobdiff_plain;f=session.php;h=75508c026be78ef4d20d83f4ac76b7ec16ffc6da;hp=b36af68660469156d8059a3bac5f3f5d8c34ab8c;hb=HEAD;hpb=856f805c6cbb6e712c662214f3b5821442f808a0 diff --git a/session.php b/session.php index b36af68..75508c0 100644 --- a/session.php +++ b/session.php @@ -1,77 +1,116 @@ . - - -# you'll need these database tables: -# create table wfpl_sessions (id int unique auto_increment, session_key varchar(16), length int, expires int); -# create table wfpl_session_data (id int unique auto_increment, session_id int, name varchar(100), value text); -# run this command to install/clear the tables: -# mysql DATABASE_NAME < code/wfpl/examples/session.sql -# note: you may need these parameters for mysql: -u USERNAME -p - -# GLOSSARY +# session_exists() +# session_new('timeout', 'max_len') +# session_set('key', 'value') +# session_sets(['key': 'value', 'key2': 'val2']) +# session_get('key') +# session_clear() # removes all set() values +# session_clear('key') +# session_kill() # -# session_key 16 digit string identifying the session -# session_id integer id of the record in the "sessions" table of the database -# UNTIL_CLOSE a constant passed as session length to indicate "until browser window closes" +# All session data is cached in globals, so: +# 1. don't set large amonuts of data +# 2. session_get() is very fast (no db access) -# session_id is kept in $GLOBALS -# session_key is sent as a cookie, and thus appears in $_REQUEST. The clean version is in $GLOBALS - # generate a new random 16-character string function session_generate_key() { $character_set = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - $id = " "; + $id = " "; # PHP 4.2.0 and up seed the random number generator for you. # Lets hope that it seeds with something harder to guess than the clock. - for($i = 0; $i < 16; ++$i) { - $id{$i} = $character_set{mt_rand(0, 61)}; - } + for($i = 0; $i < 16; ++$i) { + $id{$i} = $character_set{mt_rand(0, 61)}; + } - return $id; + return $id; } -# track this user with a session cookie (ie a cookie that goes away when the -# user closes the browser). The timestamp is how long to track the session in -# the database. Defaults to one day. -function session_new($length = 86400) { +# start a new session, tracked by a browser "session cookie". +# +# args: +# $idle_timeout (seconds) session ends after this much inactivity (or up to 10% less) +# $max_length (seconds) session ends after this long, regardless of activity +function session_new($idle_timeout = 129600 /* 36 hours */, $max_length = 604800 /* 1 week */) { + kill_session(); + $session_key = session_generate_key(); - db_insert('wfpl_sessions', 'session_key,length', $session_key, $length); - $GLOBALS['session_id'] = db_auto_id(); - $GLOBALS['session_key'] = $session_key; - $_REQUEST['session_key'] = $session_key; #just in case someone calls session_exists() after session_new() - session_touch($length); - return $GLOBALS['session_key']; + $now = time(); + $row = array( + 'session_key' => $session_key, + 'idle_timeout' => $idle_timeout, + 'expires' => $now + $idle_timeout, + 'expires_max' => $now + $max_length, + 'value' => '' + ); + + db_insert_assoc('wfpl_sessions', $row); + $session_id = db_auto_id(); + $GLOBALS['wfpl_session'] = array( + 'exists' => true, + 'id' => $session_id, + 'key' => $session_key, + 'idle_timeout' => $idle_timeout, + 'expires' => $now + $idle_timeout, + 'expires_max' => $now + $max_length, + 'value' => array() + ); + session_set_cookie(); + return $session_key; } -# call to renew the timeout for the session. -# assumes there's a session. call init_session() if you'd like one auto-create one if not found. -function session_touch($length = false) { - if(!$length) { - $length = db_get_value('wfpl_sessions', 'length', 'where id=%i', $GLOBALS['session_id']); +function session_set_cookie() { + if (session_exists()) { + if (!isset($GLOBALS['wfpl_session']['cookie_set'])) { + $GLOBALS['wfpl_session']['cookie_set'] = true; + header('Set-Cookie: session_key=' . $GLOBALS['wfpl_session']['key'] . '; Path=/'); + } } - $expires = time() + $length; - - header('Set-Cookie: session_key=' . $GLOBALS['session_key']); +} - db_update('wfpl_sessions', 'expires', $expires, 'where id=%i', $GLOBALS['session_id']); +# this is a helper function. See session_new() +function session_touch() { + if(!session_exists()) { + return; + } + # is the session extendable? + if ($GLOBALS['wfpl_session']['expires'] < $GLOBALS['wfpl_session']['expires_max']) { + # would this extend the session by at least 10%? + $now = time(); + $last_activity = $GLOBALS['wfpl_session']['expires'] - $GLOBALS['wfpl_session']['idle_timeout']; + # don't db_update if only a tiny fraction of the idle timeout has passed + $db_threshold = ceil(0.1 * $GLOBALS['wfpl_session']['idle_timeout']); + if ($now > $last_activity + $db_threshold) { + $expires = min( + $GLOBALS['wfpl_session']['expires_max'], + $now + $GLOBALS['wfpl_session']['idle_timeout'] + ); + db_update('wfpl_sessions', 'expires', $expires, 'where id=%i', $GLOBALS['wfpl_session']['id']); + $GLOBALS['wfpl_session']['expires'] = $expires; + } + } } # delete the current session @@ -79,54 +118,81 @@ function kill_session() { if(!session_exists()) { return; } - _kill_session($GLOBALS['session_id']); -} - -# for internal use. use kill_session() above -function _kill_session($id) { - db_delete('wfpl_session_data', 'where session_id=%i', $id); - db_delete('wfpl_sessions', 'where id=%i', $id); + db_delete('wfpl_sessions', 'where id=%i', $GLOBALS['wfpl_session']['id']); + $GLOBALS['wfpl_session'] = array('exists' => false); } # delete expired sessions from database function session_purge_old() { - $now = time(); - $expired_sessions = db_get_column('wfpl_sessions', 'id', 'where expires < %i', $now); - if($expired_sessions) foreach($expired_sessions as $expired_session) { - _kill_session($expired_session); - } + db_delete('wfpl_sessions', 'where expires < %i', time()); } # return true if a session exists function session_exists() { - if(!isset($_REQUEST['session_key'])) { - return false; + if (isset($GLOBALS['wfpl_session'])) { + return $GLOBALS['wfpl_session']['exists']; } - if(isset($GLOBALS['session_id'])) { - return true; + $GLOBALS['wfpl_session'] = array('exists' => false); + + if(!isset($_COOKIE['session_key'])) { + return false; } - $session_key = ereg_replace('[^a-zA-Z0-9]', '', $_REQUEST['session_key']); + $session_key = preg_replace('|[^a-z0-9]|i', '', $_COOKIE['session_key']); if(!strlen($session_key) == 16) { return false; } - $GLOBALS['session_key'] = $session_key; - - session_purge_old(); - $id = db_get_value('wfpl_sessions', 'id', 'where session_key=%"', $session_key); - if($id === false) { + $row = db_get_assoc('wfpl_sessions', 'id,idle_timeout,expires,expires_max,value', 'where session_key=%"', $session_key); + if($row === false) { + return false; + } + $now = time(); + if ($now >= (int) $row['expires']) { + session_purge_old(); return false; } - $GLOBALS['session_id'] = $id; + $GLOBALS['wfpl_session']['exists'] = true; + $GLOBALS['wfpl_session']['id'] = $row['id']; + $GLOBALS['wfpl_session']['idle_timeout'] = (int) $row['idle_timeout']; + $GLOBALS['wfpl_session']['expires'] = (int) $row['expires']; + $GLOBALS['wfpl_session']['expires_max'] = (int) $row['expires_max']; + $GLOBALS['wfpl_session']['key'] = $session_key; + + if (strlen($row['value']) && is_array($parsed = json_decode($row['value'], true))) { + $GLOBALS['wfpl_session']['value'] = $parsed; + } else { + $GLOBALS['wfpl_session']['value'] = array(); + } + + # mark session as not idle + session_touch(); + return true; } + +# generate a random password using only letters and numbers that look +# particularly unique +function new_readable_password($length = 8) { + $character_set = "ABCDEFHJKLMNPQRTUWXY34789"; + $code = ""; + + # PHP 4.2.0 and up seed the random number generator for you. + # Lets hope that it seeds with something harder to guess than the clock. + while($length--) { + $code .= $character_set{mt_rand(0, 24)}; # inclusive + } + + return $code; +} + +# depricated # return username if a session exists and is authenticated -function session_exists_and_authed() { +function logged_in() { if(!session_exists()) { return false; } @@ -135,27 +201,100 @@ function session_exists_and_authed() { } -# find existing session, or make one +# depricated +function session_exists_and_authed() { + return logged_in(); +} + + +# depricated +# return true if a session exists and is authenticated +function logged_in_as_admin() { + if(!session_exists()) { + return false; + } + + if(session_get('auth_admin')) { + return true; + } + return false; +} + + +# find existing session, or make one (name "session_init" was taken) function init_session() { if(!session_exists()) { session_new(); } } -# save a variable into the session +# internal use only (write session cache to db) +function _sync_session() { + if (count($GLOBALS['wfpl_session']['value']) > 0) { + $value = json_encode($GLOBALS['wfpl_session']['value']); + } else { + $value = ''; + } + db_update('wfpl_sessions', 'value', $value, 'where id=%i', $GLOBALS['wfpl_session']['id']); +} + +# save data into the session +# $value can be anything json_encode()able function session_set($name, $value) { - session_clear($name); - db_insert('wfpl_session_data', 'session_id,name,value', $GLOBALS['session_id'], $name, $value); + init_session(); + if (isset($GLOBALS['wfpl_session']['value'][$name])) { + if ($GLOBALS['wfpl_session']['value'][$name] === $value) { + return; + } + } + $GLOBALS['wfpl_session']['value'][$name] = $value; + _sync_session(); +} + +# save data into the session +# values can be anything json_encode()able +function session_sets($assoc) { + init_session(); + $dirty = false; + foreach ($assoc as $name => &$value) { + if (isset($GLOBALS['wfpl_session']['value'][$name])) { + if ($GLOBALS['wfpl_session']['value'][$name] === $value) { + continue; + } + } + $GLOBALS['wfpl_session']['value'][$name] = $value; + $dirty = true; + } + if ($dirty) { + _sync_session(); + } } # remove variable from the session -function session_clear($name) { - db_delete('wfpl_session_data', 'where session_id=%i && name=%"', $GLOBALS['session_id'], $name); +# with no args: clear all +function session_clear($name = -1) { + if(!session_exists()) { + return; + } + if ($name === -1) { + if (count($GLOBALS['wfpl_session']['value']) > 0) { + $GLOBALS['wfpl_session']['value'] = array(); + _sync_session(); + } + } elseif (isset($GLOBALS['wfpl_session']['value'][$name])) { + unset($GLOBALS['wfpl_session']['value'][$name]); + _sync_session(); + } } # get a variable into the session function session_get($name) { - return db_get_value('wfpl_session_data', 'value', 'where session_id=%i && name=%"', $GLOBALS['session_id'], $name); + if(!session_exists()) { + return false; + } + if (isset($GLOBALS['wfpl_session']['value'][$name])) { + return $GLOBALS['wfpl_session']['value'][$name]; + } else { + return false; + } } - -?>