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