4 * DB sessions for pear DB
8 * Originally by Stanislav Shramko <stanis@movingmail.com>
9 * Minor rewrite by Reini Urban <rurban@x-ray.at> for Phpwiki.
10 * Quasi-major rewrite/decruft/fix by Jeff Dairiki <dairiki@dairiki.org>.
16 var $_backend_type = "SQL";
18 function DbSession_SQL (&$dbh, $table) {
21 $this->_table = $table;
23 ini_set('session.save_handler','user');
24 session_module_name('user'); // new style
25 session_set_save_handler(array(&$this, 'open'),
26 array(&$this, 'close'),
27 array(&$this, 'read'),
28 array(&$this, 'write'),
29 array(&$this, 'destroy'),
34 function & _connect() {
36 $this->_connected = is_resource($dbh->connection);
37 if (!$this->_connected) {
38 $res = $dbh->connect($dbh->dsn);
39 if (DB::isError($res)) {
40 error_log("PhpWiki::DbSession::_connect: " . $res->getMessage());
46 function query($sql) {
47 return $this->_dbh->query($sql);
49 // adds surrounding quotes
50 function quote($string) {
51 return $this->_dbh->quote($string);
54 function _disconnect() {
55 if (0 and $this->_connected)
56 $this->_dbh->disconnect();
62 * Actually this function is a fake for session_set_save_handle.
63 * @param string $save_path a path to stored files
64 * @param string $session_name a name of the concrete file
65 * @return boolean true just a variable to notify PHP that everything
69 function open ($save_path, $session_name) {
70 //$this->log("_open($save_path, $session_name)");
77 * This function is called just after <i>write</i> call.
79 * @return boolean true just a variable to notify PHP that everything
84 //$this->log("_close()");
89 * Reads the session data from DB.
91 * @param string $id an id of current session
96 //$this->log("_read($id)");
97 $dbh = $this->_connect();
98 $table = $this->_table;
99 $qid = $dbh->quote($id);
101 $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
103 $this->_disconnect();
104 if (DB::isError($res) || empty($res))
106 if (isa($dbh, 'DB_pgsql'))
107 //if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
108 $res = base64_decode($res);
109 if (strlen($res) > 4000) {
110 trigger_error("Overlarge session data! ".strlen($res).
111 " gt. 4000", E_USER_WARNING);
112 $res = preg_replace('/s:6:"_cache";O:12:"WikiDB_cache".+}$/',"",$res);
113 $res = preg_replace('/s:12:"_cached_html";s:.+",s:4:"hits"/','s:4:"hits"',$res);
114 if (strlen($res) > 4000) $res = '';
120 * Saves the session data into DB.
122 * Just a comment: The "write" handler is not
123 * executed until after the output stream is closed. Thus,
124 * output from debugging statements in the "write" handler
125 * will never be seen in the browser. If debugging output
126 * is necessary, it is suggested that the debug output be
127 * written to a file instead.
130 * @param string $sess_data
131 * @return boolean true if data saved successfully and false
135 function write ($id, $sess_data) {
136 if (defined("WIKI_XMLRPC") or defined("WIKI_SOAP")) return;
138 $dbh = $this->_connect();
139 //$dbh->unlock(false,1);
140 $table = $this->_table;
141 $qid = $dbh->quote($id);
142 $qip = $dbh->quote($GLOBALS['request']->get('REMOTE_ADDR'));
143 $time = $dbh->quote(time());
144 if (DEBUG and $sess_data == 'wiki_user|N;') {
145 trigger_error("delete empty session $qid", E_USER_WARNING);
147 // postgres can't handle binary data in a TEXT field.
148 if (isa($dbh, 'DB_pgsql'))
149 $sess_data = base64_encode($sess_data);
150 $qdata = $dbh->quote($sess_data);
152 /* AffectedRows with sessions seems to be instable on certain platforms.
153 * Enable the safe and slow USE_SAFE_DBSESSION then.
155 if (USE_SAFE_DBSESSION) {
156 $dbh->query("DELETE FROM $table"
157 . " WHERE sess_id=$qid");
158 $res = $dbh->query("INSERT INTO $table"
159 . " (sess_id, sess_data, sess_date, sess_ip)"
160 . " VALUES ($qid, $qdata, $time, $qip)");
162 $res = $dbh->query("UPDATE $table"
163 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
164 . " WHERE sess_id=$qid");
165 $result = $dbh->AffectedRows();
166 if ( $result === false or $result < 1 ) { // 0 cannot happen: time, -1 (failure) on mysql
167 $res = $dbh->query("INSERT INTO $table"
168 . " (sess_id, sess_data, sess_date, sess_ip)"
169 . " VALUES ($qid, $qdata, $time, $qip)");
172 $this->_disconnect();
173 return ! DB::isError($res);
177 * Destroys a session.
179 * Removes a session from the table.
182 * @return boolean true
185 function destroy ($id) {
186 $dbh = $this->_connect();
187 $table = $this->_table;
188 $qid = $dbh->quote($id);
190 $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
192 $this->_disconnect();
197 * Cleans out all expired sessions.
199 * @param int $maxlifetime session's time to live.
200 * @return boolean true
203 function gc ($maxlifetime) {
204 $dbh = $this->_connect();
205 $table = $this->_table;
206 $threshold = time() - $maxlifetime;
208 $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
210 $this->_disconnect();
214 // WhoIsOnline support
215 // TODO: ip-accesstime dynamic blocking API
216 function currentSessions() {
218 $dbh = $this->_connect();
219 $table = $this->_table;
220 $res = $dbh->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
221 if (DB::isError($res) || empty($res))
223 while ($row = $res->fetchRow()) {
224 $data = $row['sess_data'];
225 $date = $row['sess_date'];
226 $ip = $row['sess_ip'];
227 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
228 $data = base64_decode($data);
229 if ($date < 908437560 or $date > 1588437560)
231 // session_data contains the <variable name> + "|" + <packed string>
232 // we need just the wiki_user object (might be array as well)
233 $user = strstr($data,"wiki_user|");
234 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
238 $this->_disconnect();
247 // c-hanging-comment-ender-p: nil
248 // indent-tabs-mode: nil