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