]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/DbSession.php
Prevent from some PHP5 warnings (ref args, no :: object init)
[SourceForge/phpwiki.git] / lib / DbSession.php
1 <?php rcs_id('$Id: DbSession.php,v 1.11 2004-04-19 18:27:45 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         //Fixme: E_USER_WARNING ignored!
39         trigger_error(sprintf(
40 _("Your WikiDB DB backend '%s' cannot be used for DB_Session. Set USE_DB_SESSION to false."),
41                              $db_type), E_USER_WARNING);
42         return false;
43     }
44     
45     function currentSessions() {
46         return $this->_backend->currentSessions();
47     }
48     function query($sql) {
49         return $this->_backend->query($sql);
50     }
51     function quote($string) {
52         return $this->_backend->quote($string);
53     }
54
55 }
56
57 class DB_Session_SQL
58 extends DB_Session
59 {
60     var $_backend_type = "SQL";
61
62     function DB_Session_SQL (&$dbh, $table) {
63
64         $this->_dbh = $dbh;
65         $this->_table = $table;
66
67         ini_set('session.save_handler','user');
68         session_module_name('user'); // new style
69         session_set_save_handler(array(&$this, 'open'),
70                                  array(&$this, 'close'),
71                                  array(&$this, 'read'),
72                                  array(&$this, 'write'),
73                                  array(&$this, 'destroy'),
74                                  array(&$this, 'gc'));
75         return $this;
76     }
77
78     function _connect() {
79         $dbh = &$this->_dbh;
80         $this->_connected = is_resource($dbh->connection);
81         if (!$this->_connected) {
82             $res = $dbh->connect($dbh->dsn);
83             if (DB::isError($res)) {
84                 error_log("PhpWiki::DB_Session::_connect: " . $res->getMessage());
85             }
86         }
87         return $dbh;
88     }
89     
90     function query($sql) {
91         return $this->_dbh->query($sql);
92     }
93
94     function quote($string) {
95         return $this->_dbh->quote($string);
96     }
97
98     function _disconnect() {
99         if (0 and $this->_connected)
100             $this->_dbh->disconnect();
101     }
102
103     /**
104      * Opens a session.
105      *
106      * Actually this function is a fake for session_set_save_handle.
107      * @param  string $save_path a path to stored files
108      * @param  string $session_name a name of the concrete file
109      * @return boolean true just a variable to notify PHP that everything 
110      * is good.
111      * @access private
112      */
113     function open ($save_path, $session_name) {
114         //$this->log("_open($save_path, $session_name)");
115         return true;
116     }
117
118     /**
119      * Closes a session.
120      *
121      * This function is called just after <i>write</i> call.
122      *
123      * @return boolean true just a variable to notify PHP that everything 
124      * is good.
125      * @access private
126      */
127     function close() {
128         //$this->log("_close()");
129         return true;
130     }
131
132     /**
133      * Reads the session data from DB.
134      *
135      * @param  string $id an id of current session
136      * @return string
137      * @access private
138      */
139     function read ($id) {
140         //$this->log("_read($id)");
141         $dbh = &$this->_connect();
142         $table = $this->_table;
143         $qid = $dbh->quote($id);
144     
145         $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
146
147         $this->_disconnect();
148         if (DB::isError($res) || empty($res))
149             return '';
150         if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
151             $res = base64_decode($res);
152         return $res;
153     }
154   
155     /**
156      * Saves the session data into DB.
157      *
158      * Just  a  comment:       The  "write"  handler  is  not 
159      * executed until after the output stream is closed. Thus,
160      * output from debugging statements in the "write" handler
161      * will  never be seen in the browser. If debugging output
162      * is  necessary, it is suggested that the debug output be
163      * written to a file instead.
164      *
165      * @param  string $id
166      * @param  string $sess_data
167      * @return boolean true if data saved successfully  and false
168      * otherwise.
169      * @access private
170      */
171     function write ($id, $sess_data) {
172         
173         $dbh = &$this->_connect();
174         $table = $this->_table;
175         $qid = $dbh->quote($id);
176         $qip = $dbh->quote($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
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, sess_ip=$qip"
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, sess_ip)"
191                                . " VALUES ($qid, $qdata, $time, $qip)");
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     // TODO: ip-accesstime dynamic blocking API
237     function currentSessions() {
238         $sessions = array();
239         $dbh = &$this->_connect();
240         $table = $this->_table;
241         $res = $dbh->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
242         if (DB::isError($res) || empty($res))
243             return $sessions;
244         while ($row = $res->fetchRow()) {
245             $data = $row['sess_data'];
246             $date = $row['sess_date'];
247             $ip   = $row['sess_ip'];
248             if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
249                 $data = base64_decode($data);
250             // session_data contains the <variable name> + "|" + <packed string>
251             // we need just the wiki_user object (might be array as well)
252             $user = strstr($data,"wiki_user|");
253             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
254                                 'date' => $date,
255                                 'ip'   => $ip);
256         }
257         $this->_disconnect();
258         return $sessions;
259     }
260 }
261
262 // self-written adodb-sessions
263 class DB_Session_ADODB
264 extends DB_Session
265 {
266     var $_backend_type = "ADODB";
267
268     function DB_Session_ADODB ($dbh, $table) {
269
270         $this->_dbh = $dbh;
271         $this->_table = $table;
272
273         ini_set('session.save_handler','user');
274         session_module_name('user'); // new style
275         session_set_save_handler(array(&$this, 'open'),
276                                  array(&$this, 'close'),
277                                  array(&$this, 'read'),
278                                  array(&$this, 'write'),
279                                  array(&$this, 'destroy'),
280                                  array(&$this, 'gc'));
281         return $this;
282     }
283
284     function _connect() {
285         global $DBParams;
286         static $parsed = false;
287         $dbh = &$this->_dbh;
288         if (!$dbh) {
289             if (!$parsed) $parsed = parseDSN($DBParams['dsn']);
290             $this->_dbh = &ADONewConnection($parsed['phptype']); // Probably only MySql works just now
291             $this->_dbh->Connect($parsed['hostspec'],$parsed['username'], 
292                                  $parsed['password'], $parsed['database']);
293             $dbh = &$this->_dbh;                             
294         }
295         return $dbh;
296     }
297     
298     function query($sql) {
299         return $this->_dbh->Execute($sql);
300     }
301
302     function quote($string) {
303         return $this->_dbh->qstr($string);
304     }
305
306     function _disconnect() {
307         if (0 and $this->_dbh)
308             $this->_dbh->close();
309     }
310
311     /**
312      * Opens a session.
313      *
314      * Actually this function is a fake for session_set_save_handle.
315      * @param  string $save_path a path to stored files
316      * @param  string $session_name a name of the concrete file
317      * @return boolean true just a variable to notify PHP that everything 
318      * is good.
319      * @access private
320      */
321     function open ($save_path, $session_name) {
322         //$this->log("_open($save_path, $session_name)");
323         return true;
324     }
325
326     /**
327      * Closes a session.
328      *
329      * This function is called just after <i>write</i> call.
330      *
331      * @return boolean true just a variable to notify PHP that everything 
332      * is good.
333      * @access private
334      */
335     function close() {
336         //$this->log("_close()");
337         return true;
338     }
339
340     /**
341      * Reads the session data from DB.
342      *
343      * @param  string $id an id of current session
344      * @return string
345      * @access private
346      */
347     function read ($id) {
348         //$this->log("_read($id)");
349         $dbh = &$this->_connect();
350         $table = $this->_table;
351         $qid = $dbh->qstr($id);
352         $res = '';
353         $row = $dbh->GetRow("SELECT sess_data FROM $table WHERE sess_id=$qid");
354         if ($row)
355             $res = $row[0];
356         $this->_disconnect();
357         if (!empty($res) and preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
358             $res = base64_decode($res);
359         return $res;
360     }
361   
362     /**
363      * Saves the session data into DB.
364      *
365      * Just  a  comment:       The  "write"  handler  is  not 
366      * executed until after the output stream is closed. Thus,
367      * output from debugging statements in the "write" handler
368      * will  never be seen in the browser. If debugging output
369      * is  necessary, it is suggested that the debug output be
370      * written to a file instead.
371      *
372      * @param  string $id
373      * @param  string $sess_data
374      * @return boolean true if data saved successfully  and false
375      * otherwise.
376      * @access private
377      */
378     function write ($id, $sess_data) {
379         
380         $dbh = &$this->_connect();
381         $table = $this->_table;
382         $qid = $dbh->qstr($id);
383         $qip = $dbh->qstr($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
384         $time = time();
385
386         // postgres can't handle binary data in a TEXT field.
387         if (isa($dbh, 'ADODB_postgres64'))
388             $sess_data = base64_encode($sess_data);
389         $qdata = $dbh->qstr($sess_data);
390         $rs = $dbh->Execute("UPDATE $table"
391                            . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
392                            . " WHERE sess_id=$qid");
393         if (! $dbh->Affected_Rows() )
394             $rs = $dbh->Execute("INSERT INTO $table"
395                                . " (sess_id, sess_data, sess_date, sess_ip)"
396                                . " VALUES ($qid, $qdata, $time, $qip)");
397         $result = ! $rs->EOF;
398         if ($result) $rs->free();                        
399         $this->_disconnect();
400         return $result;
401     }
402
403     /**
404      * Destroys a session.
405      *
406      * Removes a session from the table.
407      *
408      * @param  string $id
409      * @return boolean true 
410      * @access private
411      */
412     function destroy ($id) {
413         $dbh = &$this->_connect();
414         $table = $this->_table;
415         $qid = $dbh->qstr($id);
416
417         $dbh->Execute("DELETE FROM $table WHERE sess_id=$qid");
418
419         $this->_disconnect();
420         return true;     
421     }
422
423     /**
424      * Cleans out all expired sessions.
425      *
426      * @param  int $maxlifetime session's time to live.
427      * @return boolean true
428      * @access private
429      */
430     function gc ($maxlifetime) {
431         $dbh = &$this->_connect();
432         $table = $this->_table;
433         $threshold = time() - $maxlifetime;
434
435         $dbh->Execute("DELETE FROM $table WHERE sess_date < $threshold");
436
437         $this->_disconnect();
438         return true;
439     }
440
441     // WhoIsOnline support. 
442     // TODO: ip-accesstime dynamic blocking API
443     function currentSessions() {
444         $sessions = array();
445         $dbh = &$this->_connect();
446         $table = $this->_table;
447         $rs = $this->Execute("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
448         if ($rs->EOF) {
449             $rs->free();
450             return $sessions;
451         }
452         while (!$rs->EOF) {
453             $row = $rs->fetchRow();
454             $data = $row[0];
455             $date = $row[1];
456             $ip   = $row[2];
457             if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
458                 $data = base64_decode($data);
459             // session_data contains the <variable name> + "|" + <packed string>
460             // we need just the wiki_user object (might be array as well)
461             $user = strstr($data,"wiki_user|");
462             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
463                                 'date' => $date,
464                                 'ip' => $ip);
465             $rs->MoveNext();
466         }
467         $rs->free();
468         $this->_disconnect();
469         return $sessions;
470     }
471 }
472
473 /** DBA Sessions
474  *  session:
475  *    Index: session_id
476  *   Values: date : IP : data
477  */
478 class DB_Session_dba
479 extends DB_Session
480 {
481     var $_backend_type = "dba";
482
483     function DB_Session_dba (&$dbh, $table) {
484         $this->_dbh = $dbh;
485         ini_set('session.save_handler','user');
486         session_module_name('user'); // new style
487         session_set_save_handler(array(&$this, 'open'),
488                                  array(&$this, 'close'),
489                                  array(&$this, 'read'),
490                                  array(&$this, 'write'),
491                                  array(&$this, 'destroy'),
492                                  array(&$this, 'gc'));
493         return $this;
494     }
495
496     function quote($str) { return $str; }
497     function query($sql) { return false; }
498
499     function _connect() {
500         global $DBParams;
501         $dbh = &$this->_dbh;
502         if (!$dbh) {
503             $directory = '/tmp';
504             $prefix = 'wiki_';
505             $dba_handler = 'gdbm';
506             $timeout = 20;
507             extract($DBParams);
508             $dbfile = "$directory/$prefix" . 'session' . '.' . $dba_handler;
509             $dbh = new DbaDatabase($dbfile, false, $dba_handler);
510             $dbh->set_timeout($timeout);
511             if (!$dbh->open('c')) {
512                 trigger_error(sprintf(_("%s: Can't open dba database"), $dbfile), E_USER_ERROR);
513                 global $request;
514                 $request->finish(fmt("%s: Can't open dba database", $dbfile));
515             }
516             $this->_dbh = &$dbh;
517         }
518         return $dbh;
519     }
520
521     function _disconnect() {
522         if (0 and isset($this->_dbh))
523             $this->_dbh->close();
524     }
525
526     function open ($save_path, $session_name) {
527         $dbh = &$this->_connect();
528         $dbh->open();
529     }
530
531     function close() {
532         $dbh = &$this->_connect();
533         $dbh->close();
534     }
535
536     function read ($id) {
537         $dbh = &$this->_connect();
538         $result = $dbh->get($id);
539         if (!$result) {
540             return false;
541         }
542         list(,,$packed) = explode(':', $result, 3);
543         $data = unserialize($packed);
544         $this->_disconnect();
545         return $data;
546     }
547   
548     function write ($id, $sess_data) {
549         $dbh = &$this->_connect();
550         $time = time();
551         $ip = $GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR'];
552         $dbh->set($id,$time.':'.$ip.':'.$sess_data);
553         $this->_disconnect();
554         return true;
555     }
556
557     function destroy ($id) {
558         $dbh = &$this->_connect();
559         $dbh->delete($id);
560         $this->_disconnect();
561         return true;
562     }
563
564     function gc ($maxlifetime) {
565         $dbh = &$this->_connect();
566         $threshold = time() - $maxlifetime;
567         for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
568             $result = $dbh->get($id);
569             list($date,,) = explode(':', $result, 3);
570             //$dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
571             if ($date < $threshold)
572                 $dbh->delete($id);
573         }
574         $this->_disconnect();
575         return true;
576     }
577
578     // WhoIsOnline support. 
579     // TODO: ip-accesstime dynamic blocking API
580     function currentSessions() {
581         $sessions = array();
582         $dbh = &$this->_connect();
583         for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
584             $result = $dbh->get($id);
585             list($date,$ip,$packed) = explode(':', $result, 3);
586             $data = unserialize($packed);
587             // session_data contains the <variable name> + "|" + <packed string>
588             // we need just the wiki_user object (might be array as well)
589             $user = strstr($data,"wiki_user|");
590             $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
591                                 'date' => $date,
592                                 'ip' => $ip);
593         }
594         $this->_disconnect();
595         return $sessions;
596     }
597 }
598
599
600 // Local Variables:
601 // mode: php
602 // tab-width: 8
603 // c-basic-offset: 4
604 // c-hanging-comment-ender-p: nil
605 // indent-tabs-mode: nil
606 // End:
607 ?>