1<?php
2/**
3 * PageMapper for SphinxSearch plugin
4 * Handles mapping between DokuWiki Page IDs and Sphinx CRCs
5 */
6
7class PageMapper {
8    private ?PDO $_db = null;
9    private string $_dbFile = '';
10
11    public function __construct() {
12        global $conf;
13
14        // 1. Resolve the savedir relative to DOKU_INC
15        // This handles cases where savedir is just 'data' or './data'
16        $saveDir = $conf['savedir'];
17        if (strpos($saveDir, '/') !== 0 && !preg_match('/^[a-z]:\\\/i', $saveDir)) {
18            // It is a relative path, anchor it to DOKU_INC
19            $saveDir = DOKU_INC . $saveDir;
20        }
21
22        $dbPath = $saveDir . '/sphinxsearchwas';
23
24        // 2. Ensure the directory exists
25        if (!is_dir($dbPath)) {
26            @mkdir($dbPath, 0777, true);
27        }
28
29        $this->_dbFile = $dbPath . '/pages.db';
30
31        // DEBUG: Uncomment the line below if it still fails, to see where it is trying to write
32        // file_put_contents('php://stderr', "Writing DB to: " . $this->_dbFile . "\n");
33
34        $this->_initDb();
35    }
36
37    /**
38     * Connect to SQLite and ensure the table exists
39     */
40    private function _initDb(): void {
41        try {
42            $this->_db = new PDO("sqlite:" . $this->_dbFile);
43            $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
44
45            // Create the table if it doesn't exist
46            $sql = "CREATE TABLE IF NOT EXISTS pages (
47                page TEXT,
48                title TEXT,
49                title_text TEXT,
50                hid TEXT,
51                crc INTEGER PRIMARY KEY
52            )";
53            $this->_db->exec($sql);
54
55            // Optimization for SQLite
56            $this->_db->exec("PRAGMA journal_mode = WAL");
57            $this->_db->exec("PRAGMA synchronous = NORMAL");
58
59        } catch (PDOException $e) {
60            die("Database Error: " . $e->getMessage());
61        }
62    }
63
64    /**
65     * Add or Update a page mapping
66     */
67    public function add(string $page, string $title, string $title_text = '', string $hid = ''): void {
68        $crc = sprintf('%u', crc32($page . $hid) ?: 1);
69
70        $sql = "REPLACE INTO pages (page, title, title_text, hid, crc)
71                VALUES (:page, :title, :title_text, :hid, :crc)";
72
73        try {
74            $stmt = $this->_db->prepare($sql);
75            $stmt->bindValue(':page', $page);
76            $stmt->bindValue(':title', $title);
77            $stmt->bindValue(':title_text', $title_text);
78            $stmt->bindValue(':hid', $hid);
79            $stmt->bindValue(':crc', $crc);
80            $stmt->execute();
81        } catch (PDOException $e) {
82            // Ignore write errors during indexing to keep stream alive
83        }
84    }
85
86    /**
87     * Retrieve mapping by CRC
88     */
89    public function getByCrc(array $crcs): array {
90        if (empty($crcs)) return [];
91
92        $placeholders = implode(',', array_fill(0, count($crcs), '?'));
93        $sql = "SELECT * FROM pages WHERE crc IN ($placeholders)";
94
95        try {
96            $stmt = $this->_db->prepare($sql);
97            $stmt->execute(array_values($crcs));
98            $results = [];
99            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
100                $results[$row['crc']] = $row;
101            }
102            return $results;
103        } catch (PDOException $e) {
104            return [];
105        }
106    }
107}
108