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

Revision 190, 8.3 KB (checked in by exi, 15 years ago)

This is a big update...
Moved all admin scripts to common/admin.
Moved all includes and classes to common/includes.
Edited all include-paths to reflect the movement.
Fixed a bug with the session system allowing every registered user to access admin pages.
Replaced calls to date() to use a wrapper so gmdate can be used.
Replaced some calls to $config with calls to the static object.
Fixed a big which caused the portrait_grab to not download a new picute.
Added a classified-state to kills.
Removed the sync_server server script in this tree.
Added code to help modules find the includes to index.php.

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