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 public $_backend_type = "SQL";
18 function __construct(&$dbh, $table)
22 $this->_table = $table;
24 ini_set('session.save_handler', 'user');
25 session_module_name('user'); // new style
26 session_set_save_handler(array(&$this, 'open'),
27 array(&$this, 'close'),
28 array(&$this, 'read'),
29 array(&$this, 'write'),
30 array(&$this, 'destroy'),
38 $this->_connected = is_resource($dbh->connection);
39 if (!$this->_connected) {
40 $res = $dbh->connect($dbh->dsn);
41 if (DB::isError($res)) {
42 error_log("PhpWiki::DbSession::_connect: " . $res->getMessage());
50 return $this->_dbh->query($sql);
53 // adds surrounding quotes
54 function quote($string)
56 return $this->_dbh->quote($string);
59 function _disconnect()
61 if (0 and $this->_connected)
62 $this->_dbh->disconnect();
68 * Actually this function is a fake for session_set_save_handle.
69 * @param string $save_path a path to stored files
70 * @param string $session_name a name of the concrete file
71 * @return boolean true just a variable to notify PHP that everything
74 public function open($save_path, $session_name)
76 //$this->log("_open($save_path, $session_name)");
83 * This function is called just after <i>write</i> call.
85 * @return boolean true just a variable to notify PHP that everything
88 public function close()
90 //$this->log("_close()");
95 * Reads the session data from DB.
97 * @param string $id an id of current session
100 public function read($id)
102 //$this->log("_read($id)");
103 $dbh = $this->_connect();
104 $table = $this->_table;
105 $qid = $dbh->quote($id);
107 $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
109 $this->_disconnect();
110 if (DB::isError($res) || empty($res))
112 if (isa($dbh, 'DB_pgsql'))
113 //if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
114 $res = base64_decode($res);
115 if (strlen($res) > 4000) {
116 // trigger_error("Overlarge session data! ".strlen($res). " gt. 4000", E_USER_WARNING);
117 $res = preg_replace('/s:6:"_cache";O:12:"WikiDB_cache".+}$/', "", $res);
118 $res = preg_replace('/s:12:"_cached_html";s:.+",s:4:"hits"/', 's:4:"hits"', $res);
119 if (strlen($res) > 4000) $res = '';
125 * Saves the session data into DB.
127 * Just a comment: The "write" handler is not
128 * executed until after the output stream is closed. Thus,
129 * output from debugging statements in the "write" handler
130 * will never be seen in the browser. If debugging output
131 * is necessary, it is suggested that the debug output be
132 * written to a file instead.
135 * @param string $sess_data
136 * @return boolean true if data saved successfully and false
139 public function write($id, $sess_data)
141 if (defined("WIKI_XMLRPC") or defined("WIKI_SOAP")) return false;
143 $dbh = $this->_connect();
144 //$dbh->unlock(false,1);
145 $table = $this->_table;
146 $qid = $dbh->quote($id);
147 $qip = $dbh->quote($GLOBALS['request']->get('REMOTE_ADDR'));
148 $time = $dbh->quote(time());
149 if (DEBUG and $sess_data == 'wiki_user|N;') {
150 trigger_error("delete empty session $qid", E_USER_WARNING);
152 // postgres can't handle binary data in a TEXT field.
153 if (isa($dbh, 'DB_pgsql'))
154 $sess_data = base64_encode($sess_data);
155 $qdata = $dbh->quote($sess_data);
157 /* AffectedRows with sessions seems to be instable on certain platforms.
158 * Enable the safe and slow USE_SAFE_DBSESSION then.
160 if (USE_SAFE_DBSESSION) {
161 $dbh->query("DELETE FROM $table"
162 . " WHERE sess_id=$qid");
163 $res = $dbh->query("INSERT INTO $table"
164 . " (sess_id, sess_data, sess_date, sess_ip)"
165 . " VALUES ($qid, $qdata, $time, $qip)");
167 $res = $dbh->query("UPDATE $table"
168 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
169 . " WHERE sess_id=$qid");
170 $result = $dbh->AffectedRows();
171 if ($result === false or $result < 1) { // 0 cannot happen: time, -1 (failure) on mysql
172 $res = $dbh->query("INSERT INTO $table"
173 . " (sess_id, sess_data, sess_date, sess_ip)"
174 . " VALUES ($qid, $qdata, $time, $qip)");
177 $this->_disconnect();
178 return !DB::isError($res);
182 * Destroys a session.
184 * Removes a session from the table.
187 * @return boolean true
189 public function destroy($id)
191 $dbh = $this->_connect();
192 $table = $this->_table;
193 $qid = $dbh->quote($id);
195 $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
197 $this->_disconnect();
202 * Cleans out all expired sessions.
204 * @param int $maxlifetime session's time to live.
205 * @return boolean true
207 public function gc($maxlifetime)
209 $dbh = $this->_connect();
210 $table = $this->_table;
211 $threshold = time() - $maxlifetime;
213 $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
215 $this->_disconnect();
219 // WhoIsOnline support
220 // TODO: ip-accesstime dynamic blocking API
221 function currentSessions()
224 $dbh = $this->_connect();
225 $table = $this->_table;
226 $res = $dbh->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
227 if (DB::isError($res) || empty($res))
229 while ($row = $res->fetchRow()) {
230 $data = $row['sess_data'];
231 $date = $row['sess_date'];
232 $ip = $row['sess_ip'];
233 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
234 $data = base64_decode($data);
235 if ($date < 908437560 or $date > 1588437560)
237 // session_data contains the <variable name> + "|" + <packed string>
238 // we need just the wiki_user object (might be array as well)
239 $user = strstr($data, "wiki_user|");
240 $sessions[] = array('wiki_user' => substr($user, 10), // from "O:" onwards
244 $this->_disconnect();
253 // c-hanging-comment-ender-p: nil
254 // indent-tabs-mode: nil