国内著名CMS:PHPCMS整站源码分析(二)

Gavin [PHP]

2008.11.15

二、db_mysql.class.php

这个讲 phpcms 的数据库类 和 phpcms 的文本缓存的实现。

首先是数据库类,phpcms 的数据库 分mysql 和mssql 版本. MSSQL 版本的我就不说了. 他们主要的sql 语句不同点就在于我们分页常用到的 limit 语句.所以.在 mssql 数据库类驱动里.他做了个挺好的封装.让MYSQL 和MSSQL在SQL语句方面的差异性就很小了.可以说PHPCMS你可以随便转换数据库只要在代码中换下数据库类驱动就行.

db_mysql.class.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/**
mysql数据库类。写得比较简单。也没什么好说的。大家自己看下理解下。
然后就可以跳过了。
*/
defined('IN_PHPCMS') or exit('Access Denied');
/**
这个东西是不是很熟呀。对了。在上一章已经讲过了。也已经在上一章的common.inc.php 启动文件里面定义了 IN_PHPCMS 所以在以下的PHP文件里都检测下是否是人为”跳墙“进来的。是就中断
*/
/**
* Mysql 数据库类,支持Cache功能
*/
class db_mysql
{
/**
* MySQL 连接标识
* @var resource
*/
var $connid;
/**
* 整型变量用来计算被执行的sql语句数量
* @var int
*/
var $querynum = 0;
/**
* 数据库连接,返回数据库连接标识符
* @param string 数据库服务器主机
* @param string 数据库服务器帐号
* @param string 数据库服务器密码
* @param string 数据库名
* @param bool 是否保持持续连接,1为持续连接,0为非持续连接
* @return link_identifier
*/
function connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect = 0)
{
global $CONFIG;
$func = $pconnect == 1 ? 'mysql_pconnect' : 'mysql_connect';
/**
mysql_pconnect() 为常连接。它和mysql_connect 的区别是 前者在多进程的WEB服务器上效率比较好。但也有瑕疵就是在有关事务和数据表锁方面。详情请查看自己的手册。
*/
if(!$this->connid = @$func($dbhost, $dbuser, $dbpw))
{
$this->halt('Can not connect to MySQL server');
}
// 当mysql版本为4.1以上时,启用数据库字符集设置
 
if($this->version() > '4.1' && $CONFIG['dbcharset'])
{
mysql_query("SET NAMES '".$CONFIG['dbcharset']."'" , $this->connid);
}
// 当mysql版本为5.0以上时,设置sql mode,mysql5数据库带了字符集模式。设置下就好
 
if($this->version() > '5.0')
{
mysql_query("SET sql_mode=''" , $this->connid);
}
if($dbname)
{
if([email=!@mysql_select_db($dbname]!@mysql_select_db($dbname[/email] , $this->connid))
{
$this->halt('Cannot use database '.$dbname);
}
}
return $this->connid;
}
/**
* 选择数据库
* @param string 数据库名
*/
function select_db($dbname)
{
return mysql_select_db($dbname , $this->connid);
}
/**
* 执行sql语句
* @param string sql语句
* @param string 默认为空,可选值为 CACHE UNBUFFERED
* @param int Cache以秒为单位的生命周期
* @return resource
*/
function query($sql , $type = '' , $expires = 3600, $dbname = '')
{
$func = $type == 'UNBUFFERED' ? 'mysql_unbuffered_query' : 'mysql_query';
/**
mysql_unbuffered_query 效率更好。节省内存 看手册
*/
if(!($query = $func($sql , $this->connid)) && $type != 'SILENT')
{
$this->halt('MySQL Query Error', $sql);
}
$this->querynum++;
return $query;
}
/**
* 执行sql语句,只得到一条记录
* @param string sql语句
* @param string 默认为空,可选值为 CACHE UNBUFFERED
* @param int Cache以秒为单位的生命周期
* @return array
*/
function get_one($sql, $type = '', $expires = 3600, $dbname = '')
{
$query = $this->query($sql, $type, $expires, $dbname);
$rs = $this->fetch_array($query);
$this->free_result($query);
return $rs ;
}
/**
* 从结果集中取得一行作为关联数组
* @param resource 数据库查询结果资源
* @param string 定义返回类型
* @return array
*/
function fetch_array($query, $result_type = MYSQL_ASSOC)
{
return mysql_fetch_array($query, $result_type);
}
/**
* 取得前一次 MySQL 操作所影响的记录行数
* @return int
*/
function affected_rows()
{
return mysql_affected_rows($this->connid);
}
/**
* 取得结果集中行的数目
* @return int
*/
function num_rows($query)
{
return mysql_num_rows($query);
}
/**
* 返回结果集中字段的数目
* @return int
*/
function num_fields($query)
{
return mysql_num_fields($query);
}
/**
* @return array
*/
function result($query, $row)
{
return @mysql_result($query, $row);
}
function free_result($query)
{
return mysql_free_result($query);
}
/**
* 取得上一步 INSERT 操作产生的 ID
* @return int
*/
function insert_id()
{
return mysql_insert_id($this->connid);
}
/**
* @return array
*/
function fetch_row($query)
{
return mysql_fetch_row($query);
}
/**
* @return string
*/
function version()
{
return mysql_get_server_info($this->connid);
}
function close()
{
return mysql_close($this->connid);
}
/**
* @return string
*/
function error()
{
return @mysql_error($this->connid);
}
/**
* @return int
*/
function errno()
{
return intval(@mysql_errno($this->connid)) ;
}
/**
mysql_errno() 函数也挺好使的哦。自己试下
*/
/**
* 显示mysql错误信息
*/
function halt($message = '', $sql = '')
{
exit("MySQL Query:$sql
MySQL Error:".$this->error()."
MySQL Errno:".$this->errno()."
Message:$message");
}
}
?>

PHPCMS的文本缓存实现:

cache.func.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
defined('IN_PHPCMS') or exit('Access Denied');
/**
这个文件里面全是有关生成文本缓存的函数。文本缓存是个好东西。一般的项目,我们用不着内存缓存 : memcached ,文本搞定。
原理是这样的: 我们在后台是不是可以设置很多有关网站的参数。而这些参数很多都是固定的。就不变化的。都存到咱的数据库上。而我们程序那里呢
每次都要访问数据库读出参数来进行我们程序中的操作。首先数据库查询是个很耗硬盘IO资源的一个东西,所以文本缓存刚好能减轻数据库那边的承重。
我们在程序开始就把数据库里面的配置都转化为数组 等 放到 php文件里面。这样我们可以直接访问php文件而不用每次都访问数据库了。 php文本缓存其实成了
我们程序和数据库的一个中间件。 所以我们自己写自己的文本缓存的时候其实要实现的很简单: 读数据库 -> 写到PHP文件 -> 程序中include
来吧。开始文本缓存学习
*/
function cache_all() //生成所有缓存的总操作函数
 
{
cache_table(); //生成所有的数据库表名,表名是根据数据库里面当前的表明而生成。请看这个函数的详细分析
 
require_once PHPCMS_CACHEDIR.'table.php'; //包含表常量
 
cache_common();
cache_member_group();
$modules = cache_module();
$channelids = cache_channel(0);
$keyids = array_merge($modules, $channelids);
foreach($keyids as $keyid)
{
$catids = cache_categorys($keyid);
if(is_array($catids))
{
foreach($catids as $catid)
{
cache_category($catid);
}
}
}
cache_type(0);
return TRUE;
}
function cache_common()
{
global $db;
$query = $db->query("SELECT module,name,iscore,iscopy,isshare,moduledir,moduledomain FROM ".TABLE_MODULE." WHERE disabled=0"); //查询所有能用的模块
 
while($r = $db->fetch_array($query))
{
$r['linkurl'] = '';
if($r['module'] != 'phpcms' && $r['iscopy'] == 0) $r['linkurl'] = linkurl($r['moduledomain'] ? dir_path($r['moduledomain']) : $r['moduledir'].'/');
//如果模块存在目录的就取它目录地址
 
unset($r['moduledomain']);
$key = $r['module'];
$data[$key] = $r;
}
$CACHE['module'] = $data; //存到缓存数组,等一下一起把 $CACHE 数组写到文本里去
 
$data = array();
$query = $db->query("SELECT channelid,module,channelname,channeldir,channeldomain,channelpic,introduce,style,islink,linkurl,cat_html_urlruleid,item_html_urlruleid,special_html_urlruleid,cat_php_urlruleid,item_php_urlruleid,special_php_urlruleid FROM ".TABLE_CHANNEL." WHERE disabled=0 ORDER by listorder"); //罗列能用的频道列表
 
while($r = $db->fetch_array($query))
{
$r['linkurl'] = linkurl($r['linkurl']);
$key = $r['channelid'];
$data[$key] = $r;
}
$CACHE['channel'] = $data; //存到缓存数组
 
$data = array();
$r = $db->get_one("SELECT setting FROM ".TABLE_MODULE." WHERE module='phpcms'");
$CACHE['phpcms'] = unserialize($r['setting']);
//查询 phpcms这个模块的设置信息,大家可以看下数据库这个表内容。setting 字段里面的信息是经过serialize 函数窜行化的。
 
//所以取出的内容要unserialize 反窜行.我是挺喜欢使用serialize 函数的。他可以实现把一个数组存到数据库或把一个对象存到数据库。或是拿来GET传递都行。
 
//太强了。大家可以试用下。可能你项目某个地方需要用到哦。
 
$fields = array();
$result = $db->query("SELECT * FROM ".TABLE_FIELD." ORDER BY fieldid"); //下栽模块的信息,请自己看下这个表的数据就明白
 
while($r = $db->fetch_array($result))
{
$tablename = $r['tablename'];
$fields[$tablename] .= ','.$r['name'];
}
$CACHE['field'] = $fields;
cache_write('common.php', $CACHE); //开始把$CACHE 数组写到 common.php 这个文本缓存里。大家可以自己去打开这个文件看下内容。一切了然
 
return $CACHE;
}
function cache_update($action='')//更新文本缓存。最好在后台操作使用。因为PHP的文件flock 文件锁在某些平台使用不是很好。会出现多用户同写一个文件从而破坏缓存文件
 
{
global $db;
$data=array();
switch($action)
{
case 'keylink';
$query=$db->query("SELECT linktext,linkurl FROM ".TABLE_KEYLINK." where passed=1");
while($r=$db->fetch_array($query)){
$data[]=$r;
}
break;
case 'reword';
$query = $db->query("SELECT word,replacement FROM ".TABLE_REWORD." where passed=1");
while($r = $db->fetch_array($query))
{
$data[]=$r;
}
break;
default:
$actions = array('keylink','reword');
array_map('cache_update', $actions);
return TRUE;
}
cache_write('cache_'.$action.'.php', $data);
return $data;
}
function cache_table()
{
global $db,$CONFIG;
$query = $db->query("SHOW TABLES FROM `".$CONFIG['dbname']."`");
/**
显示数据库里面的所有表名
*/
while($r = $db->fetch_row($query))
{
$table = $r[0];
if(preg_match("/^".$CONFIG['tablepre']."/i", $table)) //寻找表前缀等于 $CONFIG['tablepre'] (在config.inc.php里设置) @@表前缀还有这个作用 嘿嘿
 
{
$tablename = str_replace($CONFIG['tablepre'], 'table_', $table);
$data[$tablename] = $table; // $data['table_xx'] = xx; 形式 只能意会下了
 
}
}
$db->free_result($query); //$db->free_result() 这个类方法其实是掉用了函数:mysql_free_result() 函数 主要是为了清除数据库大的查询而占用的内存。还是有必要的哦
 
if(!is_dir(PHPCMS_CACHEDIR)) // 常量 PHPCMS_CACHEDIR 在 common.inc.php 里面定义的。大家不记得了去看看吧。是存放phpcms 缓存目录的路径,这里意思是:如果缓存目录不存在
 
{
dir_create(PHPCMS_CACHEDIR); //如果缓存目录不存在那么就创建
 
dir_create($CONFIG['templatescachedir']); //创建编译后的PHP模板目录,有关phpcms模板引擎编写。在下一章合适就开讲
 
/**
dir_create() 函数为创建 目录函数。PHPCMS自己封装的,刚看了下。phpcms 挺强。这个函数还可以通过ftp 来创建目录。这样就可以解决一些 开启了安全模式下的服务器对于创建目录等出现的问题
因为涉及到PHP FTP 知识。所以打算讲解到下面再说。
*/
}
cache_write('table.php', $data , 'constant'); //很多朋友说找不到phpcms 表常量在那里定义的。就是在这里。
 
>";
//这个太关键了。因为我们把数据库的信息写到文本上去的时候。是以符合PHP语法的格式写进去的。为什么呢?@@ 十分废话,因为如果不是以PHP格式写到文件里面去
那么这个PHP文件怎么能给我们include 进程序运行调用呢? 呵呵。 知道这一点就真的明白文本缓存的实现了。忒简单。 这里使用了个小技巧:使用了 var_export() 函数
这个函数会返回一个变量的字符窜形式。这个函数太有帮助了。如果没有这个函数,我们还要自己想办法实现呢。自己写一次文本缓存就明白了。会碰到这个问题的。 '\n' 这个是
文本文件的换行。初学者 别把
和 '\n' 搞混罗。 一个是html 的 一个是文本文件的
}
elseif($type == 'constant') //以内容形式
{
$data='';
foreach($string as $key => $value) $data .= "define('".strtoupper($key)."','".addslashes($value)."');\n";
$string = "";
如果以内容形式的话。就不是写数组到文本里面了。而是把内容都定义成常量。
}
}
$strlen = file_put_contents(PHPCMS_CACHEDIR.$file, $string);//file_put_contents()函数 是PHP5才支持的 效率最好。建议使用
chmod(PHPCMS_CACHEDIR.$file, 0777); 设置目录 为可读可写可执行
return $strlen; //返回写到文本的字节数
}
 
