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