3 rcs_id('$Id: dbmlib.php,v 1.7 2001-01-31 03:11:25 wainstead Exp $');
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)
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 @.
30 function OpenDataBase($dbname) {
31 global $WikiDB; // hash of all the DBM file names
34 while (list($key, $file) = each($WikiDB)) {
35 while (($dbi[$key] = @dbmopen($file, "c")) < 1) {
37 if ($numattempts > MAX_DBM_ATTEMPTS) {
38 ExitWiki("Cannot open database '$key' : '$file', giving up.");
47 function CloseDataBase($dbi) {
49 while (list($dbmfile, $dbihandle) = each($dbi)) {
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
60 function PadSerializedData($data) {
61 // calculate the next largest number divisible by 500
62 $nextincr = 500 * ceil(strlen($data) / 500);
64 $data = sprintf("%-${nextincr}s", $data);
68 // strip trailing whitespace from the serialized data
70 function UnPadSerializedData($data) {
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));
88 // Either insert or replace a key/value (a page)
89 function InsertPage($dbi, $pagename, $pagehash, $pagestore='wiki') {
91 if ($pagestore == 'wiki') { // a bit of a hack
92 $linklist = ExtractWikiPageLinks($pagehash['content']);
93 SetWikiPageLinks($dbi, $pagename, $linklist);
96 $pagedata = PadSerializedData(serialize($pagehash));
98 if (dbminsert($dbi[$pagestore], $pagename, $pagedata)) {
99 if (dbmreplace($dbi[$pagestore], $pagename, $pagedata)) {
100 ExitWiki("Error inserting page '$pagename'");
106 // for archiving pages to a separate dbm
107 function SaveCopyToArchive($dbi, $pagename, $pagehash) {
108 global $ArchivePageStore;
110 $pagedata = PadSerializedData(serialize($pagehash));
112 if (dbminsert($dbi[$ArchivePageStore], $pagename, $pagedata)) {
113 if (dbmreplace($dbi['archive'], $pagename, $pagedata)) {
114 ExitWiki("Error storing '$pagename' into archive");
120 function IsWikiPage($dbi, $pagename) {
121 return dbmexists($dbi['wiki'], $pagename);
125 function IsInArchive($dbi, $pagename) {
126 return dbmexists($dbi['archive'], $pagename);
130 function RemovePage($dbi, $pagename) {
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
136 $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
138 // remove page from fromlinks of pages it had links to
139 if (is_array($linkinfo)) { // page exists?
140 $tolinks = $linkinfo['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');
156 // remove page itself
157 dbmdelete($dbi['wikilinks'], $pagename);
162 // setup for title-search
163 function InitTitleSearch($dbi, $search) {
164 $pos['search'] = $search;
165 $pos['key'] = dbmfirstkey($dbi['wiki']);
171 // iterating through database
172 function TitleSearchNextMatch($dbi, &$pos) {
173 while ($pos['key']) {
175 $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
177 if (eregi($pos['search'], $page)) {
185 // setup for full-text search
186 function InitFullSearch($dbi, $search) {
187 return InitTitleSearch($dbi, $search);
191 //iterating through database
192 function FullSearchNextMatch($dbi, &$pos) {
193 while ($pos['key']) {
195 $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
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'];
210 ////////////////////////
211 // new database features
213 function IncreaseHitCount($dbi, $pagename) {
215 if (dbmexists($dbi['hitcount'], $pagename)) {
216 // increase the hit count
217 // echo "$pagename there, incrementing...<br>\n";
218 $count = dbmfetch($dbi['hitcount'], $pagename);
220 dbmreplace($dbi['hitcount'], $pagename, $count);
222 // add it, set the hit count to one
224 dbminsert($dbi['hitcount'], $pagename, $count);
229 function GetHitCount($dbi, $pagename) {
231 if (dbmexists($dbi['hitcount'], $pagename)) {
232 // increase the hit count
233 $count = dbmfetch($dbi['hitcount'], $pagename);
241 function InitMostPopular($dbi, $limit) {
242 // iterate through the whole dbm file for hit counts
243 // sort the results highest to lowest, and return
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.
251 $pagename = dbmfirstkey($dbi['hitcount']);
252 $score = dbmfetch($dbi['hitcount'], $pagename);
253 $res = array($pagename => (int) $score);
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)
261 $res[$pagename] = (int) $score; // add page to $res
262 } elseif ($score > $lowest) {
263 $oldres = $res; // save old result
265 $removed = 0; // nothing removed yet
266 $newlowest = $score; // new lowest score
267 $res[$pagename] = (int) $score; // add page to $res
269 while(list($pname, $pscore) = each($oldres)) {
270 if (!$removed and ($pscore = $lowest))
271 $removed = 1; // don't copy this entry
273 $res[$pname] = (int) $pscore;
274 if ($pscore < $newlowest)
275 $newlowest = $pscore;
278 $lowest = $newlowest;
282 arsort($res); // sort
289 function MostPopularNextMatch($dbi, &$res) {
291 // the return result is a two element array with 'hits'
292 // and 'pagename' as the keys
294 if (list($pagename, $hits) = each($res)) {
297 "pagename" => $pagename
306 function GetAllWikiPagenames($dbi) {
310 $namelist[$ctr] = $key = dbmfirstkey($dbi);
312 while ($key = dbmnextkey($dbi, $key)) {
314 $namelist[$ctr] = $key;
321 ////////////////////////////////////////////
322 // functionality for the wikilinks DBM file
324 // format of the 'wikilinks' DBM file :
326 // { tolinks => ( pagename => 1}, fromlinks => { pagename => 1 } }
328 // takes a page name, returns array of scored incoming and outgoing links
329 function GetWikiPageLinks($dbi, $pagename) {
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
338 $fromlinks = array();
339 // look up pages that link to $pagename
340 $pname = dbmfirstkey($dbi['wikilinks']);
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);
349 // get and sort the outgoing links
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']);
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);
363 // get and sort the incoming links
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']);
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);
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);
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) {
400 // Phase 1: fetch the relevant pairs from 'wikilinks' into $cache
401 // ---------------------------------------------------------------
403 // first the info for $pagename
404 $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
405 if (is_array($linkinfo)) // page exists?
406 $cache[$pagename] = $linkinfo;
408 // create info for page
409 $cache[$pagename] = array( 'fromlinks' => array(),
412 // look up pages that link to $pagename
413 $pname = dbmfirstkey($dbi['wikilinks']);
415 $linkinfo = RetrievePage($dbi, $pname, 'wikilinks');
416 if ($linkinfo['tolinks'][$pagename])
417 $cache[$pagename]['fromlinks'][$pname] = 1;
418 $pname = dbmnextkey($dbi['wikilinks'], $pname);
422 // then the info for the pages that $pagename used to point to
423 $oldTolinks = $cache[$pagename]['tolinks'];
425 while (list($link, $dummy) = each($oldTolinks)) {
426 $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
427 if (is_array($linkinfo))
428 $cache[$link] = $linkinfo;
431 // finally the info for the pages that $pagename will point to
433 while (list($link, $dummy) = each($linklist)) {
434 $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
435 if (is_array($linkinfo))
436 $cache[$link] = $linkinfo;
439 // Phase 2: delete the old links
440 // ---------------------------------------------------------------
442 // delete the old tolinks for $pagename
443 // $cache[$pagename]['tolinks'] = array();
444 // (overwritten anyway in Phase 3)
446 // remove $pagename from the fromlinks of pages in $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;
461 // Phase 3: add the new links
462 // ---------------------------------------------------------------
464 // set the new tolinks for $pagename
465 $cache[$pagename]['tolinks'] = $linklist;
467 // add $pagename to the fromlinks of pages in $linklist
469 while (list($link, $dummy) = each($linklist)) {
470 if ($cache[$link]) // existing page?
471 $cache[$link]['fromlinks'][$pagename] = 1;
474 // Phase 4: write $cache back to 'wikilinks'
475 // ---------------------------------------------------------------
478 while (list($link,$fromAndTolinks) = each($cache))
479 InsertPage($dbi, $link, $fromAndTolinks, 'wikilinks');