再说多一个读 缓存文件的操作函数 :上菜
function cache_read($file, $mode = 'i')
{
$cachefile = PHPCMS_CACHEDIR.$file;
if(!file_exists($cachefile)) return array();
return $mode == 'i' ? include $cachefile : file_get_contents($cachefile);
}
读缓存其实就是 include php缓存文件。 讲完走人
*/
return $data;
}
/**
phpcms 的所有数据库表名 都用根据数据库当前的表名来用常量来进行定义。我认为这样设计不是很好。不够灵活:比如如果我们更改数据库的一个表名的话。
那么会出现找不到表的错误信息。而且想要修复还很麻烦。就是说不能随便更改表名了。不推荐大家这样写。我们可以把表名都定义在一个PHP文件里面。这样我们以后
要改某个表名,就很方便了。
*/
function cache_module($module = '')
{
global $db;
if($module)
{
$r = $db->get_one("SELECT setting,module,name,iscopy,moduledir,moduledomain FROM ".TABLE_MODULE." WHERE module='$module'"); //模块具体信息
if($r['setting'])
{
$setting = unserialize($r['setting']); //讲过了反窜行。因为里面信息是窜行化后再存到数据库的
}
$setting['name'] = $r['name'];
$setting['moduledir'] = $r['moduledir'];
$setting['moduledomain'] = $r['moduledomain'];
$setting['linkurl'] = '';
if($r['module'] != 'phpcms' && $r['iscopy'] == 0)
{
$setting['linkurl'] = linkurl($r['moduledomain'] ? dir_path($r['moduledomain']) : $r['moduledir'].'/');
cache_categorys($module);
}
unset($r['moduledomain']);
cache_write($module.'_setting.php', $setting);
return $setting;
}
else
{
$query = $db->query("SELECT module FROM ".TABLE_MODULE." WHERE disabled=0 ORDER by moduleid");
while($r = $db->fetch_array($query))
{
cache_module($r['module']);
$modules[] = $r['module'];
}
return $modules;
}
}
function cache_channel($channelid=0)
{
global $db;
if($channelid)
{
$data = $db->get_one("SELECT * FROM ".TABLE_CHANNEL." WHERE channelid=$channelid");
if($data && !$data['islink'])
{
if($data['setting'])
{
$setting = unserialize($data['setting']);
unset($data['setting']);
$data = is_array($setting) ? array_merge($data, $setting) : $data;
}
$data['linkurl'] = linkurl($data['linkurl']);
cache_write('channel_'.$channelid.'.php', $data);
cache_categorys($channelid);
return $data;
}
}
else
{
$query = $db->query("SELECT channelid FROM ".TABLE_CHANNEL." WHERE islink=0 AND disabled=0 ORDER by channelid");
while($r = $db->fetch_array($query))
{
cache_channel($r['channelid']);
$channelids[] = $r['channelid'];
}
return $channelids;
}
}
function cache_categorys($keyid)
{
global $db,$PHPCMS,$CHANNEL;
$urlpre = '';
if(is_numeric($keyid))
{
$keyid = intval($keyid);
$module = $CHANNEL[$keyid]['module'];
$sql = " channelid=$keyid ";
}
else
{
$sql = " module='$keyid' ";
}
$catids = $data = array();
$query = $db->query("SELECT module,channelid,catid,catname,style,introduce,catpic,islink,catdir,linkurl,parentid,arrparentid,parentdir,child,arrchildid,items,itemordertype,itemtarget,ismenu,islist,ishtml,htmldir,prefix,urlruleid,item_prefix,item_html_urlruleid,item_php_urlruleid FROM ".TABLE_CATEGORY." WHERE $sql ORDER by listorder,catid");
while($r = $db->fetch_array($query))
{
$r['linkurl'] = str_replace($PHPCMS['index'].'.'.$PHPCMS['fileext'], '', $r['linkurl']);
$r['linkurl'] = $urlpre ? preg_replace("|^".$urlpre."|", '', $r['linkurl']) : linkurl($r['linkurl']);
$catid = $r['catid'];
$data[$catid] = $r;
$catids[] = $catid;
}
if($data) cache_write('categorys_'.$keyid.'.php', $data); //写缓存罗。
return $catids;
}
function cache_category($catid)
{
global $db,$PHPCMS;
if(!$catid) return FALSE;
$data = $db->get_one("SELECT * FROM ".TABLE_CATEGORY." WHERE catid=$catid");
$setting = unserialize($data['setting']);
unset($data['setting']);
$data = is_array($setting) ? array_merge($data, $setting) : $data;
$data['linkurl'] = linkurl(str_replace($PHPCMS['index'].'.'.$PHPCMS['fileext'], '', $data['linkurl']));
cache_write('category_'.$catid.'.php', $data);
return $data;
}
function cache_type($keyid=0)
{
global $db;
if($keyid)
{
$result = $db->query("SELECT * FROM ".TABLE_TYPE." WHERE keyid='$keyid'");
$data = array();
while($r = $db->fetch_array($result))
{
$r['introduce'] = $r['introduce']? $r['introduce']:' ';
$data[$r['typeid']] = $r;
}
if($data)
{
cache_write('type_'.$keyid.'.php', $data);
}
return $data;
}
else
{
$modules = array();
$query = $db->query("SELECT module FROM ".TABLE_MODULE." WHERE disabled=0 ORDER by moduleid");
while($r = $db->fetch_array($query))
{
$modules[] = $r['module'];
}
$channelids = array();
$query = $db->query("SELECT channelid FROM ".TABLE_CHANNEL." WHERE islink=0 AND disabled=0 ORDER by channelid");
while($r = $db->fetch_array($query))
{
$channelids[] = $r['channelid'];
}
$modulechannels = array_merge($modules,$channelids);
foreach($modulechannels as $m)
{
$result = $db->query("SELECT * FROM ".TABLE_TYPE." WHERE keyid='$m'");
$TYPE = array();
while($r = $db->fetch_array($result))
{
$r['introduce'] = $r['introduce']? $r['introduce']:' ';
$TYPE[$r['typeid']] = $r;
}
cache_write('type_'.$m.'.php',$TYPE);
}
return $modulechannels;
}
}
function cache_member_group()
{
global $db;
$query = $db->query("SELECT * FROM ".TABLE_MEMBER_GROUP." ORDER BY groupid"); //用户组信息
while($r = $db->fetch_array($query))
{
$groupid = $r['groupid'];
cache_write('member_group_'.$groupid.'.php', $r);
$data[$groupid] = $r;
}
cache_write('member_group.php', $data); //明白了吧。写缓存罗
return $data;
}
function cache_banip()
{
global $db,$PHP_TIME;
$result = $db->query("SELECT ip,overtime FROM ".TABLE_BANIP." WHERE ifban=1 and overtime>=$PHP_TIME order by id desc");
while($r = $db->fetch_array($result))
{
$data[] = array('ip'=>$r['ip'],'overtime'=>$r['overtime']);
}
$db->free_result($result);
cache_write('banip.php', $data);
return $data;
}
?>

来源:http://www.phpchina.com/bbs/viewthread.php?tid=47107&extra=&page=11


评论

输入后可按 Ctrl+Enter 提交评论.