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