]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/DbSession.php
fixed a nasty ADODB_mysql session update bug
[SourceForge/phpwiki.git] / lib / DbSession.php
1 <?php rcs_id('$Id: DbSession.php,v 1.8 2004-04-02 15:06:55 rurban Exp $');
2
3 /**
4  * Store sessions data in Pear DB / ADODB ....
5  *
6  * History
7  *
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>.
11  */
12 class DB_Session
13 {
14     var $_backend;
15     /**
16      * Constructor
17      *
18      * @param mixed $dbh
19      * Pear DB handle, or WikiDB object (from which the Pear DB handle will
20      * be extracted.
21      *
22      * @param string $table
23      * Name of SQL table containing session data.
24      */
25     function DB_Session(&$dbh, $table = 'session') {
26         // Coerce WikiDB to PearDB or ADODB.
27         // Todo: adodb/dba handlers
28         $db_type = $GLOBALS['DBParams']['dbtype'];
29         if (isa($dbh, 'WikiDB')) {
30             $backend = &$dbh->_backend;
31             $db_type = substr(get_class($dbh),7);
32             $class = "DB_Session_".$db_type;
33             if (class_exists($class)) {
34                 $this->_backend = new $class(&$backend->_dbh, $table);
35                 return $this->_backend;
36             }
37         }
38         return false;
39         
40         //Fixme: E_USER_WARNING ignored!
41         trigger_error(sprintf(
42 _("Your WikiDB DB backend '%s' cannot be used for DB_Session. Set USE_DB_SESSION to false."),
43                              $db_type), E_USER_WARNING);
44     }
45     
46     function currentSessions() {
47         return $this->_backend->currentSessions();
48     }
49     function query($sql) {
50         return $this->_backend->query($sql);
51     }
52     function quote($string) {
53         return $this->_backend->quote($string);
54     }
55
56 }
57
58 class DB_Session_SQL
59 extends DB_Session
60 {
61     var $_backend_type = "SQL";
62
63     function DB_Session_SQL ($dbh, $table) {
64
65         $this->_dbh = &$dbh;
66         $this->_table = $table;
67
68         ini_set('session.save_handler','user');
69         session_module_name('user'); // new style
70         session_set_save_handler(array(&$this, 'open'),
71                                  array(&$this, 'close'),
72                                  array(&$this, 'read'),
73                                  array(&$this, 'write'),
74                                  array(&$this, 'destroy'),
75                                  array(&$this, 'gc'));
76         return $this;
77     }
78
79     function _connect() {
80         $dbh = &$this->_dbh;
81         $this->_connected = (bool)$dbh->connection;
82         if (!$this->_connected) {
83             $res = $dbh->connect($dbh->dsn);
84             if (DB::isError($res)) {
85                 error_log("PhpWiki::DB_Session::_connect: " . $res->getMessage());
86             }
87         }
88         return $dbh;
89     }
90     
91     function query($sql) {
92         return $this->_dbh->query($sql);
93     }
94
95     function quote($string) {
96         return $this->_dbh->quote($string);
97     }
98
99     function _disconnect() {
100         if (!$this->_connected)
101             $this->_dbh->disconnect();
102     }
103
104     /**
105      * Opens a session.
106      *
107      * Actually this function is a fake for session_set_save_handle.
108      * @param  string $save_path a path to stored files
109      * @param  string $session_name a name of the concrete file
110      * @return boolean true just a variable to notify PHP that everything 
111      * is good.
112      * @access private
113      */
114     function open ($save_path, $session_name) {
115         //$this->log("_open($save_path, $session_name)");
116         return true;
117     }
118
119     /**
120      * Closes a session.
121      *
122      * This function is called just after <i>write</i> call.
123      *
124      * @return boolean true just a variable to notify PHP that everything 
125      * is good.
126      * @access private
127      */
128     function close() {
129         //$this->log("_close()");
130         return true;
131     }
132
133     /**
134      * Reads the session data from DB.
135      *
136      * @param  string $id an id of current session
137      * @return string
138      * @access private
139      */
140     function read ($id) {
141         //$this->log("_read($id)");
142         $dbh = &$this->_connect();
143         $table = $this->_table;
144         $qid = $dbh->quote($id);
145     
146         $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
147
148         $this->_disconnect();
149         if (DB::isError($res) || empty($res))
150             return '';
151         if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
152             $res = base64_decode($res);
153         return $res;
154     }
155   
156     /**
157      * Saves the session data into DB.
158      *
159      * Just  a  comment:       The  "write"  handler  is  not 
160      * executed until after the output stream is closed. Thus,
161      * output from debugging statements in the "write" handler
162      * will  never be seen in the browser. If debugging output
163      * is  necessary, it is suggested that the debug output be
164      * written to a file instead.
165      *
166      * @param  string $id
167      * @param  string $sess_data
168      * @return boolean true if data saved successfully  and false
169      * otherwise.
170      * @access private
171      */
172     function write ($id, $sess_data) {
173         
174         $dbh = &$this->_connect();
175         $table = $this->_table;
176         $qid = $dbh->quote($id);
177         $time = time();
178
179         // postgres can't handle binary data in a TEXT field.
180         if (isa($dbh, 'DB_pgsql'))
181             $sess_data = base64_encode($sess_data);
182         $qdata = $dbh->quote($sess_data);
183         
184         $res = $dbh->query("UPDATE $table"
185                            . " SET sess_data=$qdata, sess_date=$time"
186                            . " WHERE sess_id=$qid");
187
188         if ($dbh->affectedRows() == 0)
189             $res = $dbh->query("INSERT INTO $table"
190                                . " (sess_id, sess_data, sess_date)"
191                                . " VALUES ($qid, $qdata, $time)");
192
193         $this->_disconnect();
194         return ! DB::isError($res);
195     }
196
197     /**
198      * Destroys a session.
199      *
200      * Removes a session from the table.
201      *
202      * @param  string $id
203      * @return boolean true 
204      * @access private
205      */
206     function destroy ($id) {
207         $dbh = &$this->_connect();
208         $table = $this->_table;
209         $qid = $dbh->quote($id);
210
211         $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
212
213         $this->_disconnect();
214         return true;     
215     }
216
217     /**
218      * Cleans out all expired sessions.
219      *
220      * @param  int $maxlifetime session's time to live.
221      * @return boolean true
222      * @access private
223      */
224     function gc ($maxlifetime) {
225         $dbh = &$this->_connect();
226         $table = $this->_table;
227         $threshold = time() - $maxlifetime;
228
229         $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
230
231         $this->_disconnect();
232         return true;
233     }
234
235     // WhoIsOnline support
236     function currentSessions() {
237         $sessions = array();
238         $dbh = &$this->_connect();
239         $table = $this->_table;
240         $res = $this->query("SELECT sess_data,sess_date FROM $table ORDER BY sess_date DESC");
241         if (DB::isError($res) || empty($res))
242             return $sessions;
243         while ($row = $res->fetchRow()) {
244             $data = $row['sess_data'];
245             $date = $row['sess_date'];
246             if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
247                 $data = base64_decode($data);
248             // session_data contains the <variable name> + "|" + <packed string>
249             // we need just the wiki_user object (might be array as well)
250             $user = strstr($data,"wiki_user|");
251             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
252                                 'date' => $date);
253         }
254         $this->_disconnect();
255         return $sessions;
256     }
257 }
258
259
260 // self-written adodb-sessions
261 class DB_Session_ADODB
262 extends DB_Session
263 {
264     var $_backend_type = "ADODB";
265
266     function DB_Session_ADODB ($dbh, $table) {
267
268         $this->_dbh = &$dbh;
269         $this->_table = $table;
270
271         ini_set('session.save_handler','user');
272         session_module_name('user'); // new style
273         session_set_save_handler(array(&$this, 'open'),
274                                  array(&$this, 'close'),
275                                  array(&$this, 'read'),
276                                  array(&$this, 'write'),
277                                  array(&$this, 'destroy'),
278                                  array(&$this, 'gc'));
279         return $this;
280     }
281
282     function _connect() {
283         global $DBParams;
284         static $parsed = false;
285         $dbh = &$this->_dbh;
286         if (!$dbh) {
287             if (!$parsed) $parsed = parseDSN($DBParams['dsn']);
288             $this->_dbh = &ADONewConnection($parsed['phptype']); // Probably only MySql works just now
289             $conn = $this->_dbh->Connect($parsed['hostspec'],$parsed['username'], 
290                                          $parsed['password'], $parsed['database']);
291             $dbh = &$this->_dbh;                             
292         }
293         return $dbh;
294     }
295     
296     function query($sql) {
297         return $this->_dbh->Execute($sql);
298     }
299
300     function quote($string) {
301         return $this->_dbh->qstr($string);
302     }
303
304     function _disconnect() {
305         if (!$this->_dbh)
306             $this->_dbh->close();
307     }
308
309     /**
310      * Opens a session.
311      *
312      * Actually this function is a fake for session_set_save_handle.
313      * @param  string $save_path a path to stored files
314      * @param  string $session_name a name of the concrete file
315      * @return boolean true just a variable to notify PHP that everything 
316      * is good.
317      * @access private
318      */
319     function open ($save_path, $session_name) {
320         //$this->log("_open($save_path, $session_name)");
321         return true;
322     }
323
324     /**
325      * Closes a session.
326      *
327      * This function is called just after <i>write</i> call.
328      *
329      * @return boolean true just a variable to notify PHP that everything 
330      * is good.
331      * @access private
332      */
333     function close() {
334         //$this->log("_close()");
335         return true;
336     }
337
338     /**
339      * Reads the session data from DB.
340      *
341      * @param  string $id an id of current session
342      * @return string
343      * @access private
344      */
345     function read ($id) {
346         //$this->log("_read($id)");
347         $dbh = &$this->_connect();
348         $table = $this->_table;
349         $qid = $dbh->quote($id);
350         $res = '';
351         $rs = $dbh->Execute("SELECT sess_data FROM $table WHERE sess_id=$qid");
352         if (!$rs->EOF) {
353             $res = $rs->fields["sess_data"];
354         }
355         $this->_disconnect();
356         if (!empty($res) and preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
357             $res = base64_decode($res);
358         return $res;
359     }
360   
361     /**
362      * Saves the session data into DB.
363      *
364      * Just  a  comment:       The  "write"  handler  is  not 
365      * executed until after the output stream is closed. Thus,
366      * output from debugging statements in the "write" handler
367      * will  never be seen in the browser. If debugging output
368      * is  necessary, it is suggested that the debug output be
369      * written to a file instead.
370      *
371      * @param  string $id
372      * @param  string $sess_data
373      * @return boolean true if data saved successfully  and false
374      * otherwise.
375      * @access private
376      */
377     function write ($id, $sess_data) {
378         
379         $dbh = &$this->_connect();
380         $table = $this->_table;
381         $qid = $dbh->quote($id);
382         $time = time();
383
384         // postgres can't handle binary data in a TEXT field.
385         if (isa($dbh, 'DB_pgsql'))
386             $sess_data = base64_encode($sess_data);
387         $qdata = $dbh->quote($sess_data);
388         $res = $dbh->query("UPDATE $table"
389                            . " SET sess_data=$qdata, sess_date=$time"
390                            . " WHERE sess_id=$qid");
391         // Warning: This works only only adodb_mysql!
392         // The parent class adodb needs ->AffectedRows()
393         if (!$dbh->_AffectedRows()) 
394             $res = $dbh->query("INSERT INTO $table"
395                                . " (sess_id, sess_data, sess_date)"
396                                . " VALUES ($qid, $qdata, $time)");
397         $this->_disconnect();
398         return ! $res->EOF;
399     }
400
401     /**
402      * Destroys a session.
403      *
404      * Removes a session from the table.
405      *
406      * @param  string $id
407      * @return boolean true 
408      * @access private
409      */
410     function destroy ($id) {
411         $dbh = &$this->_connect();
412         $table = $this->_table;
413         $qid = $dbh->quote($id);
414
415         $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
416
417         $this->_disconnect();
418         return true;     
419     }
420
421     /**
422      * Cleans out all expired sessions.
423      *
424      * @param  int $maxlifetime session's time to live.
425      * @return boolean true
426      * @access private
427      */
428     function gc ($maxlifetime) {
429         $dbh = &$this->_connect();
430         $table = $this->_table;
431         $threshold = time() - $maxlifetime;
432
433         $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
434
435         $this->_disconnect();
436         return true;
437     }
438
439     // WhoIsOnline support
440     function currentSessions() {
441         $sessions = array();
442         $dbh = &$this->_connect();
443         $table = $this->_table;
444         $rs = $this->query("SELECT sess_data,sess_date FROM $table ORDER BY sess_date DESC");
445         if ($rs->EOF) {
446             return $sessions;
447         }
448         while ($row = $rs->fetchRow()) {
449             $data = $row['sess_data'];
450             $date = $row['sess_date'];
451             if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
452                 $data = base64_decode($data);
453             // session_data contains the <variable name> + "|" + <packed string>
454             // we need just the wiki_user object (might be array as well)
455             $user = strstr($data,"wiki_user|");
456             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
457                                 'date' => $date);
458         }
459         $this->_disconnect();
460         return $sessions;
461     }
462
463 }
464
465 // Local Variables:
466 // mode: php
467 // tab-width: 8
468 // c-basic-offset: 4
469 // c-hanging-comment-ender-p: nil
470 // indent-tabs-mode: nil
471 // End:
472 ?>