4 * Db sessions for PDO, based on pear DB Sessions.
11 public $_backend_type = "PDO";
13 function __construct($dbh, $table)
16 $this->_table = $table;
18 ini_set('session.save_handler', 'user');
19 session_module_name('user'); // new style
20 session_set_save_handler(array(&$this, 'open'),
21 array(&$this, 'close'),
22 array(&$this, 'read'),
23 array(&$this, 'write'),
24 array(&$this, 'destroy'),
31 if (!$dbh or !is_object($dbh)) {
33 $db = new WikiDB_backend_PDO($DBParams);
34 $this->_dbh =& $db->_dbh;
35 $this->_backend =& $db;
42 return $this->_backend->query($sql);
45 function quote($string)
47 return $this->_backend->quote($string);
50 function _disconnect()
52 if (0 and $this->_dbh)
59 * Actually this function is a fake for session_set_save_handle.
60 * @param string $save_path a path to stored files
61 * @param string $session_name a name of the concrete file
62 * @return boolean true just a variable to notify PHP that everything
65 public function open($save_path, $session_name)
67 //$this->log("_open($save_path, $session_name)");
74 * This function is called just after <i>write</i> call.
76 * @return boolean true just a variable to notify PHP that everything
79 public function close()
81 //$this->log("_close()");
86 * Reads the session data from DB.
88 * @param string $id an id of current session
91 public function read($id)
93 //$this->log("_read($id)");
94 $dbh = $this->_connect();
95 $table = $this->_table;
96 $sth = $dbh->prepare("SELECT sess_data FROM $table WHERE sess_id=?");
97 $sth->bindParam(1, $id, PDO::PARAM_STR, 32);
98 if ($sth->execute()) {
99 $res = $sth->fetchColumn();
103 $this->_disconnect();
104 if (!empty($res) and isa($dbh, 'ADODB_postgres64')) {
105 $res = base64_decode($res);
107 if (strlen($res) > 4000) {
108 trigger_error("Overlarge session data! " . strlen($res) .
109 " gt. 4000", E_USER_WARNING);
110 $res = preg_replace('/s:6:"_cache";O:12:"WikiDB_cache".+}$/', "", $res);
111 $res = preg_replace('/s:12:"_cached_html";s:.+",s:4:"hits"/', 's:4:"hits"', $res);
112 if (strlen($res) > 4000) {
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
134 public function write($id, $sess_data)
136 if (defined("WIKI_XMLRPC") or defined("WIKI_SOAP")) return false;
138 $dbh = $this->_connect();
139 $table = $this->_table;
142 // postgres can't handle binary data in a TEXT field.
143 if (isa($dbh, 'ADODB_postgres64'))
144 $sess_data = base64_encode($sess_data);
146 /* AffectedRows with sessions seems to be unstable on certain platforms.
147 * Enable the safe and slow USE_SAFE_DBSESSION then.
149 if (USE_SAFE_DBSESSION) {
150 $this->_backend->beginTransaction();
151 $rs = $this->query("DELETE FROM $table"
152 . " WHERE sess_id=$id");
153 $sth = $dbh->prepare("INSERT INTO $table"
154 . " (sess_id, sess_data, sess_date, sess_ip)"
155 . " VALUES (?, ?, ?, ?)");
156 $sth->bindParam(1, $id, PDO::PARAM_STR, 32);
157 $sth->bindParam(2, $sess_data, PDO::PARAM_LOB);
158 $sth->bindParam(3, $time, PDO::PARAM_INT);
159 $sth->bindParam(4, $GLOBALS['request']->get('REMOTE_ADDR'), PDO::PARAM_STR, 15);
160 if ($result = $sth->execute()) {
161 $this->_backend->commit();
163 $this->_backend->rollBack();
166 $sth = $dbh->prepare("UPDATE $table"
167 . " SET sess_data=?, sess_date=?, sess_ip=?"
168 . " WHERE sess_id=?");
169 $sth->bindParam(1, $sess_data, PDO::PARAM_LOB);
170 $sth->bindParam(2, $time, PDO::PARAM_INT);
171 $sth->bindParam(3, $GLOBALS['request']->get('REMOTE_ADDR'), PDO::PARAM_STR, 15);
172 $sth->bindParam(4, $id, PDO::PARAM_STR, 32);
173 $result = $sth->execute(); // implicit affected rows
174 if ($result === false or $result < 1) { // false or int > 0
175 $sth = $dbh->prepare("INSERT INTO $table"
176 . " (sess_id, sess_data, sess_date, sess_ip)"
177 . " VALUES (?, ?, ?, ?)");
178 $sth->bindParam(1, $id, PDO::PARAM_STR, 32);
179 $sth->bindParam(2, $sess_data, PDO::PARAM_LOB);
180 $sth->bindParam(3, $time, PDO::PARAM_INT);
181 $sth->bindParam(4, $GLOBALS['request']->get('REMOTE_ADDR'), PDO::PARAM_STR, 15);
182 $result = $sth->execute();
185 $this->_disconnect();
190 * Destroys a session.
192 * Removes a session from the table.
195 * @return boolean true
197 public function destroy($id)
199 $table = $this->_table;
200 $dbh = $this->_connect();
201 $sth = $dbh->prepare("DELETE FROM $table WHERE sess_id=?");
202 $sth->bindParam(1, $id, PDO::PARAM_STR, 32);
204 $this->_disconnect();
209 * Cleans out all expired sessions.
211 * @param int $maxlifetime session's time to live.
212 * @return boolean true
214 public function gc($maxlifetime)
216 $table = $this->_table;
217 $threshold = time() - $maxlifetime;
218 $dbh = $this->_connect();
219 $sth = $dbh->prepare("DELETE FROM $table WHERE sess_date < ?");
220 $sth->bindParam(1, $threshold, PDO::PARAM_INT);
222 $this->_disconnect();
226 // WhoIsOnline support.
227 // TODO: ip-accesstime dynamic blocking API
228 function currentSessions()
231 $table = $this->_table;
232 $dbh = $this->_connect();
233 $sth = $dbh->prepare("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
234 if (!$sth->execute()) {
237 while ($row = $sth->fetch(PDO::FETCH_NUM)) {
241 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
242 $data = base64_decode($data);
243 if ($date < 908437560 or $date > 1588437560)
245 // session_data contains the <variable name> + "|" + <packed string>
246 // we need just the wiki_user object (might be array as well)
247 $user = strstr($data, "wiki_user|");
248 $sessions[] = array('wiki_user' => substr($user, 10), // from "O:" onwards
252 $this->_disconnect();
261 // c-hanging-comment-ender-p: nil
262 // indent-tabs-mode: nil