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