3 * Copyright 2005 $ThePhpWikiProgrammingTeam
5 * This file is part of PhpWiki.
7 * PhpWiki is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * PhpWiki is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 * DB sessions for pear DB
27 * Originally by Stanislav Shramko <stanis@movingmail.com>
28 * Minor rewrite by Reini Urban <rurban@x-ray.at> for Phpwiki.
29 * Quasi-major rewrite/decruft/fix by Jeff Dairiki <dairiki@dairiki.org>.
35 public $_backend_type = "SQL";
37 function __construct($dbh, $table)
40 $this->_table = $table;
42 ini_set('session.save_handler', 'user');
43 session_module_name('user'); // new style
44 session_set_save_handler(array(&$this, 'open'),
45 array(&$this, 'close'),
46 array(&$this, 'read'),
47 array(&$this, 'write'),
48 array(&$this, 'destroy'),
55 $this->_connected = is_resource($dbh->connection);
56 if (!$this->_connected) {
57 $res = $dbh->connect($dbh->dsn);
58 if (DB::isError($res)) {
59 error_log("PhpWiki::DbSession::_connect: " . $res->getMessage());
67 return $this->_dbh->query($sql);
70 // adds surrounding quotes
71 function quote($string)
73 return $this->_dbh->quote($string);
76 function _disconnect()
78 if (0 and $this->_connected)
79 $this->_dbh->disconnect();
85 * Actually this function is a fake for session_set_save_handle.
86 * @param string $save_path a path to stored files
87 * @param string $session_name a name of the concrete file
88 * @return boolean true just a variable to notify PHP that everything
91 public function open($save_path, $session_name)
93 //$this->log("_open($save_path, $session_name)");
100 * This function is called just after <i>write</i> call.
102 * @return boolean true just a variable to notify PHP that everything
105 public function close()
107 //$this->log("_close()");
112 * Reads the session data from DB.
114 * @param string $id an id of current session
117 public function read($id)
119 //$this->log("_read($id)");
120 $dbh = $this->_connect();
121 $table = $this->_table;
122 $qid = $dbh->quote($id);
124 $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
126 $this->_disconnect();
127 if (DB::isError($res) || empty($res)) {
130 if (is_a($dbh, 'DB_pgsql'))
131 $res = base64_decode($res);
132 if (strlen($res) > 4000) {
133 // trigger_error("Overlarge session data! ".strlen($res). " gt. 4000", E_USER_WARNING);
134 $res = preg_replace('/s:6:"_cache";O:12:"WikiDB_cache".+}$/', "", $res);
135 $res = preg_replace('/s:12:"_cached_html";s:.+",s:4:"hits"/', 's:4:"hits"', $res);
136 if (strlen($res) > 4000) {
144 * Saves the session data into DB.
146 * Just a comment: The "write" handler is not
147 * executed until after the output stream is closed. Thus,
148 * output from debugging statements in the "write" handler
149 * will never be seen in the browser. If debugging output
150 * is necessary, it is suggested that the debug output be
151 * written to a file instead.
154 * @param string $sess_data
155 * @return boolean true if data saved successfully and false
158 public function write($id, $sess_data)
161 * @var WikiRequest $request
165 if (defined("WIKI_XMLRPC") or defined("WIKI_SOAP")) return false;
167 $dbh = $this->_connect();
168 $table = $this->_table;
169 $qid = $dbh->quote($id);
170 $qip = $dbh->quote($request->get('REMOTE_ADDR'));
171 $time = $dbh->quote(time());
172 if (DEBUG and $sess_data == 'wiki_user|N;') {
173 trigger_error("delete empty session $qid", E_USER_WARNING);
175 // postgres can't handle binary data in a TEXT field.
176 if (is_a($dbh, 'DB_pgsql'))
177 $sess_data = base64_encode($sess_data);
178 $qdata = $dbh->quote($sess_data);
180 /* AffectedRows with sessions seems to be unstable on certain platforms.
181 * Enable the safe and slow USE_SAFE_DBSESSION then.
183 if (USE_SAFE_DBSESSION) {
184 $dbh->query("DELETE FROM $table"
185 . " WHERE sess_id=$qid");
186 $res = $dbh->query("INSERT INTO $table"
187 . " (sess_id, sess_data, sess_date, sess_ip)"
188 . " VALUES ($qid, $qdata, $time, $qip)");
190 $res = $dbh->query("UPDATE $table"
191 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
192 . " WHERE sess_id=$qid");
193 $result = $dbh->AffectedRows();
194 if ($result === false or $result < 1) { // 0 cannot happen: time, -1 (failure) on mysql
195 $res = $dbh->query("INSERT INTO $table"
196 . " (sess_id, sess_data, sess_date, sess_ip)"
197 . " VALUES ($qid, $qdata, $time, $qip)");
200 $this->_disconnect();
201 return !DB::isError($res);
205 * Destroys a session.
207 * Removes a session from the table.
210 * @return boolean true
212 public function destroy($id)
214 $dbh = $this->_connect();
215 $table = $this->_table;
216 $qid = $dbh->quote($id);
218 $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
220 $this->_disconnect();
225 * Cleans out all expired sessions.
227 * @param int $maxlifetime session's time to live.
228 * @return boolean true
230 public function gc($maxlifetime)
232 $dbh = $this->_connect();
233 $table = $this->_table;
234 $threshold = time() - $maxlifetime;
236 $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
238 $this->_disconnect();
242 // WhoIsOnline support
243 // TODO: ip-accesstime dynamic blocking API
244 function currentSessions()
247 $dbh = $this->_connect();
248 $table = $this->_table;
249 $res = $dbh->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
250 if (DB::isError($res) || empty($res))
252 while ($row = $res->fetchRow()) {
253 $data = $row['sess_data'];
254 $date = $row['sess_date'];
255 $ip = $row['sess_ip'];
256 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
257 $data = base64_decode($data);
258 if ($date < 908437560 or $date > 1588437560)
260 // session_data contains the <variable name> + "|" + <packed string>
261 // we need just the wiki_user object (might be array as well)
262 $user = strstr($data, "wiki_user|");
263 $sessions[] = array('wiki_user' => substr($user, 10), // from "O:" onwards
267 $this->_disconnect();
276 // c-hanging-comment-ender-p: nil
277 // indent-tabs-mode: nil