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