1*a1e6784eSAndreas Gohr<?php 2*a1e6784eSAndreas Gohr/** 3*a1e6784eSAndreas Gohr * DokuWiki Plugin sqlite (Helper Component) 4*a1e6784eSAndreas Gohr * 5*a1e6784eSAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6*a1e6784eSAndreas Gohr * @author Andreas Gohr <gohr@cosmocode.de> 7*a1e6784eSAndreas Gohr */ 8*a1e6784eSAndreas Gohr 9*a1e6784eSAndreas Gohr// must be run within Dokuwiki 10*a1e6784eSAndreas Gohrif (!defined('DOKU_INC')) die(); 11*a1e6784eSAndreas Gohr 12*a1e6784eSAndreas Gohrif (!defined('DOKU_LF')) define('DOKU_LF', "\n"); 13*a1e6784eSAndreas Gohrif (!defined('DOKU_TAB')) define('DOKU_TAB', "\t"); 14*a1e6784eSAndreas Gohrif (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 15*a1e6784eSAndreas Gohr 16*a1e6784eSAndreas Gohrclass helper_plugin_sqlite extends DokuWiki_Plugin { 17*a1e6784eSAndreas Gohr var $db = null; 18*a1e6784eSAndreas Gohr var $dbname = ''; 19*a1e6784eSAndreas Gohr 20*a1e6784eSAndreas Gohr function getInfo() { 21*a1e6784eSAndreas Gohr return confToHash(dirname(__FILE__).'plugin.info.txt'); 22*a1e6784eSAndreas Gohr } 23*a1e6784eSAndreas Gohr 24*a1e6784eSAndreas Gohr /** 25*a1e6784eSAndreas Gohr * constructor 26*a1e6784eSAndreas Gohr */ 27*a1e6784eSAndreas Gohr function helper_plugin_sqlite(){ 28*a1e6784eSAndreas Gohr if (!extension_loaded('sqlite')) { 29*a1e6784eSAndreas Gohr $prefix = (PHP_SHLIB_SUFFIX === 'dll') ? 'php_' : ''; 30*a1e6784eSAndreas Gohr if(function_exists('dl')) @dl($prefix . 'sqlite.' . PHP_SHLIB_SUFFIX); 31*a1e6784eSAndreas Gohr } 32*a1e6784eSAndreas Gohr 33*a1e6784eSAndreas Gohr if(!function_exists('sqlite_open')){ 34*a1e6784eSAndreas Gohr msg('SQLite support missing in this PHP install - plugin will not work',-1); 35*a1e6784eSAndreas Gohr } 36*a1e6784eSAndreas Gohr } 37*a1e6784eSAndreas Gohr 38*a1e6784eSAndreas Gohr /** 39*a1e6784eSAndreas Gohr * Initializes and opens the database 40*a1e6784eSAndreas Gohr * 41*a1e6784eSAndreas Gohr * Needs to be called right after loading this helper plugin 42*a1e6784eSAndreas Gohr */ 43*a1e6784eSAndreas Gohr function init($dbname,$updatedir){ 44*a1e6784eSAndreas Gohr global $conf; 45*a1e6784eSAndreas Gohr 46*a1e6784eSAndreas Gohr // check for already open DB 47*a1e6784eSAndreas Gohr if($this->db){ 48*a1e6784eSAndreas Gohr if($this->dbname == $dbname){ 49*a1e6784eSAndreas Gohr // db already open 50*a1e6784eSAndreas Gohr return true; 51*a1e6784eSAndreas Gohr } 52*a1e6784eSAndreas Gohr // close other db 53*a1e6784eSAndreas Gohr sqlite_close($this->db); 54*a1e6784eSAndreas Gohr $this->db = null; 55*a1e6784eSAndreas Gohr $this->dbname = ''; 56*a1e6784eSAndreas Gohr } 57*a1e6784eSAndreas Gohr 58*a1e6784eSAndreas Gohr $this->dbname = $dbname; 59*a1e6784eSAndreas Gohr $dbfile = $conf['metadir'].'/'.$dbname.'.sqlite'; 60*a1e6784eSAndreas Gohr $init = (!@file_exists($dbfile) || ((int) @filesize($dbfile)) < 3); 61*a1e6784eSAndreas Gohr 62*a1e6784eSAndreas Gohr $error=''; 63*a1e6784eSAndreas Gohr $this->db = sqlite_open($dbfile, 0666, $error); 64*a1e6784eSAndreas Gohr if(!$this->db){ 65*a1e6784eSAndreas Gohr msg("SQLite: failed to open SQLite ".$this->dbname." database ($error)",-1); 66*a1e6784eSAndreas Gohr return false; 67*a1e6784eSAndreas Gohr } 68*a1e6784eSAndreas Gohr 69*a1e6784eSAndreas Gohr $this->_updatedb($init,$updatedir); 70*a1e6784eSAndreas Gohr return true; 71*a1e6784eSAndreas Gohr } 72*a1e6784eSAndreas Gohr 73*a1e6784eSAndreas Gohr /** 74*a1e6784eSAndreas Gohr * Return the current Database Version 75*a1e6784eSAndreas Gohr */ 76*a1e6784eSAndreas Gohr function _currentDBversion(){ 77*a1e6784eSAndreas Gohr $sql = "SELECT val FROM opts WHERE opt = 'dbversion';"; 78*a1e6784eSAndreas Gohr $res = $this->query($sql); 79*a1e6784eSAndreas Gohr if(!$res) return false; 80*a1e6784eSAndreas Gohr $row = $this->res2row($res,0); 81*a1e6784eSAndreas Gohr return (int) $row['val']; 82*a1e6784eSAndreas Gohr } 83*a1e6784eSAndreas Gohr /** 84*a1e6784eSAndreas Gohr * Update the database if needed 85*a1e6784eSAndreas Gohr * 86*a1e6784eSAndreas Gohr * @param bool $init - true if this is a new database to initialize 87*a1e6784eSAndreas Gohr * @param string $updatedir - Database update infos 88*a1e6784eSAndreas Gohr */ 89*a1e6784eSAndreas Gohr function _updatedb($init,$updatedir){ 90*a1e6784eSAndreas Gohr if($init){ 91*a1e6784eSAndreas Gohr $current = 0; 92*a1e6784eSAndreas Gohr }else{ 93*a1e6784eSAndreas Gohr $current = $this->_currentDBversion(); 94*a1e6784eSAndreas Gohr if(!$current){ 95*a1e6784eSAndreas Gohr msg('SQLite: no DB version found. '.$this->dbname.' DB probably broken.',-1); 96*a1e6784eSAndreas Gohr return false; 97*a1e6784eSAndreas Gohr } 98*a1e6784eSAndreas Gohr } 99*a1e6784eSAndreas Gohr 100*a1e6784eSAndreas Gohr // in case of init, add versioning table 101*a1e6784eSAndreas Gohr if($init){ 102*a1e6784eSAndreas Gohr if(!$this->_runupdatefile(dirname(__FILE__).'/db.sql',0)){ 103*a1e6784eSAndreas Gohr msg('SQLite: '.$this->dbname.' database upgrade failed for version '.$i, -1); 104*a1e6784eSAndreas Gohr return false; 105*a1e6784eSAndreas Gohr } 106*a1e6784eSAndreas Gohr } 107*a1e6784eSAndreas Gohr 108*a1e6784eSAndreas Gohr $latest = (int) trim(io_readFile($updatedir.'/latest.version')); 109*a1e6784eSAndreas Gohr 110*a1e6784eSAndreas Gohr // all up to date? 111*a1e6784eSAndreas Gohr if($current >= $latest) return true; 112*a1e6784eSAndreas Gohr for($i=$current+1; $i<=$latest; $i++){ 113*a1e6784eSAndreas Gohr $file = sprintf($updatedir.'/update%04d.sql',$i); 114*a1e6784eSAndreas Gohr if(file_exists($file)){ 115*a1e6784eSAndreas Gohr if(!$this->_runupdatefile($file,$i)){ 116*a1e6784eSAndreas Gohr msg('SQLite: '.$this->dbname.' database upgrade failed for version '.$i, -1); 117*a1e6784eSAndreas Gohr 118*a1e6784eSAndreas Gohr 119*a1e6784eSAndreas Gohr return false; 120*a1e6784eSAndreas Gohr } 121*a1e6784eSAndreas Gohr } 122*a1e6784eSAndreas Gohr } 123*a1e6784eSAndreas Gohr return true; 124*a1e6784eSAndreas Gohr } 125*a1e6784eSAndreas Gohr 126*a1e6784eSAndreas Gohr /** 127*a1e6784eSAndreas Gohr * Updates the database structure using the given file to 128*a1e6784eSAndreas Gohr * the given version. 129*a1e6784eSAndreas Gohr */ 130*a1e6784eSAndreas Gohr function _runupdatefile($file,$version){ 131*a1e6784eSAndreas Gohr $sql = io_readFile($file,false); 132*a1e6784eSAndreas Gohr 133*a1e6784eSAndreas Gohr $sql = explode(";",$sql); 134*a1e6784eSAndreas Gohr array_unshift($sql,'BEGIN TRANSACTION'); 135*a1e6784eSAndreas Gohr array_push($sql,"INSERT OR REPLACE INTO opts (val,opt) VALUES ($version,'dbversion')"); 136*a1e6784eSAndreas Gohr array_push($sql,"COMMIT TRANSACTION"); 137*a1e6784eSAndreas Gohr 138*a1e6784eSAndreas Gohr foreach($sql as $s){ 139*a1e6784eSAndreas Gohr $s = preg_replace('!^\s*--.*$!m', '', $s); 140*a1e6784eSAndreas Gohr $s = trim($s); 141*a1e6784eSAndreas Gohr if(!$s) continue; 142*a1e6784eSAndreas Gohr $res = $this->query("$s;"); 143*a1e6784eSAndreas Gohr if ($res === false) { 144*a1e6784eSAndreas Gohr sqlite_query($this->db, 'ROLLBACK TRANSACTION'); 145*a1e6784eSAndreas Gohr return false; 146*a1e6784eSAndreas Gohr } 147*a1e6784eSAndreas Gohr } 148*a1e6784eSAndreas Gohr 149*a1e6784eSAndreas Gohr return ($version == $this->_currentDBversion()); 150*a1e6784eSAndreas Gohr } 151*a1e6784eSAndreas Gohr 152*a1e6784eSAndreas Gohr /** 153*a1e6784eSAndreas Gohr * Execute a query with the given parameters. 154*a1e6784eSAndreas Gohr * 155*a1e6784eSAndreas Gohr * Takes care of escaping 156*a1e6784eSAndreas Gohr * 157*a1e6784eSAndreas Gohr * @param string $sql - the statement 158*a1e6784eSAndreas Gohr * @param arguments... 159*a1e6784eSAndreas Gohr */ 160*a1e6784eSAndreas Gohr function query(){ 161*a1e6784eSAndreas Gohr if(!$this->db) return false; 162*a1e6784eSAndreas Gohr 163*a1e6784eSAndreas Gohr // get function arguments 164*a1e6784eSAndreas Gohr $args = func_get_args(); 165*a1e6784eSAndreas Gohr $sql = trim(array_shift($args)); 166*a1e6784eSAndreas Gohr 167*a1e6784eSAndreas Gohr if(!$sql){ 168*a1e6784eSAndreas Gohr msg('No SQL statement given',-1); 169*a1e6784eSAndreas Gohr return false; 170*a1e6784eSAndreas Gohr } 171*a1e6784eSAndreas Gohr 172*a1e6784eSAndreas Gohr if(is_array($args[0])) $args = $args[0]; 173*a1e6784eSAndreas Gohr $argc = count($args); 174*a1e6784eSAndreas Gohr 175*a1e6784eSAndreas Gohr // check number of arguments 176*a1e6784eSAndreas Gohr if($argc < substr_count($sql,'?')){ 177*a1e6784eSAndreas Gohr msg('Not enough arguments passed for statement. '. 178*a1e6784eSAndreas Gohr 'Expected '.substr_count($sql,'?').' got '. 179*a1e6784eSAndreas Gohr $argc.' - '.hsc($sql),-1); 180*a1e6784eSAndreas Gohr return false; 181*a1e6784eSAndreas Gohr } 182*a1e6784eSAndreas Gohr 183*a1e6784eSAndreas Gohr // explode at wildcard, then join again 184*a1e6784eSAndreas Gohr $parts = explode('?',$sql,$argc+1); 185*a1e6784eSAndreas Gohr $args = array_map(array($this,'quote_string'),$args); 186*a1e6784eSAndreas Gohr $sql = ''; 187*a1e6784eSAndreas Gohr 188*a1e6784eSAndreas Gohr while( ($part = array_shift($parts)) !== null ){ 189*a1e6784eSAndreas Gohr $sql .= $part; 190*a1e6784eSAndreas Gohr $sql .= array_shift($args); 191*a1e6784eSAndreas Gohr } 192*a1e6784eSAndreas Gohr 193*a1e6784eSAndreas Gohr // execute query 194*a1e6784eSAndreas Gohr $err = ''; 195*a1e6784eSAndreas Gohr $res = @sqlite_query($this->db,$sql,SQLITE_ASSOC,$err); 196*a1e6784eSAndreas Gohr if($err){ 197*a1e6784eSAndreas Gohr msg($err.' - '.hsc($sql),-1); 198*a1e6784eSAndreas Gohr return false; 199*a1e6784eSAndreas Gohr }elseif(!$res){ 200*a1e6784eSAndreas Gohr msg(sqlite_error_string(sqlite_last_error($this->db)). 201*a1e6784eSAndreas Gohr ' - '.hsc($sql),-1); 202*a1e6784eSAndreas Gohr return false; 203*a1e6784eSAndreas Gohr } 204*a1e6784eSAndreas Gohr 205*a1e6784eSAndreas Gohr return $res; 206*a1e6784eSAndreas Gohr } 207*a1e6784eSAndreas Gohr 208*a1e6784eSAndreas Gohr /** 209*a1e6784eSAndreas Gohr * Returns a complete result set as array 210*a1e6784eSAndreas Gohr */ 211*a1e6784eSAndreas Gohr function res2arr($res){ 212*a1e6784eSAndreas Gohr $data = array(); 213*a1e6784eSAndreas Gohr if(!sqlite_num_rows($res)) return $data; 214*a1e6784eSAndreas Gohr sqlite_rewind($res); 215*a1e6784eSAndreas Gohr while(($row = sqlite_fetch_array($res)) !== false){ 216*a1e6784eSAndreas Gohr $data[] = $row; 217*a1e6784eSAndreas Gohr } 218*a1e6784eSAndreas Gohr return $data; 219*a1e6784eSAndreas Gohr } 220*a1e6784eSAndreas Gohr 221*a1e6784eSAndreas Gohr /** 222*a1e6784eSAndreas Gohr * Return the wanted row from a given result set as 223*a1e6784eSAndreas Gohr * associative array 224*a1e6784eSAndreas Gohr */ 225*a1e6784eSAndreas Gohr function res2row($res,$rownum=0){ 226*a1e6784eSAndreas Gohr if(!@sqlite_seek($res,$rownum)){ 227*a1e6784eSAndreas Gohr return false; 228*a1e6784eSAndreas Gohr } 229*a1e6784eSAndreas Gohr return sqlite_fetch_array($res); 230*a1e6784eSAndreas Gohr } 231*a1e6784eSAndreas Gohr 232*a1e6784eSAndreas Gohr 233*a1e6784eSAndreas Gohr /** 234*a1e6784eSAndreas Gohr * Join the given values and quote them for SQL insertion 235*a1e6784eSAndreas Gohr */ 236*a1e6784eSAndreas Gohr function quote_and_join($vals,$sep=',') { 237*a1e6784eSAndreas Gohr $vals = array_map(array('helper_plugin_sqlite','quote_string'),$vals); 238*a1e6784eSAndreas Gohr return join($sep,$vals); 239*a1e6784eSAndreas Gohr } 240*a1e6784eSAndreas Gohr 241*a1e6784eSAndreas Gohr /** 242*a1e6784eSAndreas Gohr * Run sqlite_escape_string() on the given string and surround it 243*a1e6784eSAndreas Gohr * with quotes 244*a1e6784eSAndreas Gohr */ 245*a1e6784eSAndreas Gohr function quote_string($string){ 246*a1e6784eSAndreas Gohr return "'".sqlite_escape_string($string)."'"; 247*a1e6784eSAndreas Gohr } 248*a1e6784eSAndreas Gohr 249*a1e6784eSAndreas Gohr 250*a1e6784eSAndreas Gohr} 251*a1e6784eSAndreas Gohr 252*a1e6784eSAndreas Gohr// vim:ts=4:sw=4:et:enc=utf-8: 253