3 rcs_id('$Id: dbmlib.php,v 1.10 2001-06-22 21:24:40 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 // Initialize our globals:
26 function _dbname($base)
28 extract($GLOBALS['DBParams']);
29 return "$directory/${database}${prefix}${base}";
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'));
40 if (preg_match('@^/tmp\b@', $DBParams['directory']))
41 $DBWarning = "DBM files are in the /tmp directory.";
43 define('MAX_DBM_ATTEMPTS', $DBParams['timeout']);
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 @.
51 function OpenDataBase($dbname) {
52 global $WikiDB; // hash of all the DBM file names
55 while (list($key, $file) = each($WikiDB)) {
56 while (($dbi[$key] = @dbmopen($file, "c")) < 1) {
58 if ($numattempts > MAX_DBM_ATTEMPTS) {
59 ExitWiki("Cannot open database '$key' : '$file', giving up.");
68 function CloseDataBase($dbi) {
70 while (list($dbmfile, $dbihandle) = each($dbi)) {
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
81 function PadSerializedData($data) {
82 // calculate the next largest number divisible by 500
83 $nextincr = 500 * ceil(strlen($data) / 500);
85 $data = sprintf("%-${nextincr}s", $data);
89 // strip trailing whitespace from the serialized data
91 function UnPadSerializedData($data) {
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));
109 // Either insert or replace a key/value (a page)
110 function InsertPage($dbi, $pagename, $pagehash, $pagestore='wiki') {
112 if ($pagestore == 'wiki') { // a bit of a hack
113 $linklist = ExtractWikiPageLinks($pagehash['content']);
114 SetWikiPageLinks($dbi, $pagename, $linklist);
117 $pagedata = PadSerializedData(serialize($pagehash));
119 if (dbminsert($dbi[$pagestore], $pagename, $pagedata)) {
120 if (dbmreplace($dbi[$pagestore], $pagename, $pagedata)) {
121 ExitWiki("Error inserting page '$pagename'");
127 // for archiving pages to a separate dbm
128 function SaveCopyToArchive($dbi, $pagename, $pagehash) {
129 global $ArchivePageStore;
131 $pagedata = PadSerializedData(serialize($pagehash));
133 if (dbminsert($dbi[$ArchivePageStore], $pagename, $pagedata)) {
134 if (dbmreplace($dbi['archive'], $pagename, $pagedata)) {
135 ExitWiki("Error storing '$pagename' into archive");
141 function IsWikiPage($dbi, $pagename) {
142 return dbmexists($dbi['wiki'], $pagename);
146 function IsInArchive($dbi, $pagename) {
147 return dbmexists($dbi['archive'], $pagename);
151 function RemovePage($dbi, $pagename) {
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
157 $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
159 // remove page from fromlinks of pages it had links to
160 if (is_array($linkinfo)) { // page exists?
161 $tolinks = $linkinfo['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');
177 // remove page itself
178 dbmdelete($dbi['wikilinks'], $pagename);
183 // setup for title-search
184 function InitTitleSearch($dbi, $search) {
185 $pos['search'] = $search;
186 $pos['key'] = dbmfirstkey($dbi['wiki']);
192 // iterating through database
193 function TitleSearchNextMatch($dbi, &$pos) {
194 while ($pos['key']) {
196 $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
198 if (eregi($pos['search'], $page)) {
206 // setup for full-text search
207 function InitFullSearch($dbi, $search) {
208 return InitTitleSearch($dbi, $search);
212 //iterating through database
213 function FullSearchNextMatch($dbi, &$pos) {
214 while ($pos['key']) {
216 $pos['key'] = dbmnextkey($dbi['wiki'], $pos['key']);
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'];
231 ////////////////////////
232 // new database features
234 function IncreaseHitCount($dbi, $pagename) {
236 if (dbmexists($dbi['hitcount'], $pagename)) {
237 // increase the hit count
238 // echo "$pagename there, incrementing...<br>\n";
239 $count = dbmfetch($dbi['hitcount'], $pagename);
241 dbmreplace($dbi['hitcount'], $pagename, $count);
243 // add it, set the hit count to one
245 dbminsert($dbi['hitcount'], $pagename, $count);
250 function GetHitCount($dbi, $pagename) {
252 if (dbmexists($dbi['hitcount'], $pagename)) {
253 // increase the hit count
254 $count = dbmfetch($dbi['hitcount'], $pagename);
262 function InitMostPopular($dbi, $limit) {
263 // iterate through the whole dbm file for hit counts
264 // sort the results highest to lowest, and return
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.
272 $pagename = dbmfirstkey($dbi['hitcount']);
273 $score = dbmfetch($dbi['hitcount'], $pagename);
274 $res = array($pagename => (int) $score);
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)
282 $res[$pagename] = (int) $score; // add page to $res
283 } elseif ($score > $lowest) {
284 $oldres = $res; // save old result
286 $removed = 0; // nothing removed yet
287 $newlowest = $score; // new lowest score
288 $res[$pagename] = (int) $score; // add page to $res
290 while(list($pname, $pscore) = each($oldres)) {
291 if (!$removed and ($pscore = $lowest))
292 $removed = 1; // don't copy this entry
294 $res[$pname] = (int) $pscore;
295 if ($pscore < $newlowest)
296 $newlowest = $pscore;
299 $lowest = $newlowest;
303 arsort($res); // sort
310 function MostPopularNextMatch($dbi, &$res) {
312 // the return result is a two element array with 'hits'
313 // and 'pagename' as the keys
315 if (list($pagename, $hits) = each($res)) {
318 "pagename" => $pagename
327 function GetAllWikiPagenames($dbi) {
331 $namelist[$ctr] = $key = dbmfirstkey($dbi);
333 while ($key = dbmnextkey($dbi, $key)) {
335 $namelist[$ctr] = $key;
342 ////////////////////////////////////////////
343 // functionality for the wikilinks DBM file
345 // format of the 'wikilinks' DBM file :
347 // { tolinks => ( pagename => 1}, fromlinks => { pagename => 1 } }
349 // takes a page name, returns array of scored incoming and outgoing links
350 function GetWikiPageLinks($dbi, $pagename) {
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
359 $fromlinks = array();
360 // look up pages that link to $pagename
361 $pname = dbmfirstkey($dbi['wikilinks']);
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);
370 // get and sort the outgoing links
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']);
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);
384 // get and sort the incoming links
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']);
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);
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);
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) {
421 // Phase 1: fetch the relevant pairs from 'wikilinks' into $cache
422 // ---------------------------------------------------------------
424 // first the info for $pagename
425 $linkinfo = RetrievePage($dbi, $pagename, 'wikilinks');
426 if (is_array($linkinfo)) // page exists?
427 $cache[$pagename] = $linkinfo;
429 // create info for page
430 $cache[$pagename] = array( 'fromlinks' => array(),
433 // look up pages that link to $pagename
434 $pname = dbmfirstkey($dbi['wikilinks']);
436 $linkinfo = RetrievePage($dbi, $pname, 'wikilinks');
437 if ($linkinfo['tolinks'][$pagename])
438 $cache[$pagename]['fromlinks'][$pname] = 1;
439 $pname = dbmnextkey($dbi['wikilinks'], $pname);
443 // then the info for the pages that $pagename used to point to
444 $oldTolinks = $cache[$pagename]['tolinks'];
446 while (list($link, $dummy) = each($oldTolinks)) {
447 $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
448 if (is_array($linkinfo))
449 $cache[$link] = $linkinfo;
452 // finally the info for the pages that $pagename will point to
454 while (list($link, $dummy) = each($linklist)) {
455 $linkinfo = RetrievePage($dbi, $link, 'wikilinks');
456 if (is_array($linkinfo))
457 $cache[$link] = $linkinfo;
460 // Phase 2: delete the old links
461 // ---------------------------------------------------------------
463 // delete the old tolinks for $pagename
464 // $cache[$pagename]['tolinks'] = array();
465 // (overwritten anyway in Phase 3)
467 // remove $pagename from the fromlinks of pages in $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;
482 // Phase 3: add the new links
483 // ---------------------------------------------------------------
485 // set the new tolinks for $pagename
486 $cache[$pagename]['tolinks'] = $linklist;
488 // add $pagename to the fromlinks of pages in $linklist
490 while (list($link, $dummy) = each($linklist)) {
491 if ($cache[$link]) // existing page?
492 $cache[$link]['fromlinks'][$pagename] = 1;
495 // Phase 4: write $cache back to 'wikilinks'
496 // ---------------------------------------------------------------
499 while (list($link,$fromAndTolinks) = each($cache))
500 InsertPage($dbi, $link, $fromAndTolinks, 'wikilinks');
507 // c-file-style: "ellemtel"