1<?php 2/** 3 * 4 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 5 * @author Christoph Clausen <christoph.clausen@unige.ch> 6 */ 7 8// must be run within Dokuwiki 9if(!defined('DOKU_INC')) die(); 10 11class datatemplate_cache { 12 13 /** 14 * Remove any metadata that might have been stored by previous versions 15 * of the plugin. 16 * @param Doku_Renderer_metadata $renderer an instance of the dokuwiki renderer. 17 */ 18 public function removeMeta(&$renderer) { 19 global $ID; 20 foreach(array_keys($renderer->persistent) as $key) { 21 if(substr($key, 0, 12) == 'datatemplate') { 22 unset($renderer->meta[$key]); 23 unset($renderer->persistent[$key]); 24 } 25 } 26 } 27 28 /** 29 * Check and generation of cached data to avoid 30 * repeated use of the SQLite queries, which can end up 31 * being quite slow. 32 * 33 * @param array $data from the handle function 34 * @param string $sql stripped SQL for hash generation 35 * @param syntax_plugin_datatemplate_list $dtlist reference, the calling datatemplate list instance 36 */ 37 public function checkAndBuildCache($data, $sql, &$dtlist) { 38 // We know that the datatemplate list has a datahelper. 39 /** @var $sqlite helper_plugin_sqlite */ 40 $sqlite = $dtlist->dthlp->_getDB(); 41 42 // Build minimalistic data array for checking the cache 43 $dtcc = array(); 44 $dtcc['cols'] = array( 45 '%pageid%' => array( 46 'multi' => '', 47 'key' => '%pageid%', 48 'title' => 'Title', 49 'type' => 'page', 50 )); 51 // Apply also filters. Note, though, that (probably) only filters with respect 52 // to the pageid are actually considered. 53 $dtcc['filter'] = $data['filter']; 54 $sqlcc = $dtlist->_buildSQL($dtcc); 55 $res = $sqlite->query($sqlcc); 56 $pageids = $sqlite->res2arr($res, $assoc = false); 57 58 // Ask dokuwiki for cache file name 59 $cachefile = getCacheName($sql, '.datatemplate'); 60 if(file_exists($cachefile)) 61 $cachedate = filemtime($cachefile); 62 else 63 $cachedate = 0; 64 65 $latest = 0; 66 if($cachedate) { 67 // Check for newest page in SQL result 68 foreach($pageids as $pageid) { 69 $modified = filemtime(wikiFN($pageid[0])); 70 $latest = ($modified > $latest) ? $modified : $latest; 71 } 72 } 73 if(!$cachedate || $latest > (int) $cachedate || isset($_REQUEST['purge'])) { 74 $res = $sqlite->query($sql); 75 $rows = $sqlite->res2arr($res, $assoc = false); 76 file_put_contents($cachefile, serialize($rows), LOCK_EX); 77 } else { 78 // We arrive here when the cache seems up-to-date. However, 79 // it is possible that the cache contains items which should 80 // no longer be there. We need to find those and remove those. 81 82 // $pageids is an array of arrays, where the latter only contain 83 // one entry, the pageid. We need get rid of the second level of arrays: 84 foreach($pageids as $k => $v) { 85 $pageids[$k] = trim($v[0]); 86 } 87 88 // Then create map id->index for the pages that should be there. 89 $dataitems = array_flip($pageids); 90 91 // Do the same things for the pages that ARE there. 92 // Figure out which row-element is the page id. 93 94 $idx = 0; 95 foreach($data['cols'] as $key=>$value) { 96 if($key == '%pageid%') break; 97 $idx++; 98 } 99 $cache = $this->getData($sql); 100 $cacheitems = array(); 101 foreach($cache as $num=>$row) { 102 $cacheitems[trim($row[$idx])] = $num; 103 } 104 // Now calculate the difference and update cache if necessary. 105 $diff = array_diff_key($cacheitems, $dataitems); 106 if(count($diff) > 0) { 107 foreach($diff as $key => $num) { 108 unset($cache[$num]); 109 } 110 file_put_contents($cachefile, serialize($cache), LOCK_EX); 111 } 112 } 113 } 114 115 /** 116 * Retrieve cached data. 117 * @param string $sql the stripped sql for the data request 118 * @return Array containing the rows of the cached sql result 119 */ 120 public function getData($sql) { 121 $cachefile = getCacheName($sql, '.datatemplate'); 122 $datastr = file_get_contents($cachefile); 123 $data = unserialize($datastr); 124 return $data; 125 } 126}