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