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