xref: /dokuwiki/inc/Search/Index/MemoryIndex.php (revision dec26820989745ccc5f6df0b41f772b9066e3584)
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
179bd7d62fSAndreas Gohr    /** @var string the raw data lines of the index, no newlines */
189bd7d62fSAndreas Gohr    protected $data;
199bd7d62fSAndreas Gohr
209bd7d62fSAndreas Gohr    /**
219bd7d62fSAndreas Gohr     * Loads the full contents of the index into memory
229bd7d62fSAndreas Gohr     *
239bd7d62fSAndreas Gohr     * @inheritdoc
249bd7d62fSAndreas Gohr     */
259bd7d62fSAndreas Gohr    public function __construct($idx, $suffix = '')
269bd7d62fSAndreas Gohr    {
279bd7d62fSAndreas Gohr        parent::__construct($idx, $suffix);
289bd7d62fSAndreas Gohr
299bd7d62fSAndreas Gohr        $this->data = [];
309bd7d62fSAndreas Gohr        if (!file_exists($this->filename)) return;
319bd7d62fSAndreas Gohr        $this->data = file($this->filename, FILE_IGNORE_NEW_LINES);
329bd7d62fSAndreas Gohr
339bd7d62fSAndreas Gohr    }
349bd7d62fSAndreas Gohr
359bd7d62fSAndreas Gohr    /** @inheritdoc */
369bd7d62fSAndreas Gohr    public function changeRow($rid, $value)
379bd7d62fSAndreas Gohr    {
389bd7d62fSAndreas Gohr        if ($rid > count($this->data)) {
399bd7d62fSAndreas Gohr            $this->data = array_pad($this->data, $rid, '');
409bd7d62fSAndreas Gohr        }
419bd7d62fSAndreas Gohr        $this->data[$rid] = $value;
429bd7d62fSAndreas Gohr    }
439bd7d62fSAndreas Gohr
449bd7d62fSAndreas Gohr    /** @inheritdoc */
459bd7d62fSAndreas Gohr    public function retrieveRow($rid)
469bd7d62fSAndreas Gohr    {
479bd7d62fSAndreas Gohr        if (isset($this->data[$rid])) return $this->data[$rid];
48*dec26820SAndreas Gohr        $this->changeRow($rid, ''); // add to index
499bd7d62fSAndreas Gohr        return '';
509bd7d62fSAndreas Gohr    }
519bd7d62fSAndreas Gohr
52d6396b6dSAndreas Gohr    /** @inheritdoc */
538ed35011SAndreas Gohr    public function getRowIDs($values)
54d6396b6dSAndreas Gohr    {
55d6396b6dSAndreas Gohr        $values = array_map('trim', $values);
56d6396b6dSAndreas Gohr        $values = array_fill_keys($values, 1); // easier access as associative array
57d6396b6dSAndreas Gohr
58d6396b6dSAndreas Gohr        $result = [];
59d6396b6dSAndreas Gohr        $count = count($this->data);
60d6396b6dSAndreas Gohr        for ($ln = 0; $ln < $count; $ln++) {
61d6396b6dSAndreas Gohr            $line = $this->data[$ln];
62d6396b6dSAndreas Gohr            if (isset($values[$line])) {
63d6396b6dSAndreas Gohr                $result[$line] = $ln;
64d6396b6dSAndreas Gohr                unset($values[$line]);
65d6396b6dSAndreas Gohr            }
66d6396b6dSAndreas Gohr        }
67d6396b6dSAndreas Gohr
68d6396b6dSAndreas Gohr        // if there are still values, they have not been found and will be appended
69d6396b6dSAndreas Gohr        foreach (array_keys($values) as $value) {
70d6396b6dSAndreas Gohr            $this->data[] = $value;
71d6396b6dSAndreas Gohr            $result[$value] = $ln++;
72d6396b6dSAndreas Gohr        }
73d6396b6dSAndreas Gohr
74d6396b6dSAndreas Gohr        return $result;
75d6396b6dSAndreas Gohr    }
76d6396b6dSAndreas Gohr
779bd7d62fSAndreas Gohr    /**
789bd7d62fSAndreas Gohr     * Save the changed index back to its file
799bd7d62fSAndreas Gohr     *
809bd7d62fSAndreas Gohr     * @throws IndexWriteException
81*dec26820SAndreas Gohr     * @fixme store a dirty marker and only save when needed
829bd7d62fSAndreas Gohr     */
839bd7d62fSAndreas Gohr    public function save()
849bd7d62fSAndreas Gohr    {
859bd7d62fSAndreas Gohr        global $conf;
869bd7d62fSAndreas Gohr
879bd7d62fSAndreas Gohr        $tempname = $this->filename . '.tmp';
889bd7d62fSAndreas Gohr
899bd7d62fSAndreas Gohr        $fh = @fopen($tempname, 'w');
909bd7d62fSAndreas Gohr        if (!$fh) {
919bd7d62fSAndreas Gohr            throw new IndexWriteException("Failed to write $tempname");
929bd7d62fSAndreas Gohr        }
939bd7d62fSAndreas Gohr        fwrite($fh, implode("\n", $this->data));
94*dec26820SAndreas Gohr        if (count($this->data)) {
959bd7d62fSAndreas Gohr            fwrite($fh, "\n");
969bd7d62fSAndreas Gohr        }
979bd7d62fSAndreas Gohr        fclose($fh);
989bd7d62fSAndreas Gohr
999bd7d62fSAndreas Gohr        if ($conf['fperm']) {
1009bd7d62fSAndreas Gohr            chmod($tempname, $conf['fperm']);
1019bd7d62fSAndreas Gohr        }
1029bd7d62fSAndreas Gohr
1039bd7d62fSAndreas Gohr        if (!io_rename($tempname, $this->filename)) {
1049bd7d62fSAndreas Gohr            throw new IndexWriteException("Failed to write {$this->filename}");
1059bd7d62fSAndreas Gohr        }
1069bd7d62fSAndreas Gohr    }
1079bd7d62fSAndreas Gohr
1089bd7d62fSAndreas Gohr}
109