]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/dbmlib.php
Fix typo.
[SourceForge/phpwiki.git] / lib / dbmlib.php
1 <?php  
2
3    rcs_id('$Id: dbmlib.php,v 1.9 2001-02-17 05:36:21 dairiki Exp $');
4
5    /*
6       Database functions:
7
8       OpenDataBase($table)
9       CloseDataBase($dbi)
10       RetrievePage($dbi, $pagename, $pagestore)
11       InsertPage($dbi, $pagename, $pagehash)
12       SaveCopyToArchive($dbi, $pagename, $pagehash) 
13       IsWikiPage($dbi, $pagename)
14       InitTitleSearch($dbi, $search)
15       TitleSearchNextMatch($dbi, $res)
16       InitFullSearch($dbi, $search)
17       FullSearchNextMatch($dbi, $res)
18       IncreaseHitCount($dbi, $pagename)
19       GetHitCount($dbi, $pagename)
20       InitMostPopular($dbi, $limit)
21       MostPopularNextMatch($dbi, $res)
22    */
23
24 // Initialize our globals:
25 function _dbname($base)
26 {
27   extract($GLOBALS['DBParams']);
28   return "$directory/${database}${prefix}${base}";
29 }
30
31 $WikiPageStore = "wiki";
32 $ArchivePageStore = "archive";
33 $WikiDB = array('wiki'          => _dbname('pagesdb'),
34                 'archive'       => _dbname('archivedb'),
35                 'wikilinks'     => _dbname('linksdb'),
36                 'hottopics'     => _dbname('hottopicsdb'),
37                 'hitcount'      => _dbname('hitcountdb'));
38
39 if (preg_match('@^/tmp\b@', $DBParams['directory']))
40    $DBWarning = "DBM files are in the /tmp directory.";
41
42 define('MAX_DBM_ATTEMPTS', $DBParams['timeout']);
43   
44
45    // open a database and return the handle
46    // loop until we get a handle; php has its own
47    // locking mechanism, thank god.
48    // Suppress ugly error message with @.
49
50    function OpenDataBase($dbname) {
51       global $WikiDB; // hash of all the DBM file names
52
53       reset($WikiDB);
54       while (list($key, $file) = each($WikiDB)) {
55          while (($dbi[$key] = @dbmopen($file, "c")) < 1) {
56             $numattempts++;
57             if ($numattempts > MAX_DBM_ATTEMPTS) {
58                ExitWiki("Cannot open database '$key' : '$file', giving up.");
59             }
60             sleep(1);
61          }
62       }
63       return $dbi;
64    }
65
66
67    function CloseDataBase($dbi) {
68       reset($dbi);
69       while (list($dbmfile, $dbihandle) = each($dbi)) {
70          dbmclose($dbihandle);
71       }
72       return;
73    }
74
75
76    // take a serialized hash, return same padded out to
77    // the next largest number bytes divisible by 500. This
78    // is to save disk space in the long run, since DBM files
79    // leak memory.
80    function PadSerializedData($data) {
81       // calculate the next largest number divisible by 500
82       $nextincr = 500 * ceil(strlen($data) / 500);
83       // pad with spaces
84       $data = sprintf("%-${nextincr}s", $data);
85       return $data;
86    }
87
88    // strip trailing whitespace from the serialized data 
89    // structure.
90    function UnPadSerializedData($data) {
91       return chop($data);
92    }
93
94
95
96    // Return hash of page + attributes or default
97    function RetrievePage($dbi, $pagename, $pagestore) {
98       if ($data = dbmfetch($dbi[$pagestore], $pagename)) {
99          // unserialize $data into a hash
100          $pagehash = unserialize(UnPadSerializedData($data));
101          return $pagehash;
102       } else {
103          return -1;
104       }
105    }
106
107
108    // Either insert or replace a key/value (a page)
109    function InsertPage($dbi, $pagename, $pagehash, $pagestore='wiki') {
110
111       if ($pagestore == 'wiki') {       // a bit of a hack
112          $linklist = ExtractWikiPageLinks($pagehash['content']);
113          SetWikiPageLinks($dbi, $pagename, $linklist);
114       }
115
116       $pagedata = PadSerializedData(serialize($pagehash));
117
118       if (dbminsert($dbi[$pagestore], $pagename, $pagedata)) {
119          if (dbmreplace($dbi[$pagestore], $pagename, $pagedata)) {
120             ExitWiki("Error inserting page '$pagename'");
121          }
122       } 
123    }
124
125
126    // for archiving pages to a separate dbm
127    function SaveCopyToArchive($dbi, $pagename, $pagehash) {
128       global $ArchivePageStore;
129
130       $pagedata = PadSerializedData(serialize($pagehash));
131
132       if (dbminsert($dbi[$ArchivePageStore], $pagename, $pagedata)) {
133          if (dbmreplace($dbi['archive'], $pagename, $pagedata)) {
134             ExitWiki("Error storing '$pagename' into archive");
135          }
136       } 
137    }
138
139
140    function IsWikiPage($dbi, $pagename) {
141       return dbmexists($dbi['wiki'], $pagename);
142    }
143
144
145    function IsInArchive($dbi, $pagename) {
146       return dbmexists($dbi['archive'], $pagename);
147    }
148
149
150    function RemovePage($dbi, $pagename) {
151
152       dbmdelete($dbi['wiki'], $pagename);       // report error if this fails? 
153       dbmdelete($dbi['archive'], $pagename);    // no error if this fails
154       dbmdelete($dbi['hitcount'], $pagename);   // no error if this fails
155
156       $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
157       
158       // remove page from fromlinks of pages it had links to
159       if (is_array($linkinfo)) {        // page exists?
160          $tolinks = $linkinfo['tolinks'];       
161          reset($tolinks);                       
162          while (list($tolink, $dummy) = each($tolinks)) {
163             $tolinkinfo = RetrievePage($dbi, $tolink, 'wikilinks');
164             if (is_array($tolinkinfo)) {                // page found?
165                $oldFromlinks = $tolinkinfo['fromlinks'];
166                $tolinkinfo['fromlinks'] = array();      // erase fromlinks
167                reset($oldFromlinks);
168                while (list($fromlink, $dummy) = each($oldFromlinks)) {
169                   if ($fromlink != $pagename)           // not to be erased? 
170                      $tolinkinfo['fromlinks'][$fromlink] = 1; // put link back
171                }                        // put link info back in DBM file
172                InsertPage($dbi, $tolink, $tolinkinfo, 'wikilinks');
173             }
174          }
175
176          // remove page itself     
177          dbmdelete($dbi['wikilinks'], $pagename);      
178       }
179    }
180
181
182    // setup for title-search
183    function InitTitleSearch($dbi, $search) {
184       $pos['search'] = $search;
185       $pos['key'] = dbmfirstkey($dbi['wiki']);
186
187       return $pos;
188    }
189
190
191    // iterating through database
192    function TitleSearchNextMatch($dbi, &$pos) {
193       while ($pos['key']) {
194          $page = $pos['key'];
195          $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
196
197          if (eregi($pos['search'], $page)) {
198             return $page;
199          }
200       }
201       return 0;
202    }
203
204
205    // setup for full-text search
206    function InitFullSearch($dbi, $search) {
207       return InitTitleSearch($dbi, $search);
208    }
209
210
211    //iterating through database
212    function FullSearchNextMatch($dbi, &$pos) {
213       while ($pos['key']) {
214          $key = $pos['key'];
215          $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
216
217          $pagedata = dbmfetch($dbi['wiki'], $key);
218          // test the serialized data
219          if (eregi($pos['search'], $pagedata)) {
220             $page['pagename'] = $key;
221             $pagedata = unserialize(UnPadSerializedData($pagedata));
222             $page['content'] = $pagedata['content'];
223             return $page;
224          }
225       }
226       return 0;
227    }
228
229
230    ////////////////////////
231    // new database features
232
233    function IncreaseHitCount($dbi, $pagename) {
234
235       if (dbmexists($dbi['hitcount'], $pagename)) {
236          // increase the hit count
237          // echo "$pagename there, incrementing...<br>\n";
238          $count = dbmfetch($dbi['hitcount'], $pagename);
239          $count++;
240          dbmreplace($dbi['hitcount'], $pagename, $count);
241       } else {
242          // add it, set the hit count to one
243          $count = 1;
244          dbminsert($dbi['hitcount'], $pagename, $count);
245       }
246    }
247
248
249    function GetHitCount($dbi, $pagename) {
250
251       if (dbmexists($dbi['hitcount'], $pagename)) {
252          // increase the hit count
253          $count = dbmfetch($dbi['hitcount'], $pagename);
254          return $count;
255       } else {
256          return 0;
257       }
258    }
259
260
261    function InitMostPopular($dbi, $limit) {
262       // iterate through the whole dbm file for hit counts
263       // sort the results highest to lowest, and return 
264       // n..$limit results
265
266       // Because sorting all the pages may be a lot of work
267       // we only get the top $limit. A page is only added if it's score is
268       // higher than the lowest score in the list. If the list is full then
269       // one of the pages with the lowest scores is removed.
270
271       $pagename = dbmfirstkey($dbi['hitcount']);
272       $score = dbmfetch($dbi['hitcount'], $pagename);
273       $res = array($pagename => (int) $score);
274       $lowest = $score;
275
276       while ($pagename = dbmnextkey($dbi['hitcount'], $pagename)) {
277          $score = dbmfetch($dbi['hitcount'], $pagename);      
278          if (count($res) < $limit) {    // room left in $res?
279             if ($score < $lowest)
280                $lowest = $score;
281             $res[$pagename] = (int) $score;     // add page to $res
282          } elseif ($score > $lowest) {
283             $oldres = $res;             // save old result
284             $res = array();
285             $removed = 0;               // nothing removed yet
286             $newlowest = $score;        // new lowest score
287             $res[$pagename] = (int) $score;     // add page to $res         
288             reset($oldres);
289             while(list($pname, $pscore) = each($oldres)) {
290                if (!$removed and ($pscore = $lowest))
291                   $removed = 1;         // don't copy this entry
292                else {
293                   $res[$pname] = (int) $pscore;
294                   if ($pscore < $newlowest)
295                      $newlowest = $pscore;
296                }
297             }
298             $lowest = $newlowest;
299          }
300       }
301        
302       arsort($res);             // sort
303       reset($res);
304        
305       return($res);
306    }
307
308
309    function MostPopularNextMatch($dbi, &$res) {
310
311       // the return result is a two element array with 'hits'
312       // and 'pagename' as the keys
313
314       if (list($pagename, $hits) = each($res)) {
315          $nextpage = array(
316             "hits" => $hits,
317             "pagename" => $pagename
318          );
319          return $nextpage;
320       } else {
321          return 0;
322       }
323    } 
324
325
326    function GetAllWikiPagenames($dbi) {
327       $namelist = array();
328       $ctr = 0;
329
330       $namelist[$ctr] = $key = dbmfirstkey($dbi);
331
332       while ($key = dbmnextkey($dbi, $key)) {
333          $ctr++;
334          $namelist[$ctr] = $key;
335       }
336
337       return $namelist;
338    }
339
340
341    ////////////////////////////////////////////
342    // functionality for the wikilinks DBM file
343
344    // format of the 'wikilinks' DBM file :
345    // pagename =>
346    //    { tolinks => ( pagename => 1}, fromlinks => { pagename => 1 } }
347
348    // takes a page name, returns array of scored incoming and outgoing links
349    function GetWikiPageLinks($dbi, $pagename) {
350
351       $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
352       if (is_array($linkinfo))  {               // page exists?
353          $tolinks = $linkinfo['tolinks'];       // outgoing links
354          $fromlinks = $linkinfo['fromlinks'];   // incoming links
355       } else {          // new page, but pages may already point to it
356          // create info for page
357          $tolinks = array();
358          $fromlinks = array();
359          // look up pages that link to $pagename
360          $pname = dbmfirstkey($dbi['wikilinks']);
361          while ($pname) {
362             $linkinfo = RetrievePage($dbi, $pname, 'wikilinks');
363             if ($linkinfo['tolinks'][$pagename]) // $pname links to $pagename?
364                $fromlinks[$pname] = 1;
365             $pname = dbmnextkey($dbi['wikilinks'], $pname);
366          }
367       }
368
369       // get and sort the outgoing links
370       $outlinks = array();      
371       reset($tolinks);                  // look up scores for tolinks
372       while(list($tolink, $dummy) = each($tolinks)) {
373          $toPage = RetrievePage($dbi, $tolink, 'wikilinks');
374          if (is_array($toPage))         // link to internal page?
375             $outlinks[$tolink] = count($toPage['fromlinks']);
376       }
377       arsort($outlinks);                // sort on score
378       $links['out'] = array();
379       reset($outlinks);                 // convert to right format
380       while(list($link, $score) = each($outlinks))
381          $links['out'][] = array($link, $score);
382
383       // get and sort the incoming links
384       $inlinks = array();
385       reset($fromlinks);                // look up scores for fromlinks
386       while(list($fromlink, $dummy) = each($fromlinks)) {
387          $fromPage = RetrievePage($dbi, $fromlink, 'wikilinks');
388          $inlinks[$fromlink] = count($fromPage['fromlinks']);
389       } 
390       arsort($inlinks);                 // sort on score
391       $links['in'] = array();
392       reset($inlinks);                  // convert to right format
393       while(list($link, $score) = each($inlinks))
394          $links['in'][] = array($link, $score);
395
396       // sort all the incoming and outgoing links
397       $allLinks = $outlinks;            // copy the outlinks
398       reset($inlinks);                  // add the inlinks
399       while(list($key, $value) = each($inlinks))
400          $allLinks[$key] = $value;
401       reset($allLinks);                 // lookup hits
402       while(list($key, $value) = each($allLinks))
403          $allLinks[$key] = (int) dbmfetch($dbi['hitcount'], $key);
404       arsort($allLinks);                // sort on hits
405       $links['popular'] = array();
406       reset($allLinks);                 // convert to right format
407       while(list($link, $hits) = each($allLinks))
408          $links['popular'][] = array($link, $hits);
409
410       return $links;
411    }
412
413
414    // takes page name, list of links it contains
415    // the $linklist is an array where the keys are the page names
416    function SetWikiPageLinks($dbi, $pagename, $linklist) {
417
418       $cache = array();
419
420       // Phase 1: fetch the relevant pairs from 'wikilinks' into $cache
421       // ---------------------------------------------------------------
422
423       // first the info for $pagename
424       $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
425       if (is_array($linkinfo))          // page exists?
426          $cache[$pagename] = $linkinfo;
427       else {
428          // create info for page
429          $cache[$pagename] = array( 'fromlinks' => array(),
430                                     'tolinks' => array()
431                              );
432          // look up pages that link to $pagename
433          $pname = dbmfirstkey($dbi['wikilinks']);
434          while ($pname) {
435             $linkinfo = RetrievePage($dbi, $pname, 'wikilinks');
436             if ($linkinfo['tolinks'][$pagename])
437                $cache[$pagename]['fromlinks'][$pname] = 1;
438             $pname = dbmnextkey($dbi['wikilinks'], $pname);
439          }
440       }
441                              
442       // then the info for the pages that $pagename used to point to 
443       $oldTolinks = $cache[$pagename]['tolinks'];
444       reset($oldTolinks);
445       while (list($link, $dummy) = each($oldTolinks)) {
446          $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
447          if (is_array($linkinfo))
448             $cache[$link] = $linkinfo;
449       }
450
451       // finally the info for the pages that $pagename will point to
452       reset($linklist);
453       while (list($link, $dummy) = each($linklist)) {
454          $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
455          if (is_array($linkinfo))
456             $cache[$link] = $linkinfo;
457       }
458               
459       // Phase 2: delete the old links
460       // ---------------------------------------------------------------
461
462       // delete the old tolinks for $pagename
463       // $cache[$pagename]['tolinks'] = array();
464       // (overwritten anyway in Phase 3)
465
466       // remove $pagename from the fromlinks of pages in $oldTolinks
467
468       reset($oldTolinks);
469       while (list($oldTolink, $dummy) = each($oldTolinks)) {
470          if ($cache[$oldTolink]) {      // links to existing page?
471             $oldFromlinks = $cache[$oldTolink]['fromlinks'];
472             $cache[$oldTolink]['fromlinks'] = array();  // erase fromlinks
473             reset($oldFromlinks);                       // comp. new fr.links
474             while (list($fromlink, $dummy) = each($oldFromlinks)) {
475                if ($fromlink != $pagename)
476                   $cache[$oldTolink]['fromlinks'][$fromlink] = 1;
477             }
478          }
479       }
480
481       // Phase 3: add the new links
482       // ---------------------------------------------------------------
483
484       // set the new tolinks for $pagename
485       $cache[$pagename]['tolinks'] = $linklist;
486
487       // add $pagename to the fromlinks of pages in $linklist
488       reset($linklist);
489       while (list($link, $dummy) = each($linklist)) {
490          if ($cache[$link])     // existing page?
491             $cache[$link]['fromlinks'][$pagename] = 1;
492       }
493
494       // Phase 4: write $cache back to 'wikilinks'
495       // ---------------------------------------------------------------
496
497       reset($cache);
498       while (list($link,$fromAndTolinks) = each($cache))
499          InsertPage($dbi, $link, $fromAndTolinks, 'wikilinks');
500
501    }
502
503 // For emacs users
504 // Local Variables:
505 // mode: php
506 // c-file-style: "ellemtel"
507 // End:   
508 ?>