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