1<?php 2 3namespace dokuwiki\Search\Index; 4 5use dokuwiki\Search\Exception\IndexWriteException; 6 7/** 8 * Access to a single index file 9 * 10 * Access using this class always happens by loading the full index into memory. 11 * All modifications need to be explicitly made permanent using the save() method. 12 * Should be used for small indexes that receive many changes at once. 13 */ 14class MemoryIndex extends AbstractIndex 15{ 16 17 /** @var string the raw data lines of the index, no newlines */ 18 protected $data; 19 20 /** 21 * Loads the full contents of the index into memory 22 * 23 * @inheritdoc 24 */ 25 public function __construct($idx, $suffix = '') 26 { 27 parent::__construct($idx, $suffix); 28 29 $this->data = []; 30 if (!file_exists($this->filename)) return; 31 $this->data = file($this->filename, FILE_IGNORE_NEW_LINES); 32 33 } 34 35 /** @inheritdoc */ 36 public function changeRow($rid, $value) 37 { 38 if ($rid > count($this->data)) { 39 $this->data = array_pad($this->data, $rid, ''); 40 } 41 $this->data[$rid] = $value; 42 } 43 44 /** @inheritdoc */ 45 public function retrieveRow($rid) 46 { 47 if (isset($this->data[$rid])) return $this->data[$rid]; 48 $this->changeRow($rid, ''); // add to index 49 return ''; 50 } 51 52 /** @inheritdoc */ 53 public function getRowIDs($values) 54 { 55 $values = array_map('trim', $values); 56 $values = array_fill_keys($values, 1); // easier access as associative array 57 58 $result = []; 59 $count = count($this->data); 60 for ($ln = 0; $ln < $count; $ln++) { 61 $line = $this->data[$ln]; 62 if (isset($values[$line])) { 63 $result[$line] = $ln; 64 unset($values[$line]); 65 } 66 } 67 68 // if there are still values, they have not been found and will be appended 69 foreach (array_keys($values) as $value) { 70 $this->data[] = $value; 71 $result[$value] = $ln++; 72 } 73 74 return $result; 75 } 76 77 /** 78 * Save the changed index back to its file 79 * 80 * @throws IndexWriteException 81 * @fixme store a dirty marker and only save when needed 82 */ 83 public function save() 84 { 85 global $conf; 86 87 $tempname = $this->filename . '.tmp'; 88 89 $fh = @fopen($tempname, 'w'); 90 if (!$fh) { 91 throw new IndexWriteException("Failed to write $tempname"); 92 } 93 fwrite($fh, implode("\n", $this->data)); 94 if (count($this->data)) { 95 fwrite($fh, "\n"); 96 } 97 fclose($fh); 98 99 if ($conf['fperm']) { 100 chmod($tempname, $conf['fperm']); 101 } 102 103 if (!io_rename($tempname, $this->filename)) { 104 throw new IndexWriteException("Failed to write {$this->filename}"); 105 } 106 } 107 108} 109