3 rcs_id('$Id: dbmlib.php,v 1.9 2001-02-17 05:36:21 dairiki 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)
24 // Initialize our globals:
25 function _dbname($base)
27 extract($GLOBALS['DBParams']);
28 return "$directory/${database}${prefix}${base}";
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'));
39 if (preg_match('@^/tmp\b@', $DBParams['directory']))
40 $DBWarning = "DBM files are in the /tmp directory.";
42 define('MAX_DBM_ATTEMPTS', $DBParams['timeout']);
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 @.
50 function OpenDataBase($dbname) {
51 global $WikiDB; // hash of all the DBM file names
54 while (list($key, $file) = each($WikiDB)) {
55 while (($dbi[$key] = @dbmopen($file, "c")) < 1) {
57 if ($numattempts > MAX_DBM_ATTEMPTS) {
58 ExitWiki("Cannot open database '$key' : '$file', giving up.");
67 function CloseDataBase($dbi) {
69 while (list($dbmfile, $dbihandle) = each($dbi)) {
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
80 function PadSerializedData($data) {
81 // calculate the next largest number divisible by 500
82 $nextincr = 500 * ceil(strlen($data) / 500);
84 $data = sprintf("%-${nextincr}s", $data);
88 // strip trailing whitespace from the serialized data
90 function UnPadSerializedData($data) {
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));
108 // Either insert or replace a key/value (a page)
109 function InsertPage($dbi, $pagename, $pagehash, $pagestore='wiki') {
111 if ($pagestore == 'wiki') { // a bit of a hack
112 $linklist = ExtractWikiPageLinks($pagehash['content']);
113 SetWikiPageLinks($dbi, $pagename, $linklist);
116 $pagedata = PadSerializedData(serialize($pagehash));
118 if (dbminsert($dbi[$pagestore], $pagename, $pagedata)) {
119 if (dbmreplace($dbi[$pagestore], $pagename, $pagedata)) {
120 ExitWiki("Error inserting page '$pagename'");
126 // for archiving pages to a separate dbm
127 function SaveCopyToArchive($dbi, $pagename, $pagehash) {
128 global $ArchivePageStore;
130 $pagedata = PadSerializedData(serialize($pagehash));
132 if (dbminsert($dbi[$ArchivePageStore], $pagename, $pagedata)) {
133 if (dbmreplace($dbi['archive'], $pagename, $pagedata)) {
134 ExitWiki("Error storing '$pagename' into archive");
140 function IsWikiPage($dbi, $pagename) {
141 return dbmexists($dbi['wiki'], $pagename);
145 function IsInArchive($dbi, $pagename) {
146 return dbmexists($dbi['archive'], $pagename);
150 function RemovePage($dbi, $pagename) {
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
156 $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
158 // remove page from fromlinks of pages it had links to
159 if (is_array($linkinfo)) { // page exists?
160 $tolinks = $linkinfo['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');
176 // remove page itself
177 dbmdelete($dbi['wikilinks'], $pagename);
182 // setup for title-search
183 function InitTitleSearch($dbi, $search) {
184 $pos['search'] = $search;
185 $pos['key'] = dbmfirstkey($dbi['wiki']);
191 // iterating through database
192 function TitleSearchNextMatch($dbi, &$pos) {
193 while ($pos['key']) {
195 $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
197 if (eregi($pos['search'], $page)) {
205 // setup for full-text search
206 function InitFullSearch($dbi, $search) {
207 return InitTitleSearch($dbi, $search);
211 //iterating through database
212 function FullSearchNextMatch($dbi, &$pos) {
213 while ($pos['key']) {
215 $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
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'];
230 ////////////////////////
231 // new database features
233 function IncreaseHitCount($dbi, $pagename) {
235 if (dbmexists($dbi['hitcount'], $pagename)) {
236 // increase the hit count
237 // echo "$pagename there, incrementing...<br>\n";
238 $count = dbmfetch($dbi['hitcount'], $pagename);
240 dbmreplace($dbi['hitcount'], $pagename, $count);
242 // add it, set the hit count to one
244 dbminsert($dbi['hitcount'], $pagename, $count);
249 function GetHitCount($dbi, $pagename) {
251 if (dbmexists($dbi['hitcount'], $pagename)) {
252 // increase the hit count
253 $count = dbmfetch($dbi['hitcount'], $pagename);
261 function InitMostPopular($dbi, $limit) {
262 // iterate through the whole dbm file for hit counts
263 // sort the results highest to lowest, and return
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.
271 $pagename = dbmfirstkey($dbi['hitcount']);
272 $score = dbmfetch($dbi['hitcount'], $pagename);
273 $res = array($pagename => (int) $score);
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)
281 $res[$pagename] = (int) $score; // add page to $res
282 } elseif ($score > $lowest) {
283 $oldres = $res; // save old result
285 $removed = 0; // nothing removed yet
286 $newlowest = $score; // new lowest score
287 $res[$pagename] = (int) $score; // add page to $res
289 while(list($pname, $pscore) = each($oldres)) {
290 if (!$removed and ($pscore = $lowest))
291 $removed = 1; // don't copy this entry
293 $res[$pname] = (int) $pscore;
294 if ($pscore < $newlowest)
295 $newlowest = $pscore;
298 $lowest = $newlowest;
302 arsort($res); // sort
309 function MostPopularNextMatch($dbi, &$res) {
311 // the return result is a two element array with 'hits'
312 // and 'pagename' as the keys
314 if (list($pagename, $hits) = each($res)) {
317 "pagename" => $pagename
326 function GetAllWikiPagenames($dbi) {
330 $namelist[$ctr] = $key = dbmfirstkey($dbi);
332 while ($key = dbmnextkey($dbi, $key)) {
334 $namelist[$ctr] = $key;
341 ////////////////////////////////////////////
342 // functionality for the wikilinks DBM file
344 // format of the 'wikilinks' DBM file :
346 // { tolinks => ( pagename => 1}, fromlinks => { pagename => 1 } }
348 // takes a page name, returns array of scored incoming and outgoing links
349 function GetWikiPageLinks($dbi, $pagename) {
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
358 $fromlinks = array();
359 // look up pages that link to $pagename
360 $pname = dbmfirstkey($dbi['wikilinks']);
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);
369 // get and sort the outgoing links
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']);
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);
383 // get and sort the incoming links
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']);
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);
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);
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) {
420 // Phase 1: fetch the relevant pairs from 'wikilinks' into $cache
421 // ---------------------------------------------------------------
423 // first the info for $pagename
424 $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
425 if (is_array($linkinfo)) // page exists?
426 $cache[$pagename] = $linkinfo;
428 // create info for page
429 $cache[$pagename] = array( 'fromlinks' => array(),
432 // look up pages that link to $pagename
433 $pname = dbmfirstkey($dbi['wikilinks']);
435 $linkinfo = RetrievePage($dbi, $pname, 'wikilinks');
436 if ($linkinfo['tolinks'][$pagename])
437 $cache[$pagename]['fromlinks'][$pname] = 1;
438 $pname = dbmnextkey($dbi['wikilinks'], $pname);
442 // then the info for the pages that $pagename used to point to
443 $oldTolinks = $cache[$pagename]['tolinks'];
445 while (list($link, $dummy) = each($oldTolinks)) {
446 $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
447 if (is_array($linkinfo))
448 $cache[$link] = $linkinfo;
451 // finally the info for the pages that $pagename will point to
453 while (list($link, $dummy) = each($linklist)) {
454 $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
455 if (is_array($linkinfo))
456 $cache[$link] = $linkinfo;
459 // Phase 2: delete the old links
460 // ---------------------------------------------------------------
462 // delete the old tolinks for $pagename
463 // $cache[$pagename]['tolinks'] = array();
464 // (overwritten anyway in Phase 3)
466 // remove $pagename from the fromlinks of pages in $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;
481 // Phase 3: add the new links
482 // ---------------------------------------------------------------
484 // set the new tolinks for $pagename
485 $cache[$pagename]['tolinks'] = $linklist;
487 // add $pagename to the fromlinks of pages in $linklist
489 while (list($link, $dummy) = each($linklist)) {
490 if ($cache[$link]) // existing page?
491 $cache[$link]['fromlinks'][$pagename] = 1;
494 // Phase 4: write $cache back to 'wikilinks'
495 // ---------------------------------------------------------------
498 while (list($link,$fromAndTolinks) = each($cache))
499 InsertPage($dbi, $link, $fromAndTolinks, 'wikilinks');
506 // c-file-style: "ellemtel"