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 */ 608ed35011SAndreas Gohr public function getRowIDs($values) 61d6396b6dSAndreas Gohr { 62d6396b6dSAndreas Gohr $values = array_map('trim', $values); 63d6396b6dSAndreas Gohr $values = array_fill_keys($values, 1); // easier access as associative array 64d6396b6dSAndreas Gohr 65d6396b6dSAndreas Gohr $result = []; 66d6396b6dSAndreas Gohr $count = count($this->data); 67d6396b6dSAndreas Gohr for ($ln = 0; $ln < $count; $ln++) { 68d6396b6dSAndreas Gohr $line = $this->data[$ln]; 69d6396b6dSAndreas Gohr if (isset($values[$line])) { 70d6396b6dSAndreas Gohr $result[$line] = $ln; 71d6396b6dSAndreas Gohr unset($values[$line]); 72d6396b6dSAndreas Gohr } 73d6396b6dSAndreas Gohr } 74d6396b6dSAndreas Gohr 75d6396b6dSAndreas Gohr // if there are still values, they have not been found and will be appended 76d6396b6dSAndreas Gohr foreach (array_keys($values) as $value) { 77d6396b6dSAndreas Gohr $this->data[] = $value; 78d6396b6dSAndreas Gohr $result[$value] = $ln++; 79b3cb0bc3SAndreas Gohr $this->dirty = true; 80d6396b6dSAndreas Gohr } 81d6396b6dSAndreas Gohr 82d6396b6dSAndreas Gohr return $result; 83d6396b6dSAndreas Gohr } 84d6396b6dSAndreas Gohr 85*03a35633SAndreas Gohr /** @inheritdoc */ 86*03a35633SAndreas Gohr public function search($re) 87*03a35633SAndreas Gohr { 88*03a35633SAndreas Gohr return preg_grep($re, $this->data); 89*03a35633SAndreas Gohr } 90*03a35633SAndreas Gohr 919bd7d62fSAndreas Gohr /** 929bd7d62fSAndreas Gohr * Save the changed index back to its file 939bd7d62fSAndreas Gohr * 94b3cb0bc3SAndreas Gohr * The method will check the internal dirty state and will only write when the index has actually been changed 95b3cb0bc3SAndreas Gohr * 969bd7d62fSAndreas Gohr * @throws IndexWriteException 979bd7d62fSAndreas Gohr */ 989bd7d62fSAndreas Gohr public function save() 999bd7d62fSAndreas Gohr { 1009bd7d62fSAndreas Gohr global $conf; 1019bd7d62fSAndreas Gohr 102b3cb0bc3SAndreas Gohr if (!$this->isDirty()) { 103b3cb0bc3SAndreas Gohr return; 104b3cb0bc3SAndreas Gohr } 105b3cb0bc3SAndreas Gohr 1069bd7d62fSAndreas Gohr $tempname = $this->filename . '.tmp'; 1079bd7d62fSAndreas Gohr 1089bd7d62fSAndreas Gohr $fh = @fopen($tempname, 'w'); 1099bd7d62fSAndreas Gohr if (!$fh) { 1109bd7d62fSAndreas Gohr throw new IndexWriteException("Failed to write $tempname"); 1119bd7d62fSAndreas Gohr } 1129bd7d62fSAndreas Gohr fwrite($fh, implode("\n", $this->data)); 113dec26820SAndreas Gohr if (count($this->data)) { 1149bd7d62fSAndreas Gohr fwrite($fh, "\n"); 1159bd7d62fSAndreas Gohr } 1169bd7d62fSAndreas Gohr fclose($fh); 1179bd7d62fSAndreas Gohr 1189bd7d62fSAndreas Gohr if ($conf['fperm']) { 1199bd7d62fSAndreas Gohr chmod($tempname, $conf['fperm']); 1209bd7d62fSAndreas Gohr } 1219bd7d62fSAndreas Gohr 1229bd7d62fSAndreas Gohr if (!io_rename($tempname, $this->filename)) { 1239bd7d62fSAndreas Gohr throw new IndexWriteException("Failed to write {$this->filename}"); 1249bd7d62fSAndreas Gohr } 125b3cb0bc3SAndreas Gohr 126b3cb0bc3SAndreas Gohr $this->dirty = false; 1279bd7d62fSAndreas Gohr } 1289bd7d62fSAndreas Gohr 129b3cb0bc3SAndreas Gohr /** 130b3cb0bc3SAndreas Gohr * Check if the index has been modified and needs to be saved 131b3cb0bc3SAndreas Gohr * @return bool 132b3cb0bc3SAndreas Gohr */ 133b3cb0bc3SAndreas Gohr public function isDirty() 134b3cb0bc3SAndreas Gohr { 135b3cb0bc3SAndreas Gohr return $this->dirty; 136b3cb0bc3SAndreas Gohr } 1379bd7d62fSAndreas Gohr} 138