xref: /plugin/sqlite/helper.php (revision a1e6784e2c7523d37b71c8cb79af18d35ee17707)
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