19bd7d62fSAndreas Gohr<?php 29bd7d62fSAndreas Gohr 39bd7d62fSAndreas Gohrnamespace dokuwiki\Search\Index; 49bd7d62fSAndreas Gohr 59bd7d62fSAndreas Gohruse dokuwiki\Search\Exception\IndexWriteException; 69bd7d62fSAndreas Gohr 79bd7d62fSAndreas Gohr/** 89bd7d62fSAndreas Gohr * Access to a single index file 99bd7d62fSAndreas Gohr * 109bd7d62fSAndreas Gohr * Access using this class always happens by loading the full index into memory. 119bd7d62fSAndreas Gohr * All modifications need to be explicitly made permanent using the save() method. 129bd7d62fSAndreas Gohr * Should be used for small indexes that receive many changes at once. 139bd7d62fSAndreas Gohr */ 149bd7d62fSAndreas Gohrclass MemoryIndex extends AbstractIndex 159bd7d62fSAndreas Gohr{ 169bd7d62fSAndreas Gohr /** @var string the raw data lines of the index, no newlines */ 179bd7d62fSAndreas Gohr protected $data; 189bd7d62fSAndreas Gohr 19b3cb0bc3SAndreas Gohr /** @var bool has the index been modified? */ 20b3cb0bc3SAndreas Gohr protected $dirty = false; 21b3cb0bc3SAndreas Gohr 229bd7d62fSAndreas Gohr /** 239bd7d62fSAndreas Gohr * Loads the full contents of the index into memory 249bd7d62fSAndreas Gohr * 259bd7d62fSAndreas Gohr * @inheritdoc 269bd7d62fSAndreas Gohr */ 279bd7d62fSAndreas Gohr public function __construct($idx, $suffix = '') 289bd7d62fSAndreas Gohr { 299bd7d62fSAndreas Gohr parent::__construct($idx, $suffix); 309bd7d62fSAndreas Gohr 319bd7d62fSAndreas Gohr $this->data = []; 32b3cb0bc3SAndreas Gohr if (!file_exists($this->filename)) { 33b3cb0bc3SAndreas Gohr return; 34b3cb0bc3SAndreas Gohr } 359bd7d62fSAndreas Gohr $this->data = file($this->filename, FILE_IGNORE_NEW_LINES); 369bd7d62fSAndreas Gohr 379bd7d62fSAndreas Gohr } 389bd7d62fSAndreas Gohr 399bd7d62fSAndreas Gohr /** @inheritdoc */ 409bd7d62fSAndreas Gohr public function changeRow($rid, $value) 419bd7d62fSAndreas Gohr { 429bd7d62fSAndreas Gohr if ($rid > count($this->data)) { 439bd7d62fSAndreas Gohr $this->data = array_pad($this->data, $rid, ''); 449bd7d62fSAndreas Gohr } 459bd7d62fSAndreas Gohr $this->data[$rid] = $value; 46b3cb0bc3SAndreas Gohr $this->dirty = true; 479bd7d62fSAndreas Gohr } 489bd7d62fSAndreas Gohr 499bd7d62fSAndreas Gohr /** @inheritdoc */ 509bd7d62fSAndreas Gohr public function retrieveRow($rid) 519bd7d62fSAndreas Gohr { 52b3cb0bc3SAndreas Gohr if (isset($this->data[$rid])) { 53b3cb0bc3SAndreas Gohr return $this->data[$rid]; 54b3cb0bc3SAndreas Gohr } 55dec26820SAndreas Gohr $this->changeRow($rid, ''); // add to index 569bd7d62fSAndreas Gohr return ''; 579bd7d62fSAndreas Gohr } 589bd7d62fSAndreas Gohr 59d6396b6dSAndreas Gohr /** @inheritdoc */ 60*9f63f003SAndreas Gohr public function retrieveRows($rids) 61*9f63f003SAndreas Gohr { 62*9f63f003SAndreas Gohr $result = []; 63*9f63f003SAndreas Gohr foreach ($rids as $rid) { 64*9f63f003SAndreas Gohr if (isset($this->data[$rid])) $result[$rid] = $this->data[$rid]; 65*9f63f003SAndreas Gohr } 66*9f63f003SAndreas Gohr 67*9f63f003SAndreas Gohr return $result; 68*9f63f003SAndreas Gohr } 69*9f63f003SAndreas Gohr 70*9f63f003SAndreas Gohr /** @inheritdoc */ 718ed35011SAndreas Gohr public function getRowIDs($values) 72d6396b6dSAndreas Gohr { 73d6396b6dSAndreas Gohr $values = array_map('trim', $values); 74d6396b6dSAndreas Gohr $values = array_fill_keys($values, 1); // easier access as associative array 75d6396b6dSAndreas Gohr 76d6396b6dSAndreas Gohr $result = []; 77d6396b6dSAndreas Gohr $count = count($this->data); 78d6396b6dSAndreas Gohr for ($ln = 0; $ln < $count; $ln++) { 79d6396b6dSAndreas Gohr $line = $this->data[$ln]; 80d6396b6dSAndreas Gohr if (isset($values[$line])) { 81d6396b6dSAndreas Gohr $result[$line] = $ln; 82d6396b6dSAndreas Gohr unset($values[$line]); 83d6396b6dSAndreas Gohr } 84d6396b6dSAndreas Gohr } 85d6396b6dSAndreas Gohr 86d6396b6dSAndreas Gohr // if there are still values, they have not been found and will be appended 87d6396b6dSAndreas Gohr foreach (array_keys($values) as $value) { 88d6396b6dSAndreas Gohr $this->data[] = $value; 89d6396b6dSAndreas Gohr $result[$value] = $ln++; 90b3cb0bc3SAndreas Gohr $this->dirty = true; 91d6396b6dSAndreas Gohr } 92d6396b6dSAndreas Gohr 93d6396b6dSAndreas Gohr return $result; 94d6396b6dSAndreas Gohr } 95d6396b6dSAndreas Gohr 9603a35633SAndreas Gohr /** @inheritdoc */ 9703a35633SAndreas Gohr public function search($re) 9803a35633SAndreas Gohr { 9903a35633SAndreas Gohr return preg_grep($re, $this->data); 10003a35633SAndreas Gohr } 10103a35633SAndreas Gohr 1029bd7d62fSAndreas Gohr /** 1039bd7d62fSAndreas Gohr * Save the changed index back to its file 1049bd7d62fSAndreas Gohr * 105b3cb0bc3SAndreas Gohr * The method will check the internal dirty state and will only write when the index has actually been changed 106b3cb0bc3SAndreas Gohr * 1079bd7d62fSAndreas Gohr * @throws IndexWriteException 1089bd7d62fSAndreas Gohr */ 1099bd7d62fSAndreas Gohr public function save() 1109bd7d62fSAndreas Gohr { 1119bd7d62fSAndreas Gohr global $conf; 1129bd7d62fSAndreas Gohr 113b3cb0bc3SAndreas Gohr if (!$this->isDirty()) { 114b3cb0bc3SAndreas Gohr return; 115b3cb0bc3SAndreas Gohr } 116b3cb0bc3SAndreas Gohr 1179bd7d62fSAndreas Gohr $tempname = $this->filename . '.tmp'; 1189bd7d62fSAndreas Gohr 1199bd7d62fSAndreas Gohr $fh = @fopen($tempname, 'w'); 1209bd7d62fSAndreas Gohr if (!$fh) { 1219bd7d62fSAndreas Gohr throw new IndexWriteException("Failed to write $tempname"); 1229bd7d62fSAndreas Gohr } 1239bd7d62fSAndreas Gohr fwrite($fh, implode("\n", $this->data)); 124dec26820SAndreas Gohr if (count($this->data)) { 1259bd7d62fSAndreas Gohr fwrite($fh, "\n"); 1269bd7d62fSAndreas Gohr } 1279bd7d62fSAndreas Gohr fclose($fh); 1289bd7d62fSAndreas Gohr 1299bd7d62fSAndreas Gohr if ($conf['fperm']) { 1309bd7d62fSAndreas Gohr chmod($tempname, $conf['fperm']); 1319bd7d62fSAndreas Gohr } 1329bd7d62fSAndreas Gohr 1339bd7d62fSAndreas Gohr if (!io_rename($tempname, $this->filename)) { 1349bd7d62fSAndreas Gohr throw new IndexWriteException("Failed to write {$this->filename}"); 1359bd7d62fSAndreas Gohr } 136b3cb0bc3SAndreas Gohr 137b3cb0bc3SAndreas Gohr $this->dirty = false; 1389bd7d62fSAndreas Gohr } 1399bd7d62fSAndreas Gohr 140b3cb0bc3SAndreas Gohr /** 141b3cb0bc3SAndreas Gohr * Check if the index has been modified and needs to be saved 142b3cb0bc3SAndreas Gohr * @return bool 143b3cb0bc3SAndreas Gohr */ 144b3cb0bc3SAndreas Gohr public function isDirty() 145b3cb0bc3SAndreas Gohr { 146b3cb0bc3SAndreas Gohr return $this->dirty; 147b3cb0bc3SAndreas Gohr } 1489bd7d62fSAndreas Gohr} 149