1<?php 2 3namespace dokuwiki\Search\Index; 4 5use dokuwiki\Search\Exception\IndexLockException; 6 7/** 8 * Basic building block to access individual index files 9 * 10 * To be able to write to an index, a lock must be acquired. 11 */ 12abstract class AbstractIndex 13{ 14 /** @var string name of the index */ 15 protected $idx; 16 17 /** @var string suffix of the index */ 18 protected $suffix; 19 20 /** @var string full filename to the index */ 21 protected $filename; 22 23 /** @var bool has this instance acquired a lock? */ 24 protected $isWritable = false; 25 26 /** 27 * Initialize the index 28 * 29 * The $suffix argument is for an index that is split into multiple parts. 30 * Different index files should use different base names. 31 * 32 * When $isWritable is true, a lock is acquired immediately 33 * 34 * @param string $idx name of the index 35 * @param string $suffix subpart identifier 36 * @param bool $isWritable acquire a lock immediately? 37 * @throws IndexLockException 38 */ 39 public function __construct($idx, $suffix = '', $isWritable = false) 40 { 41 global $conf; 42 $this->filename = $conf['indexdir'] . '/' . $idx . $suffix . '.idx'; 43 $this->idx = $idx; 44 $this->suffix = $suffix; 45 if ($isWritable) $this->lock(); 46 } 47 48 /** 49 * Make this index writable by acquiring the lock 50 * 51 * @throws IndexLockException 52 */ 53 public function lock() 54 { 55 if ($this->isWritable) return; 56 Lock::acquire($this->idx); 57 $this->isWritable = true; 58 } 59 60 /** 61 * Make this index read-only by releasing the lock 62 * 63 * Decrements the reference count in the Lock registry. The filesystem 64 * lock is only removed when the count reaches zero. 65 */ 66 public function unlock() 67 { 68 if (!$this->isWritable) return; 69 Lock::release($this->idx); 70 $this->isWritable = false; 71 } 72 73 /** 74 * Whether this index instance is writable 75 * 76 * @return bool 77 */ 78 public function isWritable() 79 { 80 return $this->isWritable; 81 } 82 83 /** 84 * Ensure lock is released when the index is destroyed 85 */ 86 public function __destruct() 87 { 88 $this->unlock(); 89 } 90 91 /** 92 * @return string the full path to the underlying file 93 */ 94 public function getFilename() 95 { 96 return $this->filename; 97 } 98 99 /** 100 * Does this index exist, yet? 101 * 102 * @return bool 103 */ 104 public function exists() 105 { 106 return file_exists($this->getFilename()); 107 } 108 109 /** 110 * Return the largest numeric suffix for the current index 111 * 112 * This is only useful for indexes that use integer based suffixes (like the wordlength indexes) 113 * 114 * @return int 0 if no numeric suffix indexes are found 115 */ 116 public function max() 117 { 118 global $conf; 119 $result = 0; 120 $files = glob($conf['indexdir'] . '/' . $this->idx . '*.idx'); 121 foreach ($files as $file) { 122 if (preg_match('/(\d)+\.idx$/', $file, $match)) { 123 $num = (int)$match[1]; 124 if ($num > $result) $result = $num; 125 } 126 } 127 128 return $result; 129 } 130 131 /** 132 * Change a line in the index 133 * 134 * If the line doesn't exist, it will be added, creating empty 135 * lines inbetween as necessary 136 * 137 * @param int $rid the line number, count starting at 0 138 * @param string $value line content to write 139 */ 140 abstract public function changeRow($rid, $value); 141 142 /** 143 * Retrieve a line from the index 144 * 145 * Returns an empty string for non-existing lines 146 * 147 * @param int $rid the line number 148 * @return string a line with trailing whitespace removed 149 */ 150 abstract public function retrieveRow($rid); 151 152 /** 153 * Retrieve multiple lines from the index 154 * 155 * Ignores non-existing lines, eg the result array may be smaller than the input $rids 156 * 157 * @param int[] $rids 158 * @return array [rid => value] 159 */ 160 abstract public function retrieveRows($rids); 161 162 /** 163 * Searches the Index for a given value 164 * 165 * If the index is writable and the value is not found it will be added. Otherwise null is returned. 166 * 167 * Note the existence of an entry in the index does not say anything about the existence 168 * of the real world object (eg. a page) 169 * 170 * You should preferably use accessCachedValue() instead. 171 * 172 * @param string $value 173 * 174 * @return int|null the RID of the entry, null if not found and not added 175 */ 176 public function getRowID($value) 177 { 178 $result = $this->getRowIDs([$value]); 179 return $result[$value] ?? null; 180 } 181 182 /** 183 * Searches the Index for all given values 184 * 185 * If the index is writable, not found values are added 186 * 187 * @param string[] $values 188 * @return array the RIDs of the entries (value => rid) 189 */ 190 abstract public function getRowIDs($values); 191 192 /** 193 * Find all RIDs matching a regular expression 194 * 195 * A full regular expression including delimiters and modifiers is expected 196 * 197 * @param string $re the regular expression to match against 198 * @return array (rid => value) 199 */ 200 abstract public function search($re); 201 202 /** 203 * Clears the index by deleting its file 204 * 205 * @return void 206 */ 207 public function clear() 208 { 209 @unlink($this->filename); 210 } 211 212 /** 213 * Saves the index if needed 214 * 215 * The default implementation does nothing and is only for streamlining the API of 216 * the different index classes 217 * 218 * @return void 219 */ 220 public function save() 221 { 222 } 223} 224