root/dev/common/includes/class.db_cache.php @ 216

Revision 216, 9.0 KB (checked in by ralle030583, 14 years ago)

- new table kb3_navigation
- new Admin Section Navigation - Top Navigation
- removes customable top nav bar changes which came with last revs
- new tablestruct added to autoupgrade.php
- db_cache error when creating new table fixed

Line 
1<?php
2
3class DBConnection
4{
5    function DBConnection()
6    {
7        static $conn_id;
8
9        if (is_resource($conn_id))
10        {
11            $this->id_ = $conn_id;
12            return;
13        }
14        if (!$this->id_ = mysql_connect(DB_HOST, DB_USER, DB_PASS))
15            die("Unable to connect to mysql database.");
16
17        mysql_select_db(DB_NAME);
18        $conn_id = $this->id_;
19    }
20
21    function id()
22    {
23        return $this->id_;
24    }
25
26    function affectedRows()
27    {
28        return mysql_affected_rows($this->id_);
29    }
30}
31
32class DBQuery
33{
34    function DBQuery()
35    {
36        $this->executed_ = false;
37        $this->_cache = array();
38        $this->_cached = false;
39
40        // this is the minimum runtime a query has to run to be
41        // eligible for caching in seconds
42        $this->_minruntime = 0.1;
43
44        // maximum size of a cached result set (512kB)
45        $this->_maxcachesize = 524288;
46        $this->d = true;
47    }
48
49    function checkCache()
50    {
51        // only cache selects
52        // we don't use select ... into so there is no problem
53        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
54        {
55            // this is no select, update the table
56            $this->markAffectedTables();
57            return false;
58        }
59
60        if (file_exists(KB_CACHEDIR.'/qcache_qry_'.$this->_hash))
61        {
62            $this->_mtime = filemtime(KB_CACHEDIR.'/qcache_qry_'.$this->_hash);
63            if ($this->isCacheValid())
64            {
65                return true;
66            }
67        }
68
69        return false;
70    }
71
72    function parseSQL()
73    {
74        // gets all involved tables for a select statement
75        $text = strtolower($this->_sql).' ';
76
77        // we try to get the text from 'from' to 'where' because all involved
78        // tables are declared in that part
79        $from = strpos($text, 'from')+5;
80        if (!$to = strpos($text, 'where'))
81        {
82            $to = strlen($text);
83        }
84        $parse = trim(substr($text, $from, $to-$from));
85
86        $tables = array();
87        if (strpos($parse, ',') !== false)
88        {
89            // , is a synonym for join so we'll replace them
90            $parse = str_replace(',', ' join ', $parse);
91        }
92        if (strpos($parse, 'join'))
93        {
94            // if this query is a join we parse it with regexp to get all tables
95            preg_match_all('/join (.*?) /', $parse, $match);
96            $tables = $match[1];
97        }
98        else
99        {
100            // no join so it is hopefully a simple table select
101            $tables[] = $parse;
102        }
103
104        $this->_usedtables = $tables;
105    }
106
107    function isCacheValid()
108    {
109        // check if cachefiles are stil valid
110
111        // first, we need to get all involved tables
112        $this->parseSQL();
113
114        foreach ($this->_usedtables as $table)
115        {
116            $file = KB_CACHEDIR.'/qcache_tbl_'.trim($table);
117            if (file_exists($file))
118            {
119                // if one of the tables is outdated, the query is outdated
120                if ($this->_mtime < filemtime($file))
121                {
122                    return false;
123                }
124            }
125        }
126        return true;
127    }
128
129    function markAffectedTables()
130    {
131        // this function invalidates cache files for touched tables
132        $text = trim(strtolower($this->_sql));
133        $text = str_replace(array('ignore','`', "\r\n", "\n"), '', $text);
134        $ta = preg_split('/ /', $text, 0, PREG_SPLIT_NO_EMPTY);
135
136        // check for sql keywords and get the table from the appropriate position
137        $tables = array();
138        if ($ta[0] == 'update')
139        {
140            $tables[] = $ta[1];
141        }
142        elseif ($ta[0] == 'insert')
143        {
144            $tables[] = $ta[2];
145        }
146        elseif ($ta[0] == 'replace')
147        {
148            $tables[] = $ta[2];
149        }
150        elseif ($ta[0] == 'delete')
151        {
152            $tables[] = $ta[2];
153        }
154        elseif ($ta[0] == 'alter')
155        {
156            return false;
157        }
158        elseif ($ta[0] == 'create')
159        {
160            return false;
161        }
162        else
163        {
164            var_dump($ta);
165            trigger_error('No suitable handler for query found.',E_USER_WARNING);
166            return false;
167        }
168
169        foreach ($tables as $table)
170        {
171            $file = KB_CACHEDIR.'/qcache_tbl_'.$table;
172            touch($file);
173        }
174        // refresh php's filestatcache so we dont get wrong timestamps on changed files
175        clearstatcache();
176    }
177
178    function genCache()
179    {
180        // this function fetches all rows and writes the data into a textfile
181
182        // don't attemp to cache updates!
183        if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
184        {
185            return false;
186        }
187
188        $bsize = 0;
189        while ($row = $this->getRow())
190        {
191            $this->_cache[] = $row;
192
193            // if the bytesize of the table exceeds the limit we'll abort
194            // the cache generation and leave this query unbuffered
195            $bsize += join('', $row);
196            if ($bsize > $this->_maxcachesize)
197            {
198                $this->_cache[] = array();
199                $this->_cached = false;
200                $this->rewind();
201                return false;
202            }
203        }
204
205        // write data into textfile
206        file_put_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash, serialize($this->_cache));
207
208        $this->_cached = true;
209        $this->_currrow = 0;
210        $this->executed_ = true;
211    }
212
213    function loadCache()
214    {
215        // loads the cachefile into the memory
216        $this->_cache = unserialize(file_get_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash));
217
218        $this->_cached = true;
219        $this->_currrow = 0;
220        $this->executed_ = true;
221    }
222
223    function execute($sql)
224    {
225        $this->_sql = trim($sql);
226        $this->_hash = md5($this->_sql);
227        $this->_cache = array();
228        $this->_cached = false;
229
230        if ($this->checkCache())
231        {
232            $this->loadCache();
233            $this->queryCachedCount(true);
234            return true;
235        }
236
237        // we got no or no valid cache so open the connection and run the query
238        $this->dbconn_ = new DBConnection;
239
240        $t1 = strtok(microtime(), ' ') + strtok('');
241
242        $this->resid_ = mysql_query($sql, $this->dbconn_->id());
243
244        if ($this->resid_ == false)
245        {
246            if (DB_HALTONERROR === true)
247            {
248                echo "Database error: ".mysql_error($this->dbconn_->id())."<br/>";
249                echo "SQL: ".$this->_sql."<br/>";
250                exit;
251            }
252            else
253            {
254                return false;
255            }
256        }
257
258        $this->exectime_ = strtok(microtime(), ' ') + strtok('') - $t1;
259        $this->executed_ = true;
260
261        if (KB_PROFILE == 2)
262        {
263            file_put_contents('/tmp/profile.lst', $sql."\nExecution time: ".$this->exectime_."\n", FILE_APPEND);
264        }
265
266        // if the query was too slow we'll fetch all rows and run it cached
267        if ($this->exectime_ > $this->_minruntime)
268        {
269            $this->genCache();
270        }
271
272        $this->queryCount(true);
273        return true;
274    }
275
276    function queryCount($increase = false)
277    {
278        static $count;
279
280        if ($increase)
281        {
282            $count++;
283        }
284
285        return $count;
286    }
287
288    function queryCachedCount($increase = false)
289    {
290        static $count;
291
292        if ($increase)
293        {
294            $count++;
295        }
296
297        return $count;
298    }
299
300    function recordCount()
301    {
302        if ($this->_cached)
303        {
304            return count($this->_cache);
305        }
306        return mysql_num_rows($this->resid_);
307    }
308
309    function getRow()
310    {
311        if ($this->_cached)
312        {
313            if (!isset($this->_cache[$this->_currrow]))
314            {
315                return false;
316            }
317            // return the current row and increase the pointer by one
318            return $this->_cache[$this->_currrow++];
319        }
320        if (is_resource($this->resid_))
321        {
322            return mysql_fetch_assoc($this->resid_);
323        }
324        return false;
325    }
326
327    function rewind()
328    {
329        if ($this->_cached)
330        {
331            $this->_currrow = 0;
332        }
333        @mysql_data_seek($this->resid_, 0);
334    }
335
336    function getInsertID()
337    {
338        return mysql_insert_id();
339    }
340
341    function execTime()
342    {
343        return $this->exectime_;
344    }
345
346    function executed()
347    {
348        return $this->executed_;
349    }
350
351    function getErrorMsg()
352    {
353        $msg = $this->sql_."<br>";
354        $msg .= "Query failed. ".mysql_error($this->dbconn_->id());
355
356        return $msg;
357    }
358}
359?>
Note: See TracBrowser for help on using the browser.