xref: /dokuwiki/inc/Search/Index/MemoryIndex.php (revision dec26820989745ccc5f6df0b41f772b9066e3584)
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