xref: /plugin/sqlite/helper.php (revision 8da7d8059f7b9860b1f87a4aab15e87bf96c4064)
1a1e6784eSAndreas Gohr<?php
2*8da7d805SAndreas Gohr
3*8da7d805SAndreas Gohr/**
4*8da7d805SAndreas Gohr * @noinspection SqlNoDataSourceInspection
5*8da7d805SAndreas Gohr * @noinspection SqlDialectInspection
6*8da7d805SAndreas Gohr * @noinspection PhpComposerExtensionStubsInspection
7*8da7d805SAndreas Gohr */
8*8da7d805SAndreas Gohr
9*8da7d805SAndreas Gohruse dokuwiki\plugin\sqlite\SQLiteDB;
10*8da7d805SAndreas Gohruse dokuwiki\plugin\sqlite\Tools;
11*8da7d805SAndreas Gohr
12*8da7d805SAndreas Gohr
13a1e6784eSAndreas Gohr/**
14a1e6784eSAndreas Gohr * DokuWiki Plugin sqlite (Helper Component)
15a1e6784eSAndreas Gohr *
16a1e6784eSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
17a1e6784eSAndreas Gohr * @author  Andreas Gohr <gohr@cosmocode.de>
18*8da7d805SAndreas Gohr * @deprecated 2023-03-15
19a1e6784eSAndreas Gohr */
20*8da7d805SAndreas Gohrclass helper_plugin_sqlite extends DokuWiki_Plugin
21*8da7d805SAndreas Gohr{
22*8da7d805SAndreas Gohr    /** @var SQLiteDB|null */
236c7ee3f2SAndreas Gohr    protected $adapter = null;
24aa81d781SKlap-in
25*8da7d805SAndreas Gohr    /** @var array result cache */
26*8da7d805SAndreas Gohr    protected $data;
27*8da7d805SAndreas Gohr
283e9ac593SGerrit Uitslag    /**
29*8da7d805SAndreas Gohr     * constructor
303e9ac593SGerrit Uitslag     */
31*8da7d805SAndreas Gohr    public function __construct()
32*8da7d805SAndreas Gohr    {
33*8da7d805SAndreas Gohr        dbg_deprecated(SQLiteDB::class);
34*8da7d805SAndreas Gohr
35*8da7d805SAndreas Gohr        if (!$this->existsPDOSqlite()) {
36*8da7d805SAndreas Gohr            msg('PDO SQLite support missing in this PHP install - The sqlite plugin will not work', -1);
37*8da7d805SAndreas Gohr        }
38*8da7d805SAndreas Gohr    }
39*8da7d805SAndreas Gohr
40*8da7d805SAndreas Gohr    /**
41*8da7d805SAndreas Gohr     * Get the current Adapter
42*8da7d805SAndreas Gohr     * @return SQLiteDB|null
43*8da7d805SAndreas Gohr     */
44*8da7d805SAndreas Gohr    public function getAdapter()
45*8da7d805SAndreas Gohr    {
46ca2f2adaSKlap-in        return $this->adapter;
47ca2f2adaSKlap-in    }
48ca2f2adaSKlap-in
49a1e6784eSAndreas Gohr    /**
50aa81d781SKlap-in     * Keep separate instances for every call to keep database connections
51aa81d781SKlap-in     */
52*8da7d805SAndreas Gohr    public function isSingleton()
53*8da7d805SAndreas Gohr    {
54aa81d781SKlap-in        return false;
55aa81d781SKlap-in    }
56aa81d781SKlap-in
57aa81d781SKlap-in    /**
58aa81d781SKlap-in     * check availabilty of PHP PDO sqlite3
59aa81d781SKlap-in     */
60*8da7d805SAndreas Gohr    public function existsPDOSqlite()
61*8da7d805SAndreas Gohr    {
6287fa2c18Sstretchyboy        if (class_exists('pdo')) {
63*8da7d805SAndreas Gohr            return in_array('sqlite', \PDO::getAvailableDrivers());
647ed6069fSAdrian Lang        }
65aa81d781SKlap-in        return false;
66a1e6784eSAndreas Gohr    }
67a1e6784eSAndreas Gohr
68a1e6784eSAndreas Gohr    /**
69a1e6784eSAndreas Gohr     * Initializes and opens the database
70a1e6784eSAndreas Gohr     *
71a1e6784eSAndreas Gohr     * Needs to be called right after loading this helper plugin
72aa81d781SKlap-in     *
73aa81d781SKlap-in     * @param string $dbname
74aa81d781SKlap-in     * @param string $updatedir - Database update infos
75aa81d781SKlap-in     * @return bool
76a1e6784eSAndreas Gohr     */
77*8da7d805SAndreas Gohr    public function init($dbname, $updatedir)
78*8da7d805SAndreas Gohr    {
79*8da7d805SAndreas Gohr        try {
80*8da7d805SAndreas Gohr            $this->adapter = new SQLiteDB($dbname, $updatedir, $this);
81*8da7d805SAndreas Gohr        } catch (Exception $e) {
82*8da7d805SAndreas Gohr            msg('SQLite: ' . $e->getMessage(), -1);
83c137e95fSAndreas Gohr            return false;
84c137e95fSAndreas Gohr        }
85a1e6784eSAndreas Gohr        return true;
86a1e6784eSAndreas Gohr    }
87a1e6784eSAndreas Gohr
88a1e6784eSAndreas Gohr    /**
89*8da7d805SAndreas Gohr     * This is called from the adapter itself for backwards compatibility
90*8da7d805SAndreas Gohr     *
91*8da7d805SAndreas Gohr     * @param SQLiteDB $adapter
92*8da7d805SAndreas Gohr     * @return void
93a1e6784eSAndreas Gohr     */
94*8da7d805SAndreas Gohr    function setAdapter($adapter)
95*8da7d805SAndreas Gohr    {
96*8da7d805SAndreas Gohr        $this->adapter = $adapter;
97a34ef333SKlap-in    }
98f10ea6c1SKlap-in
99a1e6784eSAndreas Gohr    /**
1003ae3f79eSKlap-in     * Registers a User Defined Function for use in SQL statements
1013ae3f79eSKlap-in     */
102*8da7d805SAndreas Gohr    public function create_function($function_name, $callback, $num_args)
103*8da7d805SAndreas Gohr    {
104*8da7d805SAndreas Gohr        $this->adapter->pdo()->sqliteCreateFunction($function_name, $callback, $num_args);
1053ae3f79eSKlap-in    }
1063ae3f79eSKlap-in
107*8da7d805SAndreas Gohr    // region query and result handling functions
108*8da7d805SAndreas Gohr
1093ae3f79eSKlap-in    /**
110e7b0736cSAndreas Gohr     * Convenience function to run an INSERT OR REPLACE operation
111e7b0736cSAndreas Gohr     *
112e7b0736cSAndreas Gohr     * The function takes a key-value array with the column names in the key and the actual value in the value,
113e7b0736cSAndreas Gohr     * build the appropriate query and executes it.
114e7b0736cSAndreas Gohr     *
115e7b0736cSAndreas Gohr     * @param string $table the table the entry should be saved to (will not be escaped)
116e7b0736cSAndreas Gohr     * @param array $entry A simple key-value pair array (only values will be escaped)
117*8da7d805SAndreas Gohr     * @return bool
118e7b0736cSAndreas Gohr     */
119*8da7d805SAndreas Gohr    public function storeEntry($table, $entry)
120*8da7d805SAndreas Gohr    {
121*8da7d805SAndreas Gohr        try {
122*8da7d805SAndreas Gohr            $this->adapter->saveRecord($table, $entry);
123*8da7d805SAndreas Gohr        } catch (\Exception $e) {
124*8da7d805SAndreas Gohr            msg('SQLite: ' . $e->getMessage(), -1);
125*8da7d805SAndreas Gohr            return false;
126e7b0736cSAndreas Gohr        }
127e7b0736cSAndreas Gohr
128*8da7d805SAndreas Gohr        return true;
129*8da7d805SAndreas Gohr    }
130e7b0736cSAndreas Gohr
131e7b0736cSAndreas Gohr    /**
132a1e6784eSAndreas Gohr     * Execute a query with the given parameters.
133a1e6784eSAndreas Gohr     *
134a1e6784eSAndreas Gohr     * Takes care of escaping
135a1e6784eSAndreas Gohr     *
136a2a82480SAndreas Gohr     *
137a2a82480SAndreas Gohr     * @param string ...$args - the arguments of query(), the first is the sql and others are values
138a1e6784eSAndreas Gohr     */
139*8da7d805SAndreas Gohr    public function query()
140*8da7d805SAndreas Gohr    {
141a1e6784eSAndreas Gohr        // get function arguments
142a1e6784eSAndreas Gohr        $args = func_get_args();
143*8da7d805SAndreas Gohr        $sql = array_shift($args);
144a1e6784eSAndreas Gohr
145*8da7d805SAndreas Gohr        try {
146*8da7d805SAndreas Gohr            return $this->adapter->query($sql, $args);
147*8da7d805SAndreas Gohr        } catch (\Exception $e) {
148*8da7d805SAndreas Gohr            msg('SQLite: ' . $e->getMessage(), -1);
149*8da7d805SAndreas Gohr            return false;
150*8da7d805SAndreas Gohr        }
15187fa2c18Sstretchyboy    }
152a1e6784eSAndreas Gohr
153ff97cc8fSstretchyboy
154ff97cc8fSstretchyboy    /**
155b122d121SAndreas Gohr     * Closes the result set (and it's cursors)
156b122d121SAndreas Gohr     *
157b122d121SAndreas Gohr     * If you're doing SELECT queries inside a TRANSACTION, be sure to call this
158b122d121SAndreas Gohr     * function on all your results sets, before COMMITing the transaction.
159b122d121SAndreas Gohr     *
1604b4b2db0SGerrit Uitslag     * Also required when not all rows of a result are fetched
1614b4b2db0SGerrit Uitslag     *
162*8da7d805SAndreas Gohr     * @param \PDOStatement $res
163b122d121SAndreas Gohr     * @return bool
164b122d121SAndreas Gohr     */
165*8da7d805SAndreas Gohr    public function res_close($res)
166*8da7d805SAndreas Gohr    {
167*8da7d805SAndreas Gohr        if (!$res) return false;
168*8da7d805SAndreas Gohr
169*8da7d805SAndreas Gohr        return $res->closeCursor();
170b122d121SAndreas Gohr    }
171b122d121SAndreas Gohr
172b122d121SAndreas Gohr    /**
173aa81d781SKlap-in     * Returns a complete result set as array
174*8da7d805SAndreas Gohr     *
175*8da7d805SAndreas Gohr     * @param \PDOStatement $res
176*8da7d805SAndreas Gohr     * @return array
177ff97cc8fSstretchyboy     */
178*8da7d805SAndreas Gohr    public function res2arr($res, $assoc = true)
179*8da7d805SAndreas Gohr    {
180*8da7d805SAndreas Gohr        if (!$res) return [];
181*8da7d805SAndreas Gohr
182*8da7d805SAndreas Gohr        // this is a bullshit workaround for having res2arr and res2count work on one result
183*8da7d805SAndreas Gohr        if (!$this->data) {
184*8da7d805SAndreas Gohr            $mode = $assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM;
185*8da7d805SAndreas Gohr            $this->data = $res->fetchAll($mode);
186*8da7d805SAndreas Gohr        }
187*8da7d805SAndreas Gohr        return $this->data;
188b5b947d7SAndreas Gohr    }
189b5b947d7SAndreas Gohr
190b5b947d7SAndreas Gohr    /**
191*8da7d805SAndreas Gohr     * Return the next row from the result set as associative array
192*8da7d805SAndreas Gohr     *
193*8da7d805SAndreas Gohr     * @param \PDOStatement $res
194*8da7d805SAndreas Gohr     * @param int $rownum will be ignored
195b5b947d7SAndreas Gohr     */
196*8da7d805SAndreas Gohr    public function res2row($res, $rownum = 0)
197*8da7d805SAndreas Gohr    {
198*8da7d805SAndreas Gohr        if (!$res) return false;
199*8da7d805SAndreas Gohr
200*8da7d805SAndreas Gohr        return $res->fetch(\PDO::FETCH_ASSOC);
201b5b947d7SAndreas Gohr    }
202b5b947d7SAndreas Gohr
203e7112ccbSAdrian Lang    /**
20444685fc6SKlap-in     * Return the first value from the next row.
205*8da7d805SAndreas Gohr     *
206*8da7d805SAndreas Gohr     * @param \PDOStatement $res
207*8da7d805SAndreas Gohr     * @return mixed
208e7112ccbSAdrian Lang     */
209*8da7d805SAndreas Gohr    public function res2single($res)
210*8da7d805SAndreas Gohr    {
211*8da7d805SAndreas Gohr        if (!$res) return false;
212*8da7d805SAndreas Gohr
213*8da7d805SAndreas Gohr        $data = $res->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_ABS, 0);
214*8da7d805SAndreas Gohr        if (empty($data)) {
215*8da7d805SAndreas Gohr            return false;
216*8da7d805SAndreas Gohr        }
217*8da7d805SAndreas Gohr        return $data[0];
218e7112ccbSAdrian Lang    }
219fee3b689Sstretchyboy
220fee3b689Sstretchyboy    /**
221fee3b689Sstretchyboy     * fetch the next row as zero indexed array
222*8da7d805SAndreas Gohr     *
223*8da7d805SAndreas Gohr     * @param \PDOStatement $res
224*8da7d805SAndreas Gohr     * @return array|bool
225fee3b689Sstretchyboy     */
226*8da7d805SAndreas Gohr    public function res_fetch_array($res)
227*8da7d805SAndreas Gohr    {
228*8da7d805SAndreas Gohr        if (!$res) return false;
229*8da7d805SAndreas Gohr
230*8da7d805SAndreas Gohr        return $res->fetch(PDO::FETCH_NUM);
23187fa2c18Sstretchyboy    }
232fee3b689Sstretchyboy
233fee3b689Sstretchyboy    /**
234fee3b689Sstretchyboy     * fetch the next row as assocative array
235*8da7d805SAndreas Gohr     *
236*8da7d805SAndreas Gohr     * @param \PDOStatement $res
237*8da7d805SAndreas Gohr     * @return array|bool
238fee3b689Sstretchyboy     */
239*8da7d805SAndreas Gohr    public function res_fetch_assoc($res)
240*8da7d805SAndreas Gohr    {
241*8da7d805SAndreas Gohr        if (!$res) return false;
242*8da7d805SAndreas Gohr
243*8da7d805SAndreas Gohr        return $res->fetch(PDO::FETCH_ASSOC);
244fee3b689Sstretchyboy    }
245fee3b689Sstretchyboy
246fee3b689Sstretchyboy    /**
24778977d74SKlap-in     * Count the number of records in result
2483157674bSAndreas Gohr     *
249db58e525SKlap-in     * This function is really inperformant in PDO and should be avoided!
250*8da7d805SAndreas Gohr     *
251*8da7d805SAndreas Gohr     * @param \PDOStatement $res
252*8da7d805SAndreas Gohr     * @return int
253fee3b689Sstretchyboy     */
254*8da7d805SAndreas Gohr    public function res2count($res)
255*8da7d805SAndreas Gohr    {
256*8da7d805SAndreas Gohr        if (!$res) return 0;
257*8da7d805SAndreas Gohr
258*8da7d805SAndreas Gohr        // this is a bullshit workaround for having res2arr and res2count work on one result
259*8da7d805SAndreas Gohr        if (!$this->data) {
260*8da7d805SAndreas Gohr            $this->data = $this->res2arr($res);
261*8da7d805SAndreas Gohr        }
262*8da7d805SAndreas Gohr
263*8da7d805SAndreas Gohr        return count($this->data);
264fee3b689Sstretchyboy    }
26524a03f6cSstretchyboy
26624a03f6cSstretchyboy    /**
26724a03f6cSstretchyboy     * Count the number of records changed last time
268*8da7d805SAndreas Gohr     *
269*8da7d805SAndreas Gohr     * @param \PDOStatement $res
270*8da7d805SAndreas Gohr     * @return int
27124a03f6cSstretchyboy     */
272*8da7d805SAndreas Gohr    public function countChanges($res)
273*8da7d805SAndreas Gohr    {
274*8da7d805SAndreas Gohr        if (!$res) return 0;
275*8da7d805SAndreas Gohr
276*8da7d805SAndreas Gohr        return $res->rowCount();
277a1e6784eSAndreas Gohr    }
278a1e6784eSAndreas Gohr
279*8da7d805SAndreas Gohr    // endregion
280*8da7d805SAndreas Gohr
281*8da7d805SAndreas Gohr    // region quoting/escaping functions
282*8da7d805SAndreas Gohr
283*8da7d805SAndreas Gohr    /**
284*8da7d805SAndreas Gohr     * Join the given values and quote them for SQL insertion
285*8da7d805SAndreas Gohr     */
286*8da7d805SAndreas Gohr    public function quote_and_join($vals, $sep = ',')
287*8da7d805SAndreas Gohr    {
288*8da7d805SAndreas Gohr        $vals = array_map([$this->adapter->pdo(), 'quote'], $vals);
289*8da7d805SAndreas Gohr        return join($sep, $vals);
290*8da7d805SAndreas Gohr    }
291*8da7d805SAndreas Gohr
292*8da7d805SAndreas Gohr    /**
293*8da7d805SAndreas Gohr     * Quotes a string, by escaping it and adding quotes
294*8da7d805SAndreas Gohr     */
295*8da7d805SAndreas Gohr    public function quote_string($string)
296*8da7d805SAndreas Gohr    {
297*8da7d805SAndreas Gohr        return $this->adapter->pdo()->quote($string);
298*8da7d805SAndreas Gohr    }
299*8da7d805SAndreas Gohr
300*8da7d805SAndreas Gohr    /**
301*8da7d805SAndreas Gohr     * Similar to quote_string, but without the quotes, useful to construct LIKE patterns
302*8da7d805SAndreas Gohr     */
303*8da7d805SAndreas Gohr    public function escape_string($str)
304*8da7d805SAndreas Gohr    {
305*8da7d805SAndreas Gohr        return trim($this->adapter->pdo()->quote($str), "'");
306*8da7d805SAndreas Gohr    }
307*8da7d805SAndreas Gohr
308*8da7d805SAndreas Gohr    // endregion
309*8da7d805SAndreas Gohr
310*8da7d805SAndreas Gohr    // region speciality functions
311*8da7d805SAndreas Gohr
312*8da7d805SAndreas Gohr    /**
313*8da7d805SAndreas Gohr     * Split sql queries on semicolons, unless when semicolons are quoted
314*8da7d805SAndreas Gohr     *
315*8da7d805SAndreas Gohr     * Usually you don't need this. It's only really needed if you need individual results for
316*8da7d805SAndreas Gohr     * multiple queries. For example in the admin interface.
317*8da7d805SAndreas Gohr     *
318*8da7d805SAndreas Gohr     * @param string $sql
319*8da7d805SAndreas Gohr     * @return array sql queries
320*8da7d805SAndreas Gohr     * @deprecated
321*8da7d805SAndreas Gohr     */
322*8da7d805SAndreas Gohr    public function SQLstring2array($sql)
323*8da7d805SAndreas Gohr    {
324*8da7d805SAndreas Gohr        dbg_deprecated(Tools::class . '::SQLstring2array');
325*8da7d805SAndreas Gohr        return Tools::SQLstring2array($sql);
326*8da7d805SAndreas Gohr    }
327*8da7d805SAndreas Gohr
328*8da7d805SAndreas Gohr    /**
329*8da7d805SAndreas Gohr     * @deprecated needs to be fixed in stuct and structpublish
330*8da7d805SAndreas Gohr     */
331*8da7d805SAndreas Gohr    public function doTransaction($sql, $sqlpreparing = true) {
332*8da7d805SAndreas Gohr        throw new \Exception(
333*8da7d805SAndreas Gohr            'This method seems to never have done what it suggests. Please use the query() function instead.'
334*8da7d805SAndreas Gohr        );
335*8da7d805SAndreas Gohr    }
336*8da7d805SAndreas Gohr
337*8da7d805SAndreas Gohr    // endregion
338aa81d781SKlap-in}
339