1、 网站建设 作者:边城浪子 discuz!源码分析 源代码分析[1] 第一个文件当然是分析./include/common.inc.php这个文件,这个是Discuz的核心中的核心,基本上每次操作都include到了这个文件,下面就分七段来分析这个文件: Section One: 以下内容为程序代码: //定义PHP一些环境 error_reporting(0); set_magic_
2、quotes_runtime(0); //设置Discuz开始的时间 $mtime = explode(' ', microtime()); $discuz_starttime = $mtime[1] + $mtime[0]; //定义一些常量 define('SYS_DEBUG', FALSE); define('IN_DISCUZ', TRUE); define('DISCUZ_ROOT', substr(dirname(__FILE__), 0, -7)); //获得绝对目录 //通用性 if(PHP_VERSION < '4.1.0') {
3、 $_GET = &$HTTP_GET_VARS; $_POST = &$HTTP_POST_VARS; $_COOKIE = &$HTTP_COOKIE_VARS; $_SERVER = &$HTTP_SERVER_VARS; $_ENV = &$HTTP_ENV_VARS; $_FILES = &$HTTP_POST_FILES; } 这一段基本上就是设置一下错误报告,把magic_quotes这个sick家伙给关了,然后定一个开始的时间,这样我们在论坛底部看到的Process Ti
4、me就是通过这个开始的时间和一个结束的时间的差来计算的,然后定义一个IN_DISCUZ为真,这个IN_DISCUZ常量的作用就是在其他inc这样的包含文件中防止被非法引用,一旦没有这个常量的话就出现Access Denied这样的字样然后退出。然后获得Discuz运行的绝对目录。接下来是判断PHP 的版本是4.1 以下还是以上,因为PHP以4.1为一个分界线,在4.1以下以$HTTP_GET_VARS[‘xx’]这样的方式来得到get过来的值,而以后用$_GET来得到get过来的值,这样做的目的是为了无论是什么样的PHP版本,都能用$_GET这样的方式得到,有通用性~! 以下内容为程序代
5、码: require_once DISCUZ_ROOT.'./include/global.func.php'; 把include/global.inc.php引用进来,这个文件是Discuz的核心函数文件,包含了Discuz用到的很多通用的函数,可以说它就是一个大的通用函数库。 以下内容为程序代码: define('ISROBOT', getrobot()); if(defined('NOROBOT') && ISROBOT) { exit(header("HTTP/1.1 403 Forbidden")); } 这里是定义一个ISROBOT常量,看看浏览
6、者是什么东东,比方说如果浏览者是一个robot那么就直接来一个 403 Forbidden了…… 以下内容为程序代码: define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc()); isset($_REQUEST['GLOBALS']) && exit('Access Error'); foreach(array('_COOKIE', '_POST', '_GET') as $_request) { foreach($$_request as $_key => $_value) {
7、 $_key{0} != '_' && $$_key = daddslashes($_value); } } (!MAGIC_QUOTES_GPC) && $_FILES = daddslashes($_FILES); 此处是过滤提交的变量用的,提高安全性的用法。。 以下内容为程序代码: $charset = $dbcharset = $forumfounders = $metakeywords = $extrahead = ''; $plugins = $hooks = $admincp = array(); require_once DISCUZ_RO
8、OT.'./config.inc.php'; $_DCOOKIE = $_DSESSION = $_DCACHE = $_DPLUGIN = $advlist = array(); $prelength = strlen($cookiepre); foreach($_COOKIE as $key => $val) { if(substr($key, 0, $prelength) == $cookiepre) { $_DCOOKIE[(substr($key, $prelength))] = MAGIC_QUOTES_G
9、PC ? $val : daddslashes($val); } } 初始化一些变量,然后引用config.inc.php这个配置文件,这样开始初始化程序的一些东西了。接下来的一个循环把$_COOKIE中的东西取出来存到$_DCOOKIE这个数组中。注意:在登陆的时候Discuz会把登陆信息存放到$_COOKIE中去。在下面一段会有取出的代码。 以下内容为程序代码: unset($prelength, $_request, $_key, $_value); $timestamp = time(); if($attackevasive) {
10、 require_once DISCUZ_ROOT.'./include/security.inc.php'; } 这一部分代码是提高安全用的,防一些非法的入侵,include/security.inc.php文件中就是这样一些检查。 以下内容为程序代码: require_once DISCUZ_ROOT.'./include/db_'.$database.'.class.php'; $PHP_SELF = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']; $SCRIPT_FILEN
11、AME = str_replace('\\\\', '/', (isset($_SERVER['PATH_TRANSLATED']) ? $_SERVER['PATH_TRANSLATED'] : $_SERVER['SCRIPT_FILENAME'])); $boardurl = 'http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api|archiver|wap)?\/*$/i", '', substr($PHP_SELF, 0, strrpos($PHP_SELF, '/'))).'/'; if(getenv('HTTP_CLIE
12、NT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) { $onlineip = getenv('HTTP_CLIENT_IP'); } elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) { $onlineip = getenv('HTTP_X_FORWARDED_FOR'); } elseif(getenv('REMOTE_ADDR') &&
13、strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) { $onlineip = getenv('REMOTE_ADDR'); } elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) { $onlineip = $_SERVER['REMOTE_ADDR']; } 第一行是把include/db_mysql.class.php引用进来
14、这个文件是一个数据库的类。我觉得是不是放在这里太早了点? 然后接下的作用就是得到自身的名称$PHP_SELF,自身的文件名字$SCRIPT_FILENAME,论坛的地址$boardurl,得到浏览者的一些信息,比方说ip地址,浏览器类型等等。 以下内容为程序代码: preg_match("/[\d\.]{7,15}/", $onlineip, $onlineipmatches); $onlineip = $onlineipmatches[0] ? $onlineipmatches[0] : 'unknown'; unset($onlineipmatches); 看看ip是不是点分
15、段,7-15个数字之间,用到了一个正则表达式, 以下内容为程序代码: $cachelost = (@include DISCUZ_ROOT.'./forumdata/cache/cache_settings.php') ? '' : 'settings'; @extract($_DCACHE['settings']); 这一段是获得./forumdata/cache/cache_settings.php(即缓存下的设置数组,并展开,方面以后的写法 以下内容为程序代码: if($gzipcompress && function_exists('ob_gzhandler') && CU
16、RSCRIPT != 'wap') { ob_start('ob_gzhandler'); } else { $gzipcompress = 0; ob_start(); } 检查gzip是不是打开了,打开就用ob_gzhandler,没有就用ob_start。 以下内容为程序代码: if(!empty($loadctrl) && substr(PHP_OS, 0, 3) != 'WIN') { if($fp = @fopen('/proc/loadavg', 'r')) {
17、 list($loadaverage) = explode(' ', fread($fp, 6)); fclose($fp); if($loadaverage > $loadctrl) { header("HTTP/1.0 503 Service Unavailable"); include DISCUZ_ROOT.'./include/serverbusy.htm';
18、 exit(); } } } 看到了熟悉的service unavailable了吧?呵呵,平衡负载用的。 以下内容为程序代码: if(defined('CURSCRIPT') && in_array(CURSCRIPT, array('index', 'forumdisplay', 'viewthread', 'post', 'blog', 'pm', 'topicadmin', 'register', 'archiver'))) { $cac
19、helost .= (@include DISCUZ_ROOT.'./forumdata/cache/cache_'.CURSCRIPT.'.php') ? '' : ' '.CURSCRIPT; } 看看是不是index, forumdisplay, viewthread这些文件是不是缓存了,有的话把它装到$cachelost这个变量中。 Section Four: 以下内容为程序代码: $db = new dbstuff; $db->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect); $dbhost = $dbuser
20、 $dbpw = $dbname = $pconnect = NULL; 好了,这里是初始化一个dbstull类的实例,也就是说前面的include/db_mysql.class.php在这里用上了,所以我觉得那个require_once早了点,放到这里的前面最合适了~!J下面就是连上mysql了,然后把几个配置的变量NULL掉,安全性考虑得真多,武装到牙齿! 以下内容为程序代码: $sid = daddslashes(($transsidstatus || (defined('CURSCRIPT') && CURSCRIPT == 'wap'))&& (isset($_GET['s
21、id']) || isset($_POST['sid'])) ? (isset($_GET['sid']) ? $_GET['sid'] : $_POST['sid']) : (isset($_DCOOKIE['sid']) ? $_DCOOKIE['sid'] : '')); 看看是不是后台设置了通过sid传输的那个东东,还有是不是通过wap访问的,还有是不是有sid这个东东在$_GET或$_POST这两个的任何一个中,以上结论都成立的话从GET中获得sid,不成立的话从$_DCOOKIE中获得。 以下内容为程序代码: $discuz_auth_key
22、 = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']); 设置一个$discuz_auth_key,md5加密。。 以下内容为程序代码: if(isset($_DCOOKIE['auth']) && $_DCOOKIE['auth']) { list($discuz_pw, $discuz_secques, $discuz_uid) = daddslashes(explode("\t", authcode($_DCOOKIE['auth'], 'DECODE')), 1); i
23、f(!is_numeric($discuz_uid) || !$discuz_uid) { clearcookies(); } } else { list($discuz_pw, $discuz_secques, $discuz_uid) = array('', '', 0); } 这一段是用来检查是不是$_DCOOKIE[‘auth’]存在,如果有的话就把其中存放的东西分别给$discuz_pw, $discuz_secques, $discuz_uid这三个变量,分别对应密码,提示问题和uid。 Section
24、Five: 以下内容为程序代码: $newpm = $newpmexists = $sessionexists = $seccode = $bloguid = 0;//初始化变量 $membertablefields = 'm.uid AS discuz_uid, m.username AS discuz_user, m.password AS discuz_pw, m.secques AS discuz_secques, m.adminid, m.groupid, m.groupexpiry, m.extgroupids, m.email, m.timeoffset
25、 m.tpp, m.ppp, m.posts, m.digestposts, m.oltime, m.pageviews, m.credits, m.extcredits1, m.extcredits2, m.extcredits3, m.extcredits4, m.extcredits5, m.extcredits6, m.extcredits7, m.extcredits8, m.timeformat, m.dateformat, m.pmsound, m.sigstatus, m.invisible, m.lastvisit, m.la
26、stactivity, m.lastpost, m.newpm, m.accessmasks, m.xspacestatus, m.editormode, m.customshow'; if($sid) { if($discuz_uid) { $query = $db->query("SELECT s.sid, s.styleid, s.groupid='6' AS ipbanned, s.pageviews AS spageviews, s.lastolupdate, s.seccode, $membertablefields
27、 FROM {$tablepre}sessions s, {$tablepre}members m WHERE m.uid=s.uid AND s.sid='$sid' AND CONCAT_WS('.',s.ip1,s.ip2,s.ip3,s.ip4)='$onlineip' AND m.uid='$discuz_uid' AND m.password='$discuz_pw' AND m.secques='$discuz_secques'");
28、 } else { $query = $db->query("SELECT sid, uid AS sessionuid, groupid, groupid='6' AS ipbanned, pageviews AS spageviews, styleid, lastolupdate, seccode FROM {$tablepre}sessions WHERE sid='$sid' AND CONCAT_WS('.',ip1,ip2,ip3,ip4)='$onlineip'"); }
29、 if($_DSESSION = $db->fetch_array($query)) { $sessionexists = 1; if(!empty($_DSESSION['sessionuid'])) { $query = $db->query("SELECT $membertablefields FROM {$tablepre}members m WHERE uid='$_DSESSION[sessi
30、onuid]'"); $_DSESSION = array_merge($_DSESSION, $db->fetch_array($query)); } } else { $query = $db->query("SELECT sid, groupid, groupid='6' AS ipbanned, pageviews AS spageviews, styleid, lastolupdate, seccode
31、 FROM {$tablepre}sessions WHERE sid='$sid' AND CONCAT_WS('.',ip1,ip2,ip3,ip4)='$onlineip'"); if($_DSESSION = $db->fetch_array($query)) { clearcookies(); $sessionexists = 1; } } } 这一段是有蛮长的,不过看着长不代表它就难,第一行是初始
32、化变量用的(无论何时用变量都要考虑初始化,要不然安全性不值得一提,一个get就完了) 接下来是判断是不是有sid,有的话就从cdb_session表中取来,然后连接一下cdb_members表取出一些更具体的东西,具体是哪些东西?在$membertablefields这个变量里面已经全面写出来了,对应数据库看吧,不看的话用英语猜猜得出的。。。在这里Discuz标记了一个sessionexist变量,表示这个会员是在线的。 以下内容为程序代码: if(!$sessionexists) { if($discuz_uid) { $query
33、 $db->query("SELECT $membertablefields FROM {$tablepre}members m WHERE m.uid='$discuz_uid' AND m.password='$discuz_pw' AND m.secques='$discuz_secques'"); if(!($_DSESSION = $db->fetch_array($query))) { clearcookies();
34、 } } 要是不存在sid,不存在discuz_uid,那就肯定没有登陆了,清掉cookie,要是有$discuz_uid的话,还是从members表中取出信息存放到$_DSESSION数组中 以下内容为程序代码: if(ipbanned($onlineip)) $_DSESSION['ipbanned'] = 1; $_DSESSION['sid'] = random(6); $_DSESSION['seccode'] = random(6, 1); } $_DSESSION['dateformat'] = emp
35、ty($_DSESSION['dateformat']) ? $_DCACHE['settings']['dateformat'] : $_DSESSION['dateformat']; $_DSESSION['timeformat'] = empty($_DSESSION['timeformat']) ? $_DCACHE['settings']['timeformat'] : ($_DSESSION['timeformat'] == 1 ? 'h:i A' : 'H:i'); $_DSESSION['timeoffset'] = isset($_DSESSION['timeoffset
36、']) && $_DSESSION['timeoffset'] != 9999 ? $_DSESSION['timeoffset'] : $_DCACHE['settings']['timeoffset']; 这个是判断ip是不是在被阻止的list里,是的话就标记一下,用$_DSESSION[‘ipbanned’]标记的。再把一个随机的sid和seccode写到$_DSESSION数组。然后接下来是把日期,时间,时差写入$_DSESSION这个变量。 以下内容为程序代码: $membertablefields = ''; @extract($_DSESSION); $lastv
37、isit = empty($lastvisit) ? $timestamp - 86400 : $lastvisit; $timenow = array('time' => gmdate("$dateformat $timeformat", $timestamp + 3600 * $timeoffset), 'offset' => ($timeoffset >= 0 ? ($timeoffset == 0 ? '' : '+'.$timeoffset) : $timeoffset)); if(PHP_VERSION > '5.1') { @date_de
38、fault_timezone_set('Etc/GMT'.($timeoffset > 0 ? '-' : '+').(abs($timeoffset))); } 又见变量初始化,然后是把$_DESSION给展开,这样方便多了。接下来判断是不是有上次访问的时间,有的话就没事,没有的话就减去24小时。 接下来给一个现在的时间,这里有一个时间的问题,所以把时间加上时差乘上3600秒就得到当前时间了。 PHP 5 能处理时差了,所以Discuz在这里也设置了一下,想得真全面!! Section Six: [quote] 以下内容为程序代码: $accessadd1 = $acces
39、sadd2 = $modadd1 = $modadd2 = ''; if(empty($discuz_uid) || empty($discuz_user)) { $discuz_user = $extgroupids = ''; $discuz_uid = $adminid = $posts = $digestposts = $pageviews = $oltime = $invisible = $credits = $extcredits1 = $extcredits2 = $extcredits3 = $extcredi
40、ts4 = $extcredits5 = $extcredits6 = $extcredits7 = $extcredits8 = 0; $groupid = empty($groupid) || $groupid != 6 ? 7 : 6; } else { $discuz_userss = $discuz_user; $discuz_user = addslashes($discuz_user); if($accessmasks) { $access
41、add1 = ', a.allowview, a.allowpost, a.allowreply, a.allowgetattach, a.allowpostattach'; $accessadd2 = "LEFT JOIN {$tablepre}access a ON a.uid='$discuz_uid' AND a.fid=f.fid"; } if($adminid == 3) { $modadd1 = ', m.uid AS ismoderator';
42、 $modadd2 = "LEFT JOIN {$tablepre}moderators m ON m.uid='$discuz_uid' AND m.fid=f.fid"; } } if($errorreport == 2 || ($errorreport == 1 && $adminid > 0)) { error_reporting(E_ERROR | E_WARNING | E_PARSE); } 首先初始化变量,然后看看$discuz_user$和$discuz_user这两个变量是不是存在,不存在的话一系列的变量全部置0(没登陆当然不存
43、在版主权限管理员什么的),存在的话就来两个赋值,这就是为什么在Discuz UserGuide里面说$discuz_userss是没有过滤的,而$discuz_user是过滤掉的,能直接进行数据库操作的。 然后看看怎么样设置PHP 的出错级别,注意如果你的管理员的话,Discuz给的是一个更高的错误显示级别! 以下内容为程序代码: define('FORMHASH', formhash()); $statstatus && require_once DISCUZ_ROOT.'./include/counter.inc.php'; $extra = isset($extra)
44、 && @preg_match("/^[&=;a-z0-9]+$/i", $extra) ? $extra : ''; $tpp = intval(empty($_DSESSION['tpp']) ? $topicperpage : $_DSESSION['tpp']); $ppp = intval(empty($_DSESSION['ppp']) ? $postperpage : $_DSESSION['ppp']); 这一段定义一个form hash,这个是通过global.func.php这个文件中的formhash()函数来的。然后看是不是要等到统计信息,要的话就引用./incl
45、ude/counter.inc.php这个文件,不要的话当然就不引用了。 $extra这个东东不知道做嘛用的。。。。 $tpp-threads per page每页的帖子数 $ppp-posts per page每页的回复数 以下内容为程序代码: $rsshead = $navtitle = $navigation = ''; $_DSESSION['groupid'] = $groupid = empty($ipbanned) ? (empty($groupid) ? 7 : intval($groupid)) : 6; if(!@include DISCUZ_ROOT
46、'./forumdata/cache/usergroup_'.$groupid.'.php') { $query = $db->query("SELECT type FROM {$tablepre}usergroups WHERE groupid='$groupid'"); $grouptype = $db->result($query, 0); if(!empty($grouptype)) { $cachelost .= ' usergroup_'.$groupid; } else {
47、 $grouptype = 'member'; } } 用来得到用户组的,首先看看是不是缓存中有,没有的话就访问数据库了,有的话当然就用缓存的。。 以下内容为程序代码: if($passport_status && ($passport_status != 'shopex' || !$passport_shopex)) { $passport_forward = rawurlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); $link
48、login = $passport_url.$passport_login_url.(strpos($passport_login_url, '?') === FALSE ? '?' : '&').'forward='.$passport_forward; $link_logout = $passport_url.$passport_logout_url.(strpos($passport_logout_url, '?') === FALSE ? '?' : '&').'forward='.$passport_forward; $link_register =
49、 $passport_url.$passport_register_url.(strpos($passport_register_url, '?') === FALSE ? '?' : '&').'forward='.$passport_forward; } else { $link_login = 'logging.php?action=login'; $link_logout = 'logging.php?action=logout&formhash='.FORMHASH; $link_register = 'register.php'; } Discuz通行证用的。主要就是得到一些应用程序的地址。 以下内容为程序代码: if($discuz_uid && $_DSESSION) { if(!empty($groupexpiry) && $groupexpiry < $timestamp && (!defined('CURSCRIPT') || (CURSCRIPT != 'wap' && CURSCRIPT != 'member'))) { dheader("Location: {$boardurl}member.php?ac






