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 // check if args contains single array 163 if (isset($args[0]) && is_array($args[0])) { 164 $args = $args[0]; 165 } 166 167 // clear the cache 168 $this->data = null; 169 170 try { 171 return $this->adapter->query($sql, $args); 172 } catch (\Exception $e) { 173 msg('SQLite: ' . $e->getMessage(), -1); 174 return false; 175 } 176 } 177 178 179 /** 180 * Closes the result set (and it's cursors) 181 * 182 * If you're doing SELECT queries inside a TRANSACTION, be sure to call this 183 * function on all your results sets, before COMMITing the transaction. 184 * 185 * Also required when not all rows of a result are fetched 186 * 187 * @param \PDOStatement $res 188 * @return bool 189 */ 190 public function res_close($res) 191 { 192 if (!$res) return false; 193 194 return $res->closeCursor(); 195 } 196 197 /** 198 * Returns a complete result set as array 199 * 200 * @param \PDOStatement $res 201 * @return array 202 */ 203 public function res2arr($res, $assoc = true) 204 { 205 if (!$res) return []; 206 207 // this is a bullshit workaround for having res2arr and res2count work on one result 208 if (!$this->data) { 209 $mode = $assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM; 210 $this->data = $res->fetchAll($mode); 211 } 212 return $this->data; 213 } 214 215 /** 216 * Return the next row from the result set as associative array 217 * 218 * @param \PDOStatement $res 219 * @param int $rownum will be ignored 220 */ 221 public function res2row($res, $rownum = 0) 222 { 223 if (!$res) return false; 224 225 return $res->fetch(\PDO::FETCH_ASSOC); 226 } 227 228 /** 229 * Return the first value from the next row. 230 * 231 * @param \PDOStatement $res 232 * @return mixed 233 */ 234 public function res2single($res) 235 { 236 if (!$res) return false; 237 238 $data = $res->fetch(PDO::FETCH_NUM, PDO::FETCH_ORI_ABS, 0); 239 if (empty($data)) { 240 return false; 241 } 242 return $data[0]; 243 } 244 245 /** 246 * fetch the next row as zero indexed array 247 * 248 * @param \PDOStatement $res 249 * @return array|bool 250 */ 251 public function res_fetch_array($res) 252 { 253 if (!$res) return false; 254 255 return $res->fetch(PDO::FETCH_NUM); 256 } 257 258 /** 259 * fetch the next row as assocative array 260 * 261 * @param \PDOStatement $res 262 * @return array|bool 263 */ 264 public function res_fetch_assoc($res) 265 { 266 if (!$res) return false; 267 268 return $res->fetch(PDO::FETCH_ASSOC); 269 } 270 271 /** 272 * Count the number of records in result 273 * 274 * This function is really inperformant in PDO and should be avoided! 275 * 276 * @param \PDOStatement $res 277 * @return int 278 */ 279 public function res2count($res) 280 { 281 if (!$res) return 0; 282 283 // this is a bullshit workaround for having res2arr and res2count work on one result 284 if (!$this->data) { 285 $this->data = $this->res2arr($res); 286 } 287 288 return count($this->data); 289 } 290 291 /** 292 * Count the number of records changed last time 293 * 294 * @param \PDOStatement $res 295 * @return int 296 */ 297 public function countChanges($res) 298 { 299 if (!$res) return 0; 300 301 return $res->rowCount(); 302 } 303 304 // endregion 305 306 // region quoting/escaping functions 307 308 /** 309 * Join the given values and quote them for SQL insertion 310 */ 311 public function quote_and_join($vals, $sep = ',') 312 { 313 $vals = array_map([$this->adapter->pdo(), 'quote'], $vals); 314 return join($sep, $vals); 315 } 316 317 /** 318 * Quotes a string, by escaping it and adding quotes 319 */ 320 public function quote_string($string) 321 { 322 return $this->adapter->pdo()->quote($string); 323 } 324 325 /** 326 * Similar to quote_string, but without the quotes, useful to construct LIKE patterns 327 */ 328 public function escape_string($str) 329 { 330 return trim($this->adapter->pdo()->quote($str), "'"); 331 } 332 333 // endregion 334 335 // region speciality functions 336 337 /** 338 * Split sql queries on semicolons, unless when semicolons are quoted 339 * 340 * Usually you don't need this. It's only really needed if you need individual results for 341 * multiple queries. For example in the admin interface. 342 * 343 * @param string $sql 344 * @return array sql queries 345 * @deprecated 346 */ 347 public function SQLstring2array($sql) 348 { 349 if(!DOKU_UNITTEST) { // for now we don't want to trigger the deprecation warning in the tests 350 dbg_deprecated(Tools::class . '::SQLstring2array'); 351 } 352 return Tools::SQLstring2array($sql); 353 } 354 355 /** 356 * @deprecated needs to be fixed in stuct and structpublish 357 */ 358 public function doTransaction($sql, $sqlpreparing = true) { 359 throw new \Exception( 360 'This method seems to never have done what it suggests. Please use the query() function instead.' 361 ); 362 } 363 364 // endregion 365} 366