discuz源代码分析

时间:2022-09-09 14:24:44
第一个文件当然是分析./include/common.inc.php这个文件,这个是Discuz的核心中的核心,基本上每次操作都include到了这个文件,下面就分七段来分析这个文件:

Section One:

QUOTE:

CODE:

//定义PHP一些环境
error_reporting(0);
set_magic_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') {
        $_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 Time就是通过这个开始的时间和一个结束的时间的差来计算的,然后定义一个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这样的方式得到,有通用性~!

QUOTE:

CODE:

require_once DISCUZ_ROOT.'./include/global.func.php';把include/global.inc.php引用进来,这个文件是Discuz的核心函数文件,包含了Discuz用到的很多通用的函数,可以说它就是一个大的通用函数库。

CODE:

define('ISROBOT', getrobot());
if(defined('NOROBOT') && ISROBOT) {
        exit(header("HTTP/1.1 403 Forbidden"));
}
这里是定义一个ISROBOT常量,看看浏览者是什么东东,比方说如果浏览者是一个robot那么就直接来一个 403 Forbidden了……

CODE:

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) {
                $_key{0} != '_' && $$_key = daddslashes($_value);
        }
}
(!MAGIC_QUOTES_GPC) && $_FILES = daddslashes($_FILES);
此处是过滤提交的变量用的,提高安全性的用法。。

CODE:

$charset = $dbcharset = $forumfounders = $metakeywords = $extrahead = '';
$plugins = $hooks = $admincp = array();

require_once DISCUZ_ROOT.'./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_GPC ? $val : daddslashes($val);
        }
}
初 始化一些变量,然后引用config.inc.php这个配置文件,这样开始初始化程序的一些东西了。接下来的一个循环把$_COOKIE中的东西取出来 存到$_DCOOKIE这个数组中。注意:在登陆的时候Discuz会把登陆信息存放到$_COOKIE中去。在下面一段会有取出的代码。

CODE:

unset($prelength, $_request, $_key, $_value);
$timestamp = time();

if($attackevasive) {
        require_once DISCUZ_ROOT.'./include/security.inc.php';
}
这一部分代码是提高安全用的,防一些非法的入侵,include/security.inc.php文件中就是这样一些检查。

CODE:

require_once DISCUZ_ROOT.'./include/db_'.$database.'.class.php';


$PHP_SELF = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME'];
$SCRIPT_FILENAME = 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_CLIENT_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') && 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引用进来,这个文件是一个数据库的类。我觉得是不是放在这里太早了点?
然后接下的作用就是得到自身的名称$PHP_SELF,自身的文件名字$SCRIPT_FILENAME,论坛的地址$boardurl,得到浏览者的一些信息,比方说ip地址,浏览器类型等等。

QUOTE:

CODE:

preg_match("/[/d/.]{7,15}/", $onlineip, $onlineipmatches);
$onlineip = $onlineipmatches[0] ? $onlineipmatches[0] : 'unknown';
unset($onlineipmatches);
看看ip是不是点分段,7-15个 数字之间,用到了一个正则表达式,

CODE:

$cachelost = (@include DISCUZ_ROOT.'./forumdata/cache/cache_settings.php') ? '' : 'settings';
@extract($_DCACHE['settings']);
这一段是获得./forumdata/cache/cache_settings.php(即缓存下的设置数组,并展开,方面以后的写法

CODE:

if($gzipcompress && function_exists('ob_gzhandler') && CURSCRIPT != 'wap') {
        ob_start('ob_gzhandler');
} else {
        $gzipcompress = 0;
        ob_start();
}
检查gzip是不是打开了,打开就用ob_gzhandler,没有就用ob_start。

CODE:

if(!empty($loadctrl) && substr(PHP_OS, 0, 3) != 'WIN') {
        if($fp = @fopen('/proc/loadavg', 'r')) {
                list($loadaverage) = explode(' ', fread($fp, 6));
                fclose($fp);
                if($loadaverage > $loadctrl) {
                        header("HTTP/1.0 503 Service Unavailable");
                        include DISCUZ_ROOT.'./include/serverbusy.htm';
                        exit();
                }
        }
}
看到了熟悉的service unavailable了吧?呵呵,平衡负载用的。

CODE:

if(defined('CURSCRIPT') && in_array(CURSCRIPT, array('index', 'forumdisplay', 'viewthread', 'post', 'blog', 'pm', 'topicadmin', 'register', 'archiver'))) {
        $cachelost .= (@include DISCUZ_ROOT.'./forumdata/cache/cache_'.CURSCRIPT.'.php') ? '' : ' '.CURSCRIPT;
}
看看是不是index, forumdisplay, viewthread这些文件是不是缓存了,有的话把它装到$cachelost这个变量中。

TAG:

discuz源代码分析  笨才  发布于2007-05-28 11:19:15
Section Four:

QUOTE:

CODE:

$db = new dbstuff;
$db->connect($dbhost, $dbuser, $dbpw, $dbname, $pconnect);
$dbhost = $dbuser = $dbpw = $dbname = $pconnect = NULL;
好 了,这里是初始化一个dbstull类的实例,也就是说前面的include/db_mysql.class.php在这里用上了,所以我觉得那个 require_once早了点,放到这里的前面最合适了~!J下面就是连上mysql了,然后把几个配置的变量NULL掉,安全性考虑得真多,武装到牙 齿!

CODE:

$sid = daddslashes(($transsidstatus || (defined('CURSCRIPT') && CURSCRIPT == 'wap'))&& (isset($_GET['sid']) || isset($_POST['sid'])) ?
        (isset($_GET['sid']) ? $_GET['sid'] : $_POST['sid']) :
        (isset($_DCOOKIE['sid']) ? $_DCOOKIE['sid'] : ''));
看看是不是后台设置了通过sid传输的那个东东,还有是不是通过wap访问的,还有是不是有sid这个东东在$_GET或$_POST这两个的任何一个中,以上结论都成立的话从GET中获得sid,不成立的话从$_DCOOKIE中获得。

CODE:

$discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']);设置一个$discuz_auth_key,md5加密。。

CODE:

if(isset($_DCOOKIE['auth']) && $_DCOOKIE['auth']) {
        list($discuz_pw, $discuz_secques, $discuz_uid) = daddslashes(explode("/t", authcode($_DCOOKIE['auth'], 'DECODE')), 1);
        if(!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 Five:

QUOTE:

CODE:

$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, 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.lastactivity, 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
                        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'");
        } 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'");
        }
        if($_DSESSION = $db->fetch_array($query)) {
                $sessionexists = 1;
                if(!empty($_DSESSION['sessionuid'])) {
                        $query = $db->query("SELECT $membertablefields
                                FROM {$tablepre}members m WHERE uid='$_DSESSION[sessionuid]'");
                        $_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
                        FROM {$tablepre}sessions WHERE sid='$sid' AND CONCAT_WS('.',ip1,ip2,ip3,ip4)='$onlineip'");
                if($_DSESSION = $db->fetch_array($query)) {
                        clearcookies();
                        $sessionexists = 1;
                }
        }
}
这一段是有蛮长的,不过看着长不代表它就难,第一行是初始化变量用的(无论何时用变量都要考虑初始化,要不然安全性不值得一提,一个get就完了)
接下来是判断是 不是有sid,有的话就从cdb_session表中取来,然后连接一下cdb_members表取出一些更具体的东西,具体是哪些东西?在$ membertablefields这个变量里面已经全面写出来了,对应数据库看吧,不看的话用英语猜猜得出的。。。在这里Discuz标记了一个 sessionexist变量,表示这个会员是在线的。

CODE:

if(!$sessionexists) {
        if($discuz_uid) {
                $query = $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();
                }
        }
要是不存在sid,不存在discuz_uid,那就肯定没有登陆了,清掉cookie,要是有$discuz_uid的话,还是从members表中取出信息存放到$_DSESSION数组中

CODE:

        if(ipbanned($onlineip)) $_DSESSION['ipbanned'] = 1;

        $_DSESSION['sid'] = random(6);
        $_DSESSION['seccode'] = random(6, 1);
}
$_DSESSION['dateformat'] = empty($_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']) && $_DSESSION['timeoffset'] != 9999 ? $_DSESSION['timeoffset'] : $_DCACHE['settings']['timeoffset'];
这个是判断ip是不是在被阻止的list里,是的话就标记一下,用$_DSESSION[‘ipbanned’]标记的。再把一个随机的sid和seccode写到$_DSESSION数组。然后接下来是把日期,时间,时差写入$_DSESSION这个变量。

CODE:

$membertablefields = '';
@extract($_DSESSION);

$lastvisit = 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_default_timezone_set('Etc/GMT'.($timeoffset > 0 ? '-' : '+').(abs($timeoffset)));
}
又见变量初始化,然后是把$_DESSION给展开,这样方便多了。接下来判断是不是有上次访问的时间,有的话就没事,没有的话就减去24小时。
接下来给一个现在的时间,这里有一个时间的问题,所以把时间加上时差乘上3600秒就得到当前时间了。
PHP 5 能处理时差了,所以Discuz在这里也设置了一下,想得真全面!!
Section Six:
[quote]

CODE:

$accessadd1 = $accessadd2 = $modadd1 = $modadd2 = '';
if(empty($discuz_uid) || empty($discuz_user)) {
        $discuz_user = $extgroupids = '';
        $discuz_uid = $adminid = $posts = $digestposts = $pageviews = $oltime = $invisible
                = $credits = $extcredits1 = $extcredits2 = $extcredits3 = $extcredits4
                = $extcredits5 = $extcredits6 = $extcredits7 = $extcredits8 = 0;
        $groupid = empty($groupid) || $groupid != 6 ? 7 : 6;

} else {
        $discuz_userss = $discuz_user;
        $discuz_user = addslashes($discuz_user);

        if($accessmasks) {
                $accessadd1 = ', 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';
                $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(没登陆当然不存在 版主权限管理员什么的),存在的话就来两个赋值,这就是为什么在Discuz UserGuide里面说$discuz_userss是没有过滤的,而$discuz_user是过滤掉的,能直接进行数据库操作的。
然后看看怎么样设置PHP 的出错级别,注意如果你的管理员的话,Discuz给的是一个更高的错误显示级别!

CODE:

define('FORMHASH', formhash());

$statstatus && require_once DISCUZ_ROOT.'./include/counter.inc.php';

$extra = isset($extra) && @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()函数来的。然后看是不是要等到统计信息,要的话就引用./include/counter.inc.php这个文件,不要的话当然就不引用了。
$extra这个东东不知道做嘛用的。。。。
$tpp-threads per page每页的帖子数
$ppp-posts per page每页的回复数

CODE:

$rsshead = $navtitle = $navigation = '';


$_DSESSION['groupid'] = $groupid = empty($ipbanned) ? (empty($groupid) ? 7 : intval($groupid)) : 6;
if(!@include DISCUZ_ROOT.'./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 {
                $grouptype = 'member';
        }
}
用来得到用户组的,首先看看是不是缓存中有,没有的话就访问数据库了,有的话当然就用缓存的。。

CODE:

if($passport_status && ($passport_status != 'shopex' || !$passport_shopex)) {
        $passport_forward = rawurlencode('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
        $link_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 = $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通行证用的。主要就是得到一些应用程序的地址。

CODE:

if($discuz_uid && $_DSESSION) {
        if(!empty($groupexpiry) && $groupexpiry < $timestamp && (!defined('CURSCRIPT') || (CURSCRIPT != 'wap' && CURSCRIPT != 'member'))) {
                dheader("Location: {$boardurl}member.php?action=groupexpiry");
        } elseif($grouptype && $groupid != getgroupid($discuz_uid, array
                (
                'type' => $grouptype,
                'creditshigher' => $groupcreditshigher,
                'creditslower' => $groupcreditslower
                ), $_DSESSION)) {
                @extract($_DSESSION);
                $cachelost .= (@include DISCUZ_ROOT.'./forumdata/cache/usergroup_'.intval($groupid).'.php') ? '' : ' usergroup_'.$groupid;
        }
}
这一段用来判断你的用户组是不是过期了的,如果过期了很不幸,就被告知用户组过期了。接下来如果没有过期的话就再展开一下$_DSESSION,因为此时的$_DSESSION包含了更多的东西了。

CODE:

if(!in_array($adminid, array(1, 2, 3))) {
        $alloweditpost = $alloweditpoll = $allowstickthread = $allowmodpost = $allowdelpost = $allowmassprune
                = $allowrefund = $allowcensorword = $allowviewip = $allowbanip = $allowedituser = $allowmoduser
                = $allowbanuser = $allowpostannounce = $allowviewlog = $disablepostctrl = $supe_allowpushthread = 0;
} elseif(isset($radminid) && $adminid != $radminid && $adminid != $groupid) {
        $cachelost .= (@include DISCUZ_ROOT.'./forumdata/cache/admingroup_'.intval($adminid).'.php') ? '' : ' admingroup_'.$groupid;
}
这里是权限判断,如果你不是admin, moderator, super moderator,那么你的什么权限都没。。。

CODE:

$forum = array();
$auditstatuson = !empty($mod) && $mod == 'edit' && in_array($adminid, array(1, 2, 3)) && $allowmodpost ? true : false;

$tid = isset($tid) && is_numeric($tid) ? $tid : 0;
$fid = isset($fid) && is_numeric($fid) ? $fid : 0;
$typeid = isset($typeid) ? intval($typeid) : 0;

if(!empty($tid) || !empty($fid)) {
        if(empty($tid)) {
                $query = $db->query("SELECT f.fid, f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
                        FROM {$tablepre}forums f
                        LEFT JOIN {$tablepre}forumfields ff ON ff.fid=f.fid $accessadd2 $modadd2
                        WHERE f.fid='$fid'");
                $forum = $db->fetch_array($query);
        } else {
                $query = $db->query("SELECT t.tid, t.closed,".(defined('SQL_ADD_THREAD') ? SQL_ADD_THREAD : '')." f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
                        FROM {$tablepre}threads t
                        INNER JOIN {$tablepre}forums f ON f.fid=t.fid
                        LEFT JOIN {$tablepre}forumfields ff ON ff.fid=f.fid $accessadd2 $modadd2
                        WHERE t.tid='$tid'".($auditstatuson ? '' : " AND t.displayorder>='0'")." LIMIT 1");
                $forum = $db->fetch_array($query);
                $tid = $forum['tid'];
        }

        if($forum) {
                $fid = $forum['fid'];
                $forum['ismoderator'] = !empty($forum['ismoderator']) || $adminid == 1 || $adminid == 2 ? 1 : 0;
                foreach(array('postcredits', 'replycredits', 'threadtypes', 'digestcredits', 'postattachcredits', 'getattachcredits', 'supe_pushsetting') as $key) {
                        $forum[$key] = !empty($forum[$key]) ? unserialize($forum[$key]) : array();
                }
        } else {
                $fid = 0;
        }
}
得到论坛信息用的,如果你是看forumdisplay页面和看viewthread在数据库中执行的东东不是一样的。这样能得到一点性能上的提升。然后往forum这个数组里面写一些东西进去,比方说:发帖子得到的分数,回复得到的分数,帖子分类的类型等…

CODE:

$styleid = intval(!empty($_GET['styleid']) ? $_GET['styleid'] :
                (!empty($_POST['styleid']) ? $_POST['styleid'] :
                (!empty($_DSESSION['styleid']) ? $_DSESSION['styleid'] :
                $_DCACHE['settings']['styleid'])));

$styleid = intval(isset($stylejump[$styleid]) ? $styleid : $_DCACHE['settings']['styleid']);

if(@!include DISCUZ_ROOT.'./forumdata/cache/style_'.intval(!empty($forum['styleid']) ? $forum['styleid'] : $styleid).'.php') {
        $cachelost .= (@include DISCUZ_ROOT.'./forumdata/cache/style_'.($styleid = $_DCACHE['settings']['styleid']).'.php') ? '' : ' style_'.$styleid;
}
这里是得到论坛风格的地方,可以看到第一行的赋值Disuz用尽一切可能得到一个合理的风格styleid,呵呵,然后再来一行,看看是不是你在论坛底部切换了一下风格,有的话就override一下,把这个风格作为你的当前风格。
如果没有缓存的话,再往cachelost这个里面写点东西。

CODE:

if($cachelost) {
        require_once DISCUZ_ROOT.'./include/cache.func.php';
        updatecache();
        dexit('Cache List: '.$cachelost.'<br>Caches successfully created, please refresh.');
}
如果cachelost中有东西的话,那么就调用include/cache.func.php,并用这个文件里面定义的updatecache();来更新缓存,并强制访问者刷新论坛,使缓存立即生效。

CODE:

if(!defined('CURSCRIPT') || CURSCRIPT != 'wap') {
        if($nocacheheaders) {
                @dheader("Expires: 0");
                @dheader("Cache-Control: private, post-check=0, pre-check=0, max-age=0", FALSE);
                @dheader("Pragma: no-cache");
        }
        if($headercharset) {
                @dheader('Content-Type: text/html; charset='.$charset);
        }
        if(empty($_DCOOKIE['sid']) || $sid != $_DCOOKIE['sid']) {
                dsetcookie('sid', $sid, 604800);
        }
}
这 里是设置网页的header用的,通过判断是不是wap或者是不是有CURSCRIPT这个常量(注:CURSCRIPT代表了当前执行的script, 比方说forumdisplay.php中就会有define(‘CURSCRIPT’, ‘forumdisplay’);这样的定义),下一行是说sid过期了,再生成一个,dsetcookie()这个函数是. /include/global.func.php这个文件中定义的。

CODE:

if($cronnextrun && $cronnextrun <= $timestamp) {
        require_once DISCUZ_ROOT.'./include/cron.func.php';
        runcron();
}
呵呵,后台的计划任务来了。。。。当然,这种情况是要至少有一个会员访问论坛,如果说没有人访问,你再设置也没有用,PHP的一大痛处,不能自己执行…

CODE:

if(isset($plugins['include']) && is_array($plugins['include'])) {
        foreach($plugins['include'] as $include) {
                if(!$include['adminid'] || ($include['adminid'] && $include['adminid'] >= $adminid)) {
                        @include_once DISCUZ_ROOT.'./plugins/'.$include['script'].'.inc.php';
                }
        }
}
这里的加载一些插件的文件。

CODE:

if((!empty($_DCACHE['advs']) || $globaladvs || $redirectadvs) && !defined('IN_ADMINCP')) {
        require_once DISCUZ_ROOT.'./include/advertisements.inc.php';
}
这里是看看是不是在论坛的cache中有广告的存在(注:$_DCACHE这个数组中存放的是论坛的一些设置之类的缓存),有广告的话就引用广告的文件。

CODE:

if(isset($allowvisit) && $allowvisit == 0 && !(defined('CURSCRIPT') && CURSCRIPT == 'member' && $action == 'groupexpiry')) {
        showmessage('user_banned', NULL, 'HALTED');
} elseif(!((defined('CURSCRIPT') && in_array(CURSCRIPT, array('logging', 'wap', 'seccode'))) || $adminid == 1)) {
        if($bbclosed) {
                clearcookies();
                $closedreason = $db->result($db->query("SELECT value FROM {$tablepre}settings WHERE variable='closedreason'"), 0);
                showmessage($closedreason ? $closedreason : 'board_closed', NULL, 'NOPERM');
        }
        periodscheck('visitbanperiods');
}
论 坛的安全访问设置。一个是不允许访问,另一个是访问到了member.php?action=groupexpiry的话,就说用户被ban了。然后就是 检查是不是论坛关闭了,可以看也论坛关闭对管理员没有影响。接下来检查是不是禁的时间到了…periodscheck()函数就是这个用的。如果没到是会 被showmessage说没有访问权限的,具体看include/global.func.php这个文件中的定义。当然,下一期我就会分析这个文件。

CODE:

if((!empty($fromuid) || !empty($fromuser)) && ($creditspolicy['promotion_visit'] || $creditspolicy['promotion_register'])) {
        require_once DISCUZ_ROOT.'/include/promotion.inc.php';
}
这个当然是推荐注册用的。应该没看错,呵呵。

CODE:

$rssauth = $rssstatus && $discuz_uid ? rawurlencode(authcode("$discuz_uid/t".($fid ? $fid : '')."/t".substr(md5($discuz_pw.$discuz_secques), 0, 8), 'ENCODE', md5($_DCACHE['settings']['authkey']))) : '0';Rss检查

好了,到此./include/common.inc.php这个文件就分析完了。
discuz源代码分析  笨才  发布于2007-05-28 11:41:13
include/global.func.php的注释

CODE:

if(!defined('IN_DISCUZ')) {
        exit('Access Denied');
}
//防非法引用的

CODE:

/**
* 这一个部分是生成discuz authcode的,用的是base64加密,分别能加密和解密。通过传入$operation = 'ENCODE'|'DECODE'来实现。
* @para string $string 要加/解密的string
* @para string $operation 方法(个人觉得用boolean比较好) 
* @para string $key 用来加密的key

* @return string
*/

function authcode($string, $operation, $key = '') {

        $key = md5($key ? $key : $GLOBALS['discuz_auth_key']);
        $key_length = strlen($key);

        $string = $operation == 'DECODE' ? base64_decode($string) : substr(md5($string.$key), 0, 8).$string;
        $string_length = strlen($string);

        $rndkey = $box = array();
        $result = '';

        for($i = 0; $i <= 255; $i++) {
                $rndkey[$i] = ord($key[$i % $key_length]);
                $box[$i] = $i;
        }

        for($j = $i = 0; $i < 256; $i++) {
                $j = ($j + $box[$i] + $rndkey[$i]) % 256;
                $tmp = $box[$i];
                $box[$i] = $box[$j];
                $box[$j] = $tmp;
        }

        for($a = $j = $i = 0; $i < $string_length; $i++) {
                $a = ($a + 1) % 256;
                $j = ($j + $box[$a]) % 256;
                $tmp = $box[$a];
                $box[$a] = $box[$j];
                $box[$j] = $tmp;
                $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }

        if($operation == 'DECODE') {
                if(substr($result, 0, 8) == substr(md5(substr($result, 8).$key), 0, 8)) {
                        return substr($result, 8);
                } else {
                        return '';
                }
        } else {
                return str_replace('=', '', base64_encode($result));
        }

}

CODE:

/**
* 这个是用来清除所有的cookie的
*/

function clearcookies() {
        global $discuz_uid, $discuz_user, $discuz_pw, $discuz_secques, $adminid, $credits;
        dsetcookie('sid', '', -86400 * 365);
        dsetcookie('auth', '', -86400 * 365);
        dsetcookie('visitedfid', '', -86400 * 365);
        dsetcookie('onlinedetail', '', -86400 * 365, 0);

        $discuz_uid = $adminid = $credits = 0;
        $discuz_user = $discuz_pw = $discuz_secques = '';
}

CODE:

/**
* 用来检查积分的最低要求的
* @para array $creditsarray 传入积分数组
* @para int $coef 单位
*/

function checklowerlimit($creditsarray, $coef = 1) {
        if(is_array($creditsarray)) {
                global $extcredits, $id;
                foreach($creditsarray as $id => $addcredits) {
                        if($addcredits * $coef < 0 && $GLOBALS['extcredits'.$id] - $addcredits < $extcredits[$id]['lowerlimit']) {
                                showmessage('credits_policy_lowerlimit');
                        }
                }
        }
}

CODE:

/**
* 用来完美分词的,也就是把一段中文字只取前面一段,再加一个…
* @para string $string 用来分词的串
* @para int $length 保留的长度
* @para string $dot 最后加点什么

* @return string
*/

function cutstr($string, $length, $dot = ' ...') {
        global $charset;

        if(strlen($string) <= $length) {
                return $string;
        }

        $string = str_replace(array('&', '"', '&lt;', '&gt;'), array('&', '"', '<', '>'), $string);

        $strcut = '';
        if(strtolower($charset) == 'utf-8') {

                $n = $tn = $noc = 0;
                while($n < strlen($string)) {

                        $t = ord($string[$n]);
                        if($t == 9 || $t == 10 || (32 <= $t && $t <= 126)) {
                                $tn = 1; $n++; $noc++;
                        } elseif(194 <= $t && $t <= 223) {
                                $tn = 2; $n += 2; $noc += 2;
                        } elseif(224 <= $t && $t < 239) {
                                $tn = 3; $n += 3; $noc += 2;
                        } elseif(240 <= $t && $t <= 247) {
                                $tn = 4; $n += 4; $noc += 2;
                        } elseif(248 <= $t && $t <= 251) {
                                $tn = 5; $n += 5; $noc += 2;
                        } elseif($t == 252 || $t == 253) {
                                $tn = 6; $n += 6; $noc += 2;
                        } else {
                                $n++;
                        }

                        if($noc >= $length) {
                                break;
                        }

                }
                if($noc > $length) {
                        $n -= $tn;
                }

                $strcut = substr($string, 0, $n);

        } else {
                for($i = 0; $i < $length - strlen($dot) - 1; $i++) {
                        $strcut .= ord($string[$i]) > 127 ? $string[$i].$string[++$i] : $string[$i];
                }
        }

        $strcut = str_replace(array('&', '"', '<', '>'), array('&', '"', '&lt;', '&gt;'), $strcut);

        return $strcut.$dot;
}

CODE:

/**
* 用来过滤字串的,之所以要这样一个函数是考虑到是不是打开了magic_quotes_gpc.
* @para string $string 要过滤的字串
* @para int $force 强制过滤

* @return string
*/

function daddslashes($string, $force = 0) {
        !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc());
        if(!MAGIC_QUOTES_GPC || $force) {
                if(is_array($string)) {
                        foreach($string as $key => $val) {
                                $string[$key] = daddslashes($val, $force);
                        }
                } else {
                        $string = addslashes($string);
                }
        }
        return $string;
}

CODE:

/**
* 检查一个日期是否合法
* @para string $ymd 日期字串
* @para string $sep 分隔符

* @return boolean
*/

function datecheck($ymd, $sep='-') {
        if(!empty($ymd)) {
                list($year, $month, $day) = explode($sep, $ymd);
                return checkdate($month, $day, $year);
        } else {
                return FALSE;
        }
}

CODE:

/**
* 用来计算程序运行时间的,论坛底部的debug info

* @return boolean
*/
function debuginfo() {
        if($GLOBALS['debug']) {
                global $db, $discuz_starttime, $debuginfo;
                $mtime = explode(' ', microtime());
                $debuginfo = array('time' => number_format(($mtime[1] + $mtime[0] - $discuz_starttime), 6), 'queries' => $db->querynum);
                return TRUE;
        } else {
                return FALSE;
        }
}

CODE:

/**
* 强制退出
* @para string $message

*/
function dexit($message = '') {
        echo $message;
        output();
        exit();
}

CODE:

/**        
* 过滤HTML代码的
* @para string $string

* @return string
*/
function dhtmlspecialchars($string) {
        if(is_array($string)) {
                foreach($string as $key => $val) {
                        $string[$key] = dhtmlspecialchars($val);
                }
        } else {
                $string = preg_replace('/&((#(/d{3,5}|x[a-fA-F0-9]{4})|[a-zA-Z][a-z0-9]{2,5});)/', '&//1',
                str_replace(array('&', '"', '<', '>'), array('&', '"', '&lt;', '&gt;'), $string));
        }
        return $string;
}

CODE:

/**
* 用来设置header的
* @para string $string
* @para boolean $replace
* @para int $http_reponse_code

*/

function dheader($string, $replace = true, $http_response_code = 0) {
        $string = str_replace(array("/r", "/n"), array('', ''), $string);
        header($string, $replace, $http_response_code); //直接设置header
        if(preg_match('/^/s*location:/is', $string)) { //如果不符合location开头的话就exit了
                exit();
        }
}

CODE:

/**
* 判断是不是文件上传了
* @return boolean
*/

function disuploadedfile($file) {
        return function_exists('is_uploaded_file') && (is_uploaded_file($file) || is_uploaded_file(str_replace('////', '//', $file)));
}

discuz源代码分析  笨才  发布于2007-05-28 11:44:58

CODE:

/**
* 用来得到上一个页面的地址,也就是来路。
* @para string $default 这个参数是直接设置一个refer,不用判断得到
*
* @return string
*/

function dreferer($default = '') {
        global $referer, $indexname;

        $default = empty($default) ? $indexname : '';
        if(empty($referer) && isset($GLOBALS['_SERVER']['HTTP_REFERER'])) {
                $referer = preg_replace("/([/?&])((sid/=[a-z0-9]{6})(&|$))/i", '//1', $GLOBALS['_SERVER']['HTTP_REFERER']);
                $referer = substr($referer, -1) == '?' ? substr($referer, 0, -1) : $referer;
        } else {
                $referer = dhtmlspecialchars($referer);
        }

        if(!preg_match("/(/.php|[a-z]+(/-/d+)+/.html)/", $referer) || strpos($referer, 'logging.php')) {
                $referer = $default;
        }
        return $referer;
}

CODE:

/**
* 设置cookie用的,我觉得这个和clearcookies放到一起比较好,不过好像这个是按字母排的…
* @para string $var cookie名
* @para string $value cookie值
* @para int $life 生存时间
* @para int $prefix cookie前缀

*/

function dsetcookie($var, $value, $life = 0, $prefix = 1) {
        global $cookiepre, $cookiedomain, $cookiepath, $timestamp, $_SERVER;
        //echo $prefix."--".$var."--".$value."--".$life."--".$cookiepath;
        
        setcookie(($prefix ? $cookiepre : '').$var, $value,
                $life ? $timestamp + $life : 0, $cookiepath,
                $cookiedomain, $_SERVER['SERVER_PORT'] == 443 ? 1 : 0);
}

CODE:

/**
* 删除论坛的附件用的
* @para string $filename 附件名
* @para int $havethumb 是否有缩略图
* @para int $remote 是否为远程附件

*/

function dunlink($filename, $havethumb = 0, $remote = 0) {
        global $authkey, $ftp, $attachdir;
        if($remote) {
                require_once DISCUZ_ROOT.'./include/ftp.func.php';
                if(!$ftp['connid']) {
                        if(!($ftp['connid'] = dftp_connect($ftp['host'], $ftp['username'], authcode($ftp['password'], 'DECODE', md5($authkey)), $ftp['attachdir'], $ftp['port'], $ftp['ssl']))) {
                                return;
                        }
                }
                dftp_delete($ftp['connid'], $filename);
                $havethumb && dftp_delete($ftp['connid'], $filename.'.thumb.jpg');
        } else {
                @unlink($attachdir.'/'.$filename);
                $havethumb && @unlink($attachdir.'/'.$filename.'.thumb.jpg');
        }
}

CODE:

/**
* 生成email连接用的,比如把[email]nicollelord@yahoo.com[/email]换成:<a href="mailto:nicollelord@yahoo.com">nicollelord@yahoo.com</a>这样 的形式
* @para string $email
* @para int $tolink
*/

function emailconv($email, $tolink = 1) {
        $email = str_replace(array('@', '.'), array('&#64;', '&#46;'), $email);
        return $tolink ? '<a href="mailto: '.$email.'">'.$email.'</a>': $email;
}

CODE:

/**
* 记录错误日志用的
* @para string $type 错误类型
* @para string $message 错误内容
* @para int $halt 发生错误后是不是就马上停止论坛的运行

*/

function errorlog($type, $message, $halt = 1) {
        global $timestamp, $discuz_userss, $onlineip, $_SERVER;
        $user = empty($discuz_userss) ? '' : $discuz_userss.'<br>';
        $user .= $onlineip.'|'.$_SERVER['REMOTE_ADDR'];
        writelog('errorlog', dhtmlspecialchars("$timestamp/t$type/t$user/t".str_replace(array("/r", "/n"), array(' ', ' '), trim($message))));
        if($halt) {
                dexit();
        }
}

CODE:

/**
* 判断访问者是不是robot

* @return boolean
*/

function getrobot() {
        if(!defined('IS_ROBOT')) {
                $kw_spiders = 'Bot|Crawl|Spider|slurp|sohu-search|lycos|robozilla';
                $kw_browsers = 'MSIE|Netscape|Opera|Konqueror|Mozilla';
                if(preg_match("/($kw_browsers)/", $_SERVER['HTTP_USER_AGENT'])) {
                        define('IS_ROBOT', FALSE);
                } elseif(preg_match("/($kw_spiders)/", $_SERVER['HTTP_USER_AGENT'])) {
                        define('IS_ROBOT', TRUE);
                } else {
                        define('IS_ROBOT', FALSE);
                }
        }
        return IS_ROBOT;
}

CODE:

/**
* 得到一个文件的扩展名
* @para string $filename

* @return string
*/

function fileext($filename) {
        return trim(substr(strrchr($filename, '.'), 1, 10));
}

CODE:

/**
* 用当前时间,会员名,uid,密码,authkey生成一个form hash(哈希)

* @return string
*/

function formhash() {
        global $discuz_user, $discuz_uid, $discuz_pw, $timestamp, $discuz_auth_key;
        return substr(md5(substr($timestamp, 0, -7).$discuz_user.$discuz_uid.$discuz_pw.$discuz_auth_key), 8, 8);
}

CODE:

/**
* 生成论坛访问权限的字串,以|隔开
* @para string $permstr 访问权限字串
*
* @return string
*/

function forumperm($permstr) {
        global $groupid, $extgroupids;

        $groupidarray = array($groupid);
        foreach(explode("/t", $extgroupids) as $extgroupid) {
                if($extgroupid = intval(trim($extgroupid))) {
                        $groupidarray[] = $extgroupid;
                }
        }
        return preg_match("/(^|/t)(".implode('|', $groupidarray).")(/t|$)/", $permstr);
}

CODE:

/**
* 得到用户组,同步groupid和member['groupid'],当会员积分和当前积分不一致更新members表。
* @para int $uid 会员的uid
* @para array $group 会员所属的用户组
* @para array $member
*
* @return string
*/

function getgroupid($uid, $group, &$member) {
        global $creditsformula, $db, $tablepre;

        if(!empty($creditsformula)) {
                $updatearray = array();
                eval("/$credits = round($creditsformula);");

                if($credits != $member['credits']) {
                        $updatearray[] = "credits='$credits'";
                }
                if($group['type'] == 'member' && !($member['credits'] >= $group['creditshigher'] && $member['credits'] < $group['creditslower'])) {
                        $query = $db->query("SELECT groupid FROM {$tablepre}usergroups WHERE type='member' AND $member[credits]>=creditshigher AND $member[credits]<creditslower LIMIT 1");
                        if($db->num_rows($query)) {
                                $member['groupid'] = $db->result($query, 0);
                                $updatearray[] = "groupid='$member[groupid]'";
                        }
                }

                if($updatearray) {
                        $db->query("UPDATE {$tablepre}members SET ".implode(', ', $updatearray)." WHERE uid='$uid'");
                }
        }

        return $member['groupid'];
}

CODE:

/**
* 这个的作用主要是把序列化后存在于数据库中的会员组到期信息取出来
* @para string $terms
* @para int $expiry
*/

function groupexpiry($terms) {
        $terms = is_array($terms) ? $terms : unserialize($terms);
        $groupexpiry = isset($terms['main']['time']) ? intval($terms['main']['time']) : 0;
        if(is_array($terms['ext'])) {
                foreach($terms['ext'] as $expiry) {
                        if((!$groupexpiry && $expiry) || $expiry < $groupexpiry) {
                                $groupexpiry = $expiry;
                        }
                }
        }
        return $groupexpiry;
}

CODE:

/**
* 看看一个ip是不是在允许访问的范围内
* @para string $ip
* @para array $accesslist
*
* @return boolean
*/

function ipaccess($ip, $accesslist) {
        return preg_match("/^(".str_replace(array("/r/n", ' '), array('|', ''), preg_quote($accesslist, '/')).")/", $ip);
}

CODE:

/**
* 判断ip是不是被ban了
* @para string $onlineip

* @return boolean
*/

function ipbanned($onlineip) {
        global $ipaccess, $timestamp, $cachelost;

        if($ipaccess && !ipaccess($onlineip, $ipaccess)) {
                return TRUE;
        }

        $cachelost .= (@include DISCUZ_ROOT.'./forumdata/cache/cache_ipbanned.php') ? '' : ' ipbanned';
        if(empty($_DCACHE['ipbanned'])) {
                return FALSE;
        } else {
                if($_DCACHE['ipbanned']['expiration'] < $timestamp) {
                        @unlink(DISCUZ_ROOT.'./forumdata/cache/cache_ipbanned.php');
                }
                return preg_match("/^(".$_DCACHE['ipbanned']['regexp'].")$/", $onlineip);
        }
}

CODE:

/**
* 检查一个email的合法性
* @para string $email
*
* @return boolean
*/

function isemail($email) {
        return strlen($email) > 6 && preg_match("/^[/w/-/.]+@[/w/-/.]+(/./w+)+$/", $email);
}

CODE:

/**
* Discuz语言解析用到的东西,即按照需要装入语言包这个数组
* @para string $file 语言文件(如:templates, email, actions等)
* @para int $templateid 用的是哪套模板中的,若没有的话用TEMPLATEID这个常数取代
* @para string $tpldir 模板所在的目录
*
* @return array or false
*/

function language($file, $templateid = 0, $tpldir = '') {
        $tpldir = $tpldir ? $tpldir : TPLDIR;
        $templateid = $templateid ? $templateid : TEMPLATEID;

        $languagepack = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.lang.php';
        if(file_exists($languagepack)) {
                return $languagepack;
        } elseif($templateid != 1 && $tpldir != './templates/default') {
                return language($file, 1, './templates/default');
        } else {
                return FALSE;
        }
}

CODE:

/**
* 超经典的分页函数来了
* @para int $num 记录总数
* @para int $perpage 每页的记录数
* @para int $curpage 当前页
* @para string $mpurl 这个是用来保留query string中的参数的,打个比方:forumdisplay.php?fid=2,这个就是mpurl了,处理后会变成forumdisplay.php?fid=2&page=xx
* @para int $maxpages 最大的页数
* @para int $page 总页数
* @para int $simple 好像simple如果比一大的话就只有第一页,上一页,下一页,最后页
* @para string $onclick 点击触发的事件,AJAX用的。
*
* @return string
*/

function multi($num, $perpage, $curpage, $mpurl, $maxpages = 0, $page = 10, $simple = 0, $onclick = '') {
        $multipage = '';
        $mpurl .= strpos($mpurl, '?') ? '&' : '?';
        $onclick = $onclick ? ' onclick="'.$onclick.'(event)"' : '';
        if($num > $perpage) {
                $offset = 2;

                $realpages = @ceil($num / $perpage);
                $pages = $maxpages && $maxpages < $realpages ? $maxpages : $realpages;

                if($page > $pages) {
                        $from = 1;
                        $to = $pages;
                } else {
                        $from = $curpage - $offset;
                        $to = $from + $page - 1;
                        if($from < 1) {
                                $to = $curpage + 1 - $from;
                                $from = 1;
                                if($to - $from < $page) {
                                        $to = $page;
                                }
                        } elseif($to > $pages) {
                                $from = $pages - $page + 1;
                                $to = $pages;
                        }
                }

                $multipage = ($curpage - $offset > 1 && $pages > $page ? '<a href="'.$mpurl.'page=1" class="p_redirect"'.$onclick.'>|‹</a>' : '').
                        ($curpage > 1 && !$simple ? '<a href="'.$mpurl.'page='.($curpage - 1).'" class="p_redirect">‹‹</a>' : '');
                for($i = $from; $i <= $to; $i++) {
                        $multipage .= $i == $curpage ? '<a class="p_curpage">'.$i.'</a>' :
                                '<a href="'.$mpurl.'page='.$i.'" class="p_num"'.$onclick.'>'.$i.'</a>';
                }

                $multipage .= ($curpage < $pages && !$simple ? '<a href="'.$mpurl.'page='.($curpage + 1).'" class="p_redirect"'.$onclick.'>››</a>' : '').
                        ($to < $pages ? '<a href="'.$mpurl.'page='.$pages.'" class="p_redirect"'.$onclick.'>›|</a>' : '').
                        ($curpage == $maxpages ? '<a class="p_redirect" href="misc.php?action=maxpages&pages='.$maxpages.'">›?</a>' : '').
                        (!$simple && $pages > $page ? '<a class="p_pages" style="padding: 0px"><input class="p_input" type="text" name="custompage" onKeyDown="if(event.keyCode==13) {window.location=/''.$mpurl.'page=/'+this.value; return false;}"></a>' : '');

                $multipage = $multipage ? '<div class="p_bar">'.(!$simple ? '<a class="p_total"> '.$num.' </a><a class="p_pages"> '.$curpage.'/'.$realpages.' </a>' : '').$multipage.'</div>' : '';
        }
        return $multipage;
}

discuz源代码分析  笨才  发布于2007-05-28 11:56:32

CODE:

/**
* 输出用的,这个时候可以做一点事情,比如说:把sid用正则写到form里面,rewrite规则启用后把论坛里面的forumdisplay.php?fid=1&page=2用正则换成forum-1-2.html。
* 并把ftp连接关掉,写入cache
*/

function output() {
        global $sid, $transsidstatus, $rewritestatus, $ftp;

        if(($transsidstatus = empty($GLOBALS['_DCOOKIE']['sid']) && $transsidstatus) || in_array($rewritestatus, array(2, 3))) {
                if($transsidstatus) {
                        $searcharray = array
                                (
                                "//<a(/s*[^/>]+/s*)href/=([/"|/']?)([^/"/'/s]+)/ies",
                                "/(/<form.+?/>)/is"
                                );
                        $replacearray = array
                                (
                                "transsid('//3','<a//1href=//2')",
                                "//1/n<input type=/"hidden/" name=/"sid/" value=/"$sid/">"
                                );
                } else {
                        $searcharray = array
                                (
                                //"//<a href/=/"index/.php/"/>/",
                                "//<a href/=/"forumdisplay/.php/?fid/=(/d+)(&page/=(/d+))?/"([^/>]*)/>/e",
                                "//<a href/=/"viewthread/.php/?tid/=(/d+)(&extra/=page/%3D(/d+))?(&page/=(/d+))?/"([^/>]*)/>/e",
                                "//<a href/=/"viewpro/.php/?(uid/=(/d+)|username/=([^&]+?))/"([^/>]*)/>/e",
                                "//<a href/=/"space/.php/?(uid/=(/d+)|username/=([^&]+?))/"([^/>]*)/>/e"
                                );
                        $replacearray = array
                                (
                                //"<a href=/"index.html/">",
                                "rewrite_forum('//1', '//3', '//4')",
                                "rewrite_thread('//1', '//5', '//3', '//6')",
                                "rewrite_profile('//2', '//3', '//4')",
                                "rewrite_space('//2', '//3', '//4')"
                                );
                }

                $content = preg_replace($searcharray, $replacearray, ob_get_contents());
                ob_end_clean();
                $GLOBALS['gzipcompress'] ? ob_start('ob_gzhandler') : ob_start();

                echo $content;
        }
        if($ftp['connid']) {
                @ftp_close($ftp['connid']);
        }
        $ftp = array();

        if(defined('CACHE_FILE') && CACHE_FILE && !defined('CACHE_FORBIDDEN')) {
                global $cachethreaddir;
                if(diskfreespace(DISCUZ_ROOT.'./'.$cachethreaddir) > 1000000) {
                        $fp = fopen(CACHE_FILE, 'w');
                        if($fp) {
                                flock($fp, LOCK_EX);
                                fwrite($fp, empty($content) ? ob_get_contents() : $content);
                        }
                        @fclose($fp);
                }
        }
}

CODE:

/**这一段都是rewrite规则,和上一个函数相映成趣~
* @para int $tid 帖子的id
* @para int $page 页数
* @para int $prevpage 上一页
* @para string $extra 附加的东东
*
* @return string 返回一个链接,如: <a href = "thread-1-1.html">
*/

function rewrite_thread($tid, $page = 0, $prevpage = 0, $extra = '') {
        return '<a href="thread-'.$tid.'-'.($page ? $page : 1).'-'.($prevpage ? $prevpage : 1).'.html"'.stripslashes($extra).'>';
}

function rewrite_forum($fid, $page = 0, $extra = '') {
        return '<a href="forum-'.$fid.'-'.($page ? $page : 1).'.html"'.stripslashes($extra).'>';
}

function rewrite_profile($uid, $username, $extra = '') {
        return '<a href="profile-'.($uid ? 'uid-'.$uid : 'username-'.$username).'.html"'.stripslashes($extra).'>';
}

function rewrite_space($uid, $username, $extra = '') {
        return '<a href="space-'.($uid ? 'uid-'.$uid : 'username-'.$username).'.html"'.stripslashes($extra).'>';
}

CODE:

/**
* 访问时段检查
* @para string $periods 允许访问的时段
* @para string showmessage 如果不允许访问的时段会员访问了的话出现的自定义提示

* @return TRUE or no return
*/

function periodscheck($periods, $showmessage = 1) {
        global $timestamp, $disableperiodctrl, $_DCACHE, $banperiods;

        if(!$disableperiodctrl && $_DCACHE['settings'][$periods]) {
                $now = gmdate('G.i', $timestamp + $_DCACHE['settings']['timeoffset'] * 3600);
                foreach(explode("/r/n", str_replace(':', '.', $_DCACHE['settings'][$periods])) as $period) {
                        list($periodbegin, $periodend) = explode('-', $period);
                        if(($periodbegin > $periodend && ($now >= $periodbegin || $now < $periodend)) || ($oeriodbegin < $periodend && $now >= $periodbegin && $now < $periodend)) {
                                $banperiods = str_replace("/r/n", ', ', $_DCACHE['settings'][$periods]);
                                if($showmessage) {
                                        showmessage('period_nopermission', NULL, 'NOPERM');
                                } else {
                                        return TRUE;
                                }
                        }
                }
        }
        return FALSE;
}

CODE:

/**
* 加密安全提问的,MD5加密
* @para int $qestionid
* @para string $anser
*
* @return string 返回加密后的字串
*/

function quescrypt($questionid, $answer) {
        return $questionid > 0 && $answer != '' ? substr(md5($answer.md5($questionid)), 16, 8) : '';
}

CODE:

/**
* 生成随机数(这里返回$hash名字有点点不合适,hash是包含key和value的,这里没有这样的意思)
* @para int $length 要生成的长度
* @para int $numeric 传入一个非零的数表示要生成的全是数字
*
* @return string 返回生成的字串
*/

function random($length, $numeric = 0) {
        PHP_VERSION < '4.2.0' && mt_srand((double)microtime() * 1000000);
        if($numeric) {
                $hash = sprintf('%0'.$length.'d', mt_rand(0, pow(10, $length) - 1));
        } else {
                $hash = '';
                $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz';
                $max = strlen($chars) - 1;
                for($i = 0; $i < $length; $i++) {
                        $hash .= $chars[mt_rand(0, $max)];
                }
        }
        return $hash;
}

CODE:

/**
* 删除目录和文件用的
* @para string $dirname 要删除的目录名
* @para boolean $keepdir 是否在删除文件后保留目录,也就是只清空目录
*
* @return boolean
*/

function removedir($dirname, $keepdir = FALSE) {

        $dirname = wipespecial($dirname);

        if(!is_dir($dirname)) {
                return FALSE;
        }
        $handle = opendir($dirname);
        while(($file = readdir($handle)) !== FALSE) {
                if($file != '.' && $file != '..') {
                        $dir = $dirname . DIRECTORY_SEPARATOR . $file;
                        is_dir($dir) ? removedir($dir) : unlink($dir);
                }
        }
        closedir($handle);
        return !$keepdir ? (@rmdir($dirname) ? TRUE : FALSE) : TRUE;
}

CODE:

/**
* 发信件用的,用到了include目录下的sendmail.inc.php
* @para string $email_to
* @para string $email_subject
* @para string $email_message
* @para string $email_from

*/

function sendmail($email_to, $email_subject, $email_message, $email_from = '') {
        extract($GLOBALS, EXTR_SKIP);
        require DISCUZ_ROOT.'./include/sendmail.inc.php';
}

CODE:

/**
* 发送PM的函数
* @para int $toid 对方id
* @para string $subject PM主题
* @para int $fromid 发送方id
* @para string $from 发送方用户名

*/

function sendpm($toid, $subject, $message, $fromid = '', $from = '') {
        extract($GLOBALS, EXTR_SKIP);
        include language('pms');

        if(isset($language[$subject])) {
                eval("/$subject = addslashes(/"".$language[$subject]."/");");
        }
        if(isset($language[$message])) {
                eval("/$message = addslashes(/"".$language[$message]."/");");
        }

        if(!$fromid && !$from) {
                $fromid = $discuz_uid;
                $from = $discuz_user;
        }

        $pmids = array();
        foreach(explode(',', $toid) as $uid) {
                if(is_numeric($uid)) {
                        $query = $db->query("INSERT INTO {$tablepre}pms (msgfrom, msgfromid, msgtoid, folder, new, subject, dateline, message)
                                VALUES ('$from', '$fromid', '$uid', 'inbox', '1', '$subject', '$timestamp', '$message')");
                        if($query) {
                                $pmids[] = $uid;
                        }
                }
        }

        if($toid = implodeids($pmids)) {
                $db->query("UPDATE {$tablepre}members SET newpm='1' WHERE uid IN ($toid)");
        }
}

CODE:

/**
* 大家熟悉的showmesage终于出现~!包含了ajax效果了
* @para string $message 显示在跳转页面的信息,比如:您已成功登陆……
* @para string $url_forword 下一个url,过三秒会自动跳转过去
* @para string $extra 这个用来控制一些特殊的显示,比如:HALTED, NOPERM,这样可以控制一些特殊场合,Discuz想得真是太全面了~!
*/

function showmessage($message, $url_forward = '', $extra = '') {
        extract($GLOBALS, EXTR_SKIP);

        global $extrahead, $discuz_action, $debuginfo, $seccode, $fid, $tid, $supe_fromsupesite, $supe_jumpurl, $supe, $charset, $show_message, $_DCACHE;
        $supe_messagetpl = $supe_error = '';
        $show_message = $message;
        $msgforward = unserialize($_DCACHE['settings']['msgforward']);
        $msgforward['refreshtime'] = intval($msgforward['refreshtime']);
        $url_forward = empty($url_forward) ? '' : (empty($_DCOOKIE['sid']) && $transsidstatus ? transsid($url_forward) : $url_forward);

        if($supe_fromsupesite && $supe['status']) {
                $supe_messagetpl = 'supesite_';
                $extra = '';
                $supe_error = $url_forward ? false : true;
                $url_forward = !empty($supe_jumpurl) && !$supe_error ? urldecode($supe_jumpurl) :  $url_forward;
        } elseif($url_forward && empty($_GET['inajax']) && $msgforward['quick'] && $msgforward['messages'] && @in_array($message, $msgforward['messages'])) {
                updatesession();
                dheader("location: ".str_replace('&', '&', $url_forward));
        }

        if(in_array($extra, array('HALTED', 'NOPERM'))) {
                $fid = $tid = 0;
                $discuz_action = 254;
        } else {
                $discuz_action = 255;
        }

        include language('messages'); //把message这个语言包加了进来

        if(isset($language[$message])) {
                $supe_pre = $supe_fromsupesite ? 'supe_' : '';
                eval("/$show_message = /"".($language[$supe_pre.$message] ? $language[$supe_pre.$message] : $language[$message])."/";");
                unset($supe_pre);
        }

        ajaxtemplate('showmessage_ajax'); 

        $extrahead .= $url_forward ? '<meta http-equiv="refresh" content="'.$msgforward['refreshtime'].' url='.$url_forward.'">' : '';

        if($advlist = $advlist['redirect']) {
                foreach($advlist AS $type => $redirectadvs) {
                        $advlist[$type] = $redirectadvs[array_rand($redirectadvs)];
                }
        }

        if($extra == 'NOPERM' && !$passport_status) {
                //get secure code checking status (pos. -2)
                if($seccodecheck = substr(sprintf('%05b', $seccodestatus), -2, 1)) {
                        $seccode = random(6, 1) + $seccode{0} * 1000000;
                }
                include template('nopermission');
        } else {
                include template($supe_messagetpl.'showmessage');
        }
        dexit();
}

CODE:

/**
* 用来计算星星月亮太阳显示的
* @para $num 等级数

*/

function showstars($num) {
        global $starthreshold;

        $alt = 'alt="Rank: '.$num.'"';
        if(empty($starthreshold)) {
                for($i = 0; $i < $num; $i++) {
                        echo '<img src="'.IMGDIR.'/star_level1.gif" '.$alt.' />';
                }
        } else {
                for($i = 3; $i > 0; $i--) {
                        $numlevel = intval($num / pow($starthreshold, ($i - 1)));
                        $num = ($num % pow($starthreshold, ($i - 1)));
                        for($j = 0; $j < $numlevel; $j++) {
                                echo '<img src="'.IMGDIR.'/star_level'.$i.'.gif" '.$alt.' />';
                        }
                }
        }
}

CODE:

/**
* 得到站点
*
* @return string 如: [url]http://discuz.net[/url]
*/

function site() {
        return $_SERVER['HTTP_HOST'];
}

CODE:

/**
* 这个看成函数重载也无妨,功能就是查找$haystack是不是在$needle中存在
* @para string $haystack
* @para string $needle
*
* @return boolean
*/

function strexists($haystack, $needle) {
        return !(strpos($haystack, $needle) === FALSE);
}

CODE:

/**
* 验证码转换,具体功能用处还没研究
* @para string $seccode 验证码
*
*/

function seccodeconvert(&$seccode) {
        $seccode = substr($seccode, -6);
        $s = sprintf('%04s', base_convert($seccode, 10, 24));
        $seccode = '';
        $seccodeunits = 'BCEFGHJKMPQRTVWXY2346789';
        for($i = 0; $i < 4; $i++) {
                $unit = ord($s{$i});
                $seccode .= ($unit >= 0x30 && $unit <= 0x39) ? $seccodeunits[$unit - 0x30] : $seccodeunits[$unit - 0x57];
        }
}

CODE:

/**
* 提交后的检查,主要是检查验证码,安全提问和来路是不是正常。
* @para string $var 存放在全局变量中的下标
* @para int $allowget 是不是允许get提交
* @para int $seccodecheck 验证码检查
* @para int $secqaacheck 安全提问检查
*
* @return boolean
*/

function submitcheck($var, $allowget = 0, $seccodecheck = 0, $secqaacheck = 0) {
        if(empty($GLOBALS[$var])) {
                return FALSE;
        } else {
                global $_SERVER, $seccode, $seccodeverify, $secanswer, $_DCACHE;
                if($allowget || ($_SERVER['REQUEST_METHOD'] == 'POST' && $GLOBALS['formhash'] == formhash() && (empty($_SERVER['HTTP_REFERER']) ||
                        preg_replace("/https?:////([^/://]+).*/i", "//1", $_SERVER['HTTP_REFERER']) == preg_replace("/([^/:]+).*/", "//1", $_SERVER['HTTP_HOST'])))) {
                        if($seccodecheck) {
                                $tmp = $seccode{0};
                                seccodeconvert($seccode);
                                if(strtoupper($seccodeverify) != $seccode) {
                                        showmessage('submit_seccode_invalid');
                                }
                                $seccode = random(6, 1) + $tmp * 1000000;
                        }
                        if($secqaacheck) {
                                require_once DISCUZ_ROOT.'./forumdata/cache/cache_secqaa.php';
                                if(md5($secanswer) != $_DCACHE['secqaa'][substr($seccode, 0, 1)]['answer']) {
                                        showmessage('submit_secqaa_invalid');
                                }
                                $seccode = random(1, 1) * 1000000 + substr($seccode, -6);
                        }
                        return TRUE;
                } else {
                        showmessage('submit_invalid');
                }
        }
}

CODE:

/**
* 另一个提交检查,检查super site的提交的
* @para int $allowget 是不是允许get提交
* @para int $timespan 时间跨度

* @return boolean
*/
function supe_submitcheck($allowget = 0, $timespan = 300) {
        global $supe_seccode, $timestamp, $_DCOOKIE, $supe, $supe_fromsupesite;
        $supe_hash = isset($_GET['supe_hash']) || isset($_POST['supe_hash']) ?
                (isset($_GET['supe_hash']) ? $_GET['supe_hash'] : $_POST['supe_hash']) :
                (isset($_DCOOKIE['supe_hash']) ? $_DCOOKIE['supe_hash'] : '');
        if($supe_fromsupesite && $supe['status'] && ($allowget || $_SERVER['REQUEST_METHOD'] == 'POST') && $supe_hash && !empty($supe_seccode)) {
                list($check_timestamp, $check_seccode) = explode("/t", authcode($supe_hash, 'DECODE'));
                if($timestamp - $check_timestamp <= $timespan && $check_seccode == $supe_seccode) {
                        return TRUE;
                }
                showmessage('submit_invalid');
        }
        return FALSE;
}

CODE:

/**
* 另外一个重大函数来了,那就是模板解析,绝对Discuz核心
* @para string $file 模板文件(如:discuz, forumdata, viewthread等)
* @para int $templateid 用的是哪套模板中的,若没有的话用TEMPLATEID这个常数取代
* @para string $tpldir 模板所在的目录

* @return string 解析好的模板文件,通过include template('xxx')这样引用到文件,framework的MVC也是这样一个模式的
*/
function template($file, $templateid = 0, $tpldir = '') {
        global $tplrefresh;

        $tpldir = $tpldir ? $tpldir : TPLDIR;
        $templateid = $templateid ? $templateid : TEMPLATEID;

        $tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';
        $objfile = DISCUZ_ROOT.'./forumdata/templates/'.$templateid.'_'.$file.'.tpl.php';
        if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) {
                return template($file, 1, './templates/default/');
        }
        if($tplrefresh == 1 || ($tplrefresh > 1 && substr($GLOBALS['timestamp'], -1) > $tplrefresh)) {
                if(@filemtime($tplfile) > @filemtime($objfile)) {
                        require_once DISCUZ_ROOT.'./include/template.func.php';
                        parse_template($file, $templateid, $tpldir);
                }
        }
        return $objfile;
}

CODE:

/**
* 得到url中的sid
* @para string $url
* @para string tag
* @para int $wml
*
* @return string
*/
function transsid($url, $tag = '', $wml = 0) {
        global $sid;
        $tag = stripslashes($tag);
        if(!$tag || (!preg_match("/^(http:////|mailto:|#|javascript)/i", $url) && !strpos($url, 'sid='))) {
                if($pos = strpos($url, '#')) {
                        $urlret = substr($url, $pos);
                        $url = substr($url, 0, $pos);
                } else {
                        $urlret = '';
                }
                $url .= (strpos($url, '?') ? ($wml ? '&' : '&') : '?').'sid='.$sid.$urlret;
        }
        return $tag.$url;
}

CODE:

/**
* 生成主题分类下拉列表
* @para int $curtypeid 当前选择的id
*
* @return string
*/
function typeselect($curtypeid = 0) {
        if($threadtypes = $GLOBALS['forum']['threadtypes']) {
                $html = '<select name="typeid"><option value="0"> </option>';
                foreach($threadtypes['types'] as $typeid => $name) {
                        $html .= '<option value="'.$typeid.'" '.($curtypeid == $typeid ? 'selected' : '').'>'.strip_tags($name).'</option>';
                }
                $html .= '</select>';
                return $html;
        } else {
                return '';
        }
}

CODE:

/**
* 更新积分用到的函数
* @para string $uids 要更新的uid
* @para array $creditsarray 要更新的积分
* @para int $coef 单位
* @para string $extrasql 附加的sql语句

*/
function updatecredits($uids, $creditsarray, $coef = 1, $extrasql = '') {
        if($uids && ((!empty($creditsarray) && is_array($creditsarray)) || $extrasql)) {
                global $db, $tablepre;
                $creditsadd = $comma = '';
                foreach($creditsarray as $id => $addcredits) {
                        $creditsadd .= $comma.'extcredits'.$id.'=extcredits'.$id.'+('.intval($addcredits).')*('.$coef.')';
                        $comma = ', ';
                }

                if($creditsadd || $extrasql) {
                        $db->query("UPDATE {$tablepre}members SET $creditsadd ".($creditsadd && $extrasql ? ', ' : '')." $extrasql WHERE uid IN ('$uids')", 'UNBUFFERED');
                }
        }
}

CODE:

/**
* 把session更新一下,更新了如下的表:onlinetime, members, sessions
*/
function updatesession() {
        if(!empty($GLOBALS['sessionupdated'])) {
                return TRUE;
        }

        global $db, $tablepre, $sessionexists, $sessionupdated, $sid, $onlineip, $discuz_uid, $discuz_user, $timestamp, $lastactivity, $seccode,
                $pvfrequence, $spageviews, $lastolupdate, $oltimespan, $onlinehold, $groupid, $styleid, $invisible, $discuz_action, $fid, $tid, $bloguid;

        $fid = intval($fid);
        $tid = intval($tid);

        if($oltimespan && $discuz_uid && $lastactivity && $timestamp - ($lastolupdate ? $lastolupdate : $lastactivity) > $oltimespan * 60) {
                $lastolupdate = $timestamp;
                $db->query("UPDATE {$tablepre}onlinetime SET total=total+'$oltimespan', thismonth=thismonth+'$oltimespan', lastupdate='$timestamp' WHERE uid='$discuz_uid' AND lastupdate<='".($timestamp - $oltimespan * 60)."'");
                if(!$db->affected_rows()) {
                        $db->query("INSERT INTO {$tablepre}onlinetime (uid, thismonth, total, lastupdate)
                                VALUES ('$discuz_uid', '$oltimespan', '$oltimespan', '$timestamp')", 'SILENT');
                }
        } else {
                $lastolupdate = intval($lastolupdate);
        }

        if($sessionexists == 1) {
                if($pvfrequence && $discuz_uid) {
                        if($spageviews >= $pvfrequence) {
                                $pageviewsadd = ', pageviews=/'0/'';
                                $db->query("UPDATE {$tablepre}members SET pageviews=pageviews+'$spageviews' WHERE uid='$discuz_uid'", 'UNBUFFERED');
                        } else {
                                $pageviewsadd = ', pageviews=pageviews+1';
                        }
                } else {
                        $pageviewsadd = '';
                }
                $db->query("UPDATE {$tablepre}sessions SET uid='$discuz_uid', username='$discuz_user', groupid='$groupid', styleid='$styleid', invisible='$invisible', action='$discuz_action', lastactivity='$timestamp', lastolupdate='$lastolupdate', seccode='$seccode', fid='$fid', tid='$tid', bloguid='$bloguid' $pageviewsadd WHERE sid='$sid'");
        } else {
                $ips = explode('.', $onlineip);

                $db->query("DELETE FROM {$tablepre}sessions WHERE sid='$sid' OR lastactivity<($timestamp-$onlinehold) OR ('$discuz_uid'<>'0' AND uid='$discuz_uid') OR (uid='0' AND ip1='$ips[0]' AND ip2='$ips[1]' AND ip3='$ips[2]' AND ip4='$ips[3]' AND lastactivity>$timestamp-60)");
                $db->query("INSERT INTO {$tablepre}sessions (sid, ip1, ip2, ip3, ip4, uid, username, groupid, styleid, invisible, action, lastactivity, lastolupdate, seccode, fid, tid, bloguid)
                        VALUES ('$sid', '$ips[0]', '$ips[1]', '$ips[2]', '$ips[3]', '$discuz_uid', '$discuz_user', '$groupid', '$styleid', '$invisible', '$discuz_action', '$timestamp', '$lastolupdate', '$seccode', '$fid', '$tid', '$bloguid')", 'SILENT');
                if($discuz_uid && $timestamp - $lastactivity > 21600) {
                        if($oltimespan && $timestamp - $lastactivity > 86400) {
                                $query = $db->query("SELECT total FROM {$tablepre}onlinetime WHERE uid='$discuz_uid'");
                                $oltimeadd = ', oltime='.round(intval($db->result($query, 0)) / 60);
                        } else {
                                $oltimeadd = '';
                        }
                        $db->query("UPDATE {$tablepre}members SET lastip='$onlineip', lastvisit=lastactivity, lastactivity='$timestamp' $oltimeadd WHERE uid='$discuz_uid'", 'UNBUFFERED');
                }
        }

        $sessionupdated = 1;
}
[code][/code]
discuz源代码分析  笨才  发布于2007-05-28 11:58:49

CODE:

/**
* 更新版主工作统计的
* @para string $modaction 进行的何种操作(高亮,关闭,提升…)
* @para int $posts 一次进行了的个数
*
*/
function updatemodworks($modaction, $posts = 1) {
        global $modworkstatus, $db, $tablepre, $discuz_uid, $timestamp, $_DCACHE;
        $today = gmdate('Y-m-d', $timestamp + $_DCACHE['settings']['timeoffset'] * 3600);
        if($modworkstatus && $modaction && $posts) {
                $db->query("UPDATE {$tablepre}modworks SET count=count+1, posts=posts+'$posts' WHERE uid='$discuz_uid' AND modaction='$modaction' AND dateline='$today'");
                if(!$db->affected_rows()) {
                        $db->query("INSERT INTO {$tablepre}modworks (uid, modaction, dateline, count, posts) VALUES ('$discuz_uid', '$modaction', '$today', 1, '$posts')");
                }
        }
}

CODE:

/**
* 写入系统日志
* @para string $file 写入的日志文件
* @para string $log 要写入的内容提要

*/
function writelog($file, $log) {
        global $timestamp, $_DCACHE;
        $yearmonth = gmdate('Ym', $timestamp + $_DCACHE['settings']['timeoffset'] * 3600);
        $logdir = DISCUZ_ROOT.'./forumdata/logs/';
        $logfile = $logdir.$yearmonth.'_'.$file.'.php';
        if(@filesize($logfile) > 2048000) {
                $dir = opendir($logdir);
                $length = strlen($file);
                $maxid = $id = 0;
                while($entry = readdir($dir)) {
                        if(strexists($entry, $yearmonth.'_'.$file)) {
                                $id = intval(substr($entry, $length + 8, -4));
                                $id > $maxid && $maxid = $id;
                        }
                }
                closedir($dir);

                $logfilebak = $logdir.$yearmonth.'_'.$file.'_'.($maxid + 1).'.php';
                @rename($logfile, $logfilebak);
        }
        if($fp = @fopen($logfile, 'a')) {
                @flock($fp, 2);
                $log = is_array($log) ? $log : array($log);
                foreach($log as $tmp) {
                        fwrite($fp, "<?PHP exit;?>/t".str_replace(array('<?', '?>'), '', $tmp)."/n");
                }
                fclose($fp);
        }
}

CODE:

/**
* 看成函数的重载,即把implode这个函数重载成在两边加上'(单引号)的函数
* @para array $array
*
* @return string
*/
function implodeids($array) {
        if(!empty($array)) {
                return "'".implode("','", is_array($array) ? $array : array($array))."'";
        } else {
                return '';
        }
}

CODE:

/**
* 把这三个函数放一起,因为它们三个是一起实现的,即传说中的ajax…
* 这里说一下ajax的实现方式,一种方法是用XMLHttpRequest异步提交给服务器,服务器返回一个带有数据结点的xml文件
* javascript或者php等解析这个xml文档,从而得到数据;另外一种是这个xml文件中含一个CDATA,把已经用HTML格式化好的
* 内容返回过来直接用,显然第二种好用,Discuz用的就是这样一个方式。其他用iframe等等就不在这里介绍了,那个一般
* 在提交数据检查、异步文件上传的时候用。
*/
//ajaxshowheader生成一个xml头和一个<root>根以及一个CDATA
function ajaxshowheader() {
        global $charset;
        @header("Expires: -1");
        @header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE);
        @header("Pragma: no-cache");
        header("Content-type: application/xml");
        echo "<?xml version=/"1.0/" encoding=/"$charset/"?>/n<root><![CDATA[";
}

//闭合CDATA和root
function ajaxshowfooter() {
        echo ']]></root>';
}

//ajaxtemplate组合了ajaxshowheader + 模板 + ajaxshowfooter,使之完整。
function ajaxtemplate($tplname) {
        if(!empty($_GET['inajax'])) {
                extract($GLOBALS);
                updatesession();
                ajaxshowheader();
                include template($tplname);
                ajaxshowfooter();
                die();
        }
}

CODE:

/**
* 过滤.., /n, /r 用的
* @para string $str 要过滤的string

* @return string
*/
function wipespecial($str) {
        return str_replace(array('..', "/n", "/r"), array('', '', ''), $str);
}

CODE:

/**
* supser site 数据库连接
* 通过$super['db']来用,而Discuz本身的是$db
*/
function supe_dbconnect() {
        global $supe, $db;
        if(empty($supe['dbmode'])) {
                $supe['db'] = $db;
        } elseif(empty($supe['db'])) {
                $supe['db'] = new dbstuff;
                $supe['db']->connect($supe['dbhost'], $supe['dbuser'], $supe['dbpw'], $supe['dbname'], $supe['pconnect']);
        }
}

discuz源代码分析  笨才  发布于2007-05-28 12:02:11
Discuz!源代码分析系列--./include/misc.func.php

CODE:

//同样,防止非法引用用的。
if(!defined('IN_DISCUZ')) {
        exit('Access Denied');
}

CODE:

/**
* 这个函数看着很长,其实要实现的功能很少,就是通过ip地址返回一个对应的地理位置
* @para string $ip //给定的ip,要符合点分十进制

* @return string 
*/
function convertip($ip) {
        if(!preg_match("/^/d{1,3}/./d{1,3}/./d{1,3}/./d{1,3}$/", $ip)) {
                return '';
        }

        if($fd = @fopen(DISCUZ_ROOT.'./ipdata/wry.dat', 'rb')) {

                $ip = explode('.', $ip);
                $ipNum = $ip[0] * 16777216 + $ip[1] * 65536 + $ip[2] * 256 + $ip[3];

                $DataBegin = fread($fd, 4);
                $DataEnd = fread($fd, 4);
                $ipbegin = implode('', unpack('L', $DataBegin));
                if($ipbegin < 0) $ipbegin += pow(2, 32);
                $ipend = implode('', unpack('L', $DataEnd));
                if($ipend < 0) $ipend += pow(2, 32);
                $ipAllNum = ($ipend - $ipbegin) / 7 + 1;

                $BeginNum = 0;
                $EndNum = $ipAllNum;

                while($ip1num > $ipNum || $ip2num < $ipNum) {
                        $Middle= intval(($EndNum + $BeginNum) / 2);

                        fseek($fd, $ipbegin + 7 * $Middle);
                        $ipData1 = fread($fd, 4);
                        if(strlen($ipData1) < 4) {
                                fclose($fd);
                                return '- System Error';
                        }
                        $ip1num = implode('', unpack('L', $ipData1));
                        if($ip1num < 0) $ip1num += pow(2, 32);

                        if($ip1num > $ipNum) {
                                $EndNum = $Middle;
                                continue;
                        }

                        $DataSeek = fread($fd, 3);
                        if(strlen($DataSeek) < 3) {
                                fclose($fd);
                                return '- System Error';
                        }
                        $DataSeek = implode('', unpack('L', $DataSeek.chr(0)));
                        fseek($fd, $DataSeek);
                        $ipData2 = fread($fd, 4);
                        if(strlen($ipData2) < 4) {
                                fclose($fd);
                                return '- System Error';
                        }
                        $ip2num = implode('', unpack('L', $ipData2));
                        if($ip2num < 0) $ip2num += pow(2, 32);

                        if($ip2num < $ipNum) {
                                if($Middle == $BeginNum) {
                                        fclose($fd);
                                        return '- Unknown';
                                }
                                $BeginNum = $Middle;
                        }
                }

                $ipFlag = fread($fd, 1);
                if($ipFlag == chr(1)) {
                        $ipSeek = fread($fd, 3);
                        if(strlen($ipSeek) < 3) {
                                fclose($fd);
                                return '- System Error';
                        }
                        $ipSeek = implode('', unpack('L', $ipSeek.chr(0)));
                        fseek($fd, $ipSeek);
                        $ipFlag = fread($fd, 1);
                }

                if($ipFlag == chr(2)) {
                        $AddrSeek = fread($fd, 3);
                        if(strlen($AddrSeek) < 3) {
                                fclose($fd);
                                return '- System Error';
                        }
                        $ipFlag = fread($fd, 1);
                        if($ipFlag == chr(2)) {
                                $AddrSeek2 = fread($fd, 3);
                                if(strlen($AddrSeek2) < 3) {
                                        fclose($fd);
                                        return '- System Error';
                                }
                                $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
                                fseek($fd, $AddrSeek2);
                        } else {
                                fseek($fd, -1, SEEK_CUR);
                        }

                        while(($char = fread($fd, 1)) != chr(0))
                                $ipAddr2 .= $char;

                        $AddrSeek = implode('', unpack('L', $AddrSeek.chr(0)));
                        fseek($fd, $AddrSeek);

                        while(($char = fread($fd, 1)) != chr(0))
                                $ipAddr1 .= $char;
                } else {
                        fseek($fd, -1, SEEK_CUR);
                        while(($char = fread($fd, 1)) != chr(0))
                                $ipAddr1 .= $char;

                        $ipFlag = fread($fd, 1);
                        if($ipFlag == chr(2)) {
                                $AddrSeek2 = fread($fd, 3);
                                if(strlen($AddrSeek2) < 3) {
                                        fclose($fd);
                                        return '- System Error';
                                }
                                $AddrSeek2 = implode('', unpack('L', $AddrSeek2.chr(0)));
                                fseek($fd, $AddrSeek2);
                        } else {
                                fseek($fd, -1, SEEK_CUR);
                        }
                        while(($char = fread($fd, 1)) != chr(0))
                                $ipAddr2 .= $char;
                }
                fclose($fd);

                if(preg_match('/http/i', $ipAddr2)) {
                        $ipAddr2 = '';
                }
                $ipaddr = "$ipAddr1 $ipAddr2";
                $ipaddr = preg_replace('/CZ88/.NET/is', '', $ipaddr);
                $ipaddr = preg_replace('/^/s*/is', '', $ipaddr);
                $ipaddr = preg_replace('//s*$/is', '', $ipaddr);
                if(preg_match('/http/i', $ipaddr) || $ipaddr == '') {
                        $ipaddr = '- Unknown';
                }

                return '- '.$ipaddr;

        } else {

                $datadir = DISCUZ_ROOT.'./ipdata/';
                $ip_detail = explode('.', $ip);
                if(file_exists($datadir.$ip_detail[0].'.txt')) {
                        $ip_fdata = @fopen($datadir.$ip_detail[0].'.txt', 'r');
                } else {
                        if(!($ip_fdata = @fopen($datadir.'0.txt', 'r'))) {
                                return '- Invalid IP data file';
                        }
                }
                for($i = 0; $i <= 3; $i++) {
                        $ip_detail[$i] = sprintf('%03d', $ip_detail[$i]);
                }
                $ip = join('.', $ip_detail);
                do {
                        $ip_data = fgets($ip_fdata, 200);
                        $ip_data_detail = explode('|', $ip_data);
                        if($ip >= $ip_data_detail[0] && $ip <= $ip_data_detail[1]) {
                                fclose($ip_fdata);
                                return '- '.$ip_data_detail[2].$ip_data_detail[3];
                        }
                } while(!feof($ip_fdata));
                fclose($ip_fdata);
                return '- UNKNOWN';

        }

}

CODE:

/**
* 目前发现这个函数在两个地方用到,search.php和digest.php,这个函数的用处是把从数据库中得到的帖子处理一下,然后给前端显示。
* @para array $thread //从数据库中取出的帖子原始数据

* @return array
*/
function procthread($thread) {
        global $dateformat, $timeformat, $timeoffset, $ppp, $colorarray;

        if(empty($colorarray)) {
                $colorarray = array('', 'red', 'orange', 'yellow', 'green', 'cyan', 'blue', 'purple', 'gray');
        }

        $thread['forumname'] = $GLOBALS['_DCACHE']['forums'][$thread['fid']]['name'];
        $thread['dateline'] = gmdate($dateformat, $thread['dateline'] + $timeoffset * 3600);
        $thread['lastpost'] = gmdate("$dateformat $timeformat", $thread['lastpost'] + $timeoffset * 3600);
        $thread['lastposterenc'] = rawurlencode($thread['lastposter']);

        if($thread['replies'] > $thread['views']) {
                $thread['views'] = $thread['replies'];
        }

        $postsnum = $thread['replies'] + 1;
        $pagelinks = '';
        if($postsnum  > $ppp) {
                $posts = $postsnum;
                $topicpages = ceil($posts / $ppp);
                for($i = 1; $i <= $topicpages; $i++) {
                        $pagelinks .= '<a href="viewthread.php?tid='.$thread['tid'].'&page='.$i.'" target="_blank">'.$i.'</a> ';
                        if($i == 6) {
                                $i = $topicpages + 1;
                        }
                }
                if($topicpages > 6) {
                        $pagelinks .= ' .. <a href="viewthread.php?tid='.$thread['tid'].'&page='.$topicpages.'" target="_blank">'.$topicpages.'</a> ';
                }
                $thread['multipage'] = '   ( <img src="'.IMGDIR.'/multipage.gif" class="absmiddle" border="0" alt="" /> '.$pagelinks.')';
        } else {
                $thread['multipage'] = '';
        }

        if($thread['highlight']) {
                $string = sprintf('%02d', $thread['highlight']);
                $stylestr = sprintf('%03b', $string[0]);

                $thread['highlight'] = 'style="';
                $thread['highlight'] .= $stylestr[0] ? 'font-weight: bold;' : '';
                $thread['highlight'] .= $stylestr[1] ? 'font-style: italic;' : '';
                $thread['highlight'] .= $stylestr[2] ? 'text-decoration: underline;' : '';
                $thread['highlight'] .= $string[1] ? 'color: '.$colorarray[$string[1]] : '';
                $thread['highlight'] .= '"';
        } else {
                $thread['highlight'] = '';
        }

        if($thread['attachment']) {
                require_once DISCUZ_ROOT.'./include/attachment.func.php';
                $thread['attachment'] = attachtype($thread['attachment']).' ';
        } else {
                $thread['attachment'] = '';
        }

        return $thread;
}

CODE:

/**
* 用来更新点击数的
* @para string $table //用来更新的数据表
* @para string $idcol //用来更新的数据的id
* @para string $viewscol //用来更新的列
* @para string $logfile //日志文件,点击是写入日志的
*/
function updateviews($table, $idcol, $viewscol, $logfile) {
        global $db, $tablepre;

        $viewlog = $viewarray = array();
        if(@$viewlog = file($logfile = DISCUZ_ROOT.$logfile)) {
                @unlink($logfile);
                $viewlog = array_count_values($viewlog);
                foreach($viewlog as $id => $views) {
                        $viewarray[$views] .= ($id > 0) ? ','.intval($id) : '';
                }
                foreach($viewarray as $views => $ids) {
                        $db->query("UPDATE $tablepre$table SET $viewscol=$viewscol+$views WHERE $idcol IN (0$ids)", 'UNBUFFERED');
                }
        }
}

CODE:

/**
* 给版主的工作写入日志的
* @para array $thread //对哪个帖子的操作
* @para string $action //进行的什么操作
*
*/
function modlog($thread, $action) {
        global $discuz_user, $adminid, $onlineip, $timestamp, $forum, $reason;
        writelog('modslog', dhtmlspecialchars("$timestamp/t$discuz_user/t$adminid/t$onlineip/t$forum[fid]/t$forum[name]/t$thread[tid]/t$thread[subject]/t$action/t$reason"));
}

CODE:

/**
* 检查是不是写了理由

*/
function checkreasonpm() {
        global $reason;
        $reason = trim(strip_tags($reason));
        if(($GLOBALS['reasonpm'] == 1 || $GLOBALS['reasonpm'] == 3) && !$reason) {
                showmessage('admin_reason_invalid');
        }
}

CODE:

/**
* 发送操作理由报告给帖子作者
* @para array $var
* @para string $item
*/
function sendreasonpm($var, $item) {
        global $$var;
        ${$var}['subject'] = strtr(${$var}['subject'], array_flip(get_html_translation_table(HTML_ENTITIES)));
        ${$var}['dateline'] = gmdate($GLOBALS['_DCACHE']['settings']['dateformat'].' '.$GLOBALS['_DCACHE']['settings']['timeformat'], ${$var}['dateline'] + ($GLOBALS['timeoffset'] * 3600));
        sendpm(${$var}['authorid'], $item.'_subject', $item.'_message');
}

CODE:

/**
* 得到一个下拉列表,这个列表是版主操作理由选择

* @return string $select 
*/
function modreasonselect() {
        global $_DCACHE;
        if(!isset($_DCACHE['modreasons']) || !is_array($_DCACHE['modreasons'])) {
                @include DISCUZ_ROOT.'./forumdata/cache/cache_topicadmin.php';
        }
        $select = '';
        foreach($_DCACHE['modreasons'] as $reason) {
                $select .= $reason ? '<option value="'.dhtmlspecialchars($reason).'">'.$reason.'</option>' : '<option value="">--------</option>';
        }
        return $select;
}

CODE:

/**
* 登陆检查,看看是不是在十五分钟已经登陆五次了
*
* @return int
*/
function logincheck() {
        global $db, $tablepre, $onlineip, $timestamp;
        $query = $db->query("SELECT count, lastupdate FROM {$tablepre}failedlogins WHERE ip='$onlineip'");
        if($login = $db->fetch_array($query)) {
                if($timestamp - $login['lastupdate'] > 900) {
                        return 3;
                } elseif($login['count'] < 5) {
                        return 2;
                } else {
                        return 0;
                }
        } else {
                return 1;
        }
}

CODE:

/**
* 和上面的那个函数(logincheck)是配对的,看是哪种方式登陆的
* @para int $permission

*/
function loginfailed($permission) {
        global $db, $tablepre, $onlineip, $timestamp;
        switch($permission) {
                case 1:        $db->query("REPLACE INTO {$tablepre}failedlogins (ip, count, lastupdate) VALUES ('$onlineip', '1', '$timestamp')");
                        break;
                case 2: $db->query("UPDATE {$tablepre}failedlogins SET count=count+1, lastupdate='$timestamp' WHERE ip='$onlineip'");
                        break;
                case 3: $db->query("UPDATE {$tablepre}failedlogins SET count='1', lastupdate='$timestamp' WHERE ip='$onlineip'");
                        $db->query("DELETE FROM {$tablepre}failedlogins WHERE lastupdate<$timestamp-901", 'UNBUFFERED');
                        break;
        }
}

discuz源代码分析  笨才  发布于2007-05-28 12:07:07
Discuz!源代码分析系列--./include/template.func.php(模板)
本帖不仅仅是分析文件,还把Discuz中模板解析这一原理分析了一下。

我记得我刚开始学PHP的时候,对模板解析实在是觉得很奇怪,不知道这个原理怎么实现的,后来看书看多了也明白有一个著名的Smarty在那,曾经也用过 一段,不过感觉不是很好,就开始分析Discuz的模板技术是怎么实现的了,然后我把这个模板解析的代码分离出来了,觉得很好用,用了一段时间, Discuz的模板解析是用正则表达式替换一些模板中的规定的语言标记,然后呢,写到forumdata/templates中,再用include引用 到index, forumdisplay等等中,和smarty的原理基本上相同,只是功能没有smarty那么多,smarty内建了一个cache来着…连个 User Guide都几百页…
  呵呵,不过现在基本上两个都没 用过,正则表达式好是好用,不过正则的效率不是很高,以前看PHP技术文档的时候说能不用正则就尽量不要用,那东西烦着,现在做什么项目基本上用的是框 架,MVC的模式,View中的代码一般不用模板解析出来,混杂php代码在里面,也觉得不错,OOP的开发效率比较高,很多地方重用代码是很开心的~!

  Discuz的模板解析要分析出来只要用到两个文件:./include/global.func.php和. /include/template.func.php,global只要一个函数就够了,template要全部的文件下面我就分开讲一下,会比较详 细,大家耐心看:

Section One--./include/global.func.php---->template function

CODE:

function template($file, $templateid = 0, $tpldir = '') {
        global $tplrefresh;

        $tpldir = $tpldir ? $tpldir : TPLDIR;
        $templateid = $templateid ? $templateid : TEMPLATEID;

        $tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';
        $objfile = DISCUZ_ROOT.'./forumdata/templates/'.$templateid.'_'.$file.'.tpl.php';
        if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) {
                return template($file, 1, './templates/default/');
        }
        if($tplrefresh == 1 || ($tplrefresh > 1 && substr($GLOBALS['timestamp'], -1) > $tplrefresh)) {
                if(@filemtime($tplfile) > @filemtime($objfile)) {
                        require_once DISCUZ_ROOT.'./include/template.func.php';
                        parse_template($file, $templateid, $tpldir);
                }
        }
        return $objfile;
}
这个函数一共有三个传入参数:
$file 表示模板名
$templateid 表示模板id
$tpldir 表示模板目录

QUOTE:

CODE:

global $tplrefresh;这个是把$tplrefresh作为一个全局变量,tplrefresh表示是不是刷新模板

QUOTE:

CODE:

        $tpldir = $tpldir ? $tpldir : TPLDIR;
        $templateid = $templateid ? $templateid : TEMPLATEID;
给$tpldir和$templateid赋值,如果没有传进来就用常量TPLDIR和TEMPLATEID给它们值

QUOTE:

CODE:

        $tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm';
        $objfile = DISCUZ_ROOT.'./forumdata/templates/'.$templateid.'_'.$file.'.tpl.php';
这里是把$tplfile(表示的是模板文件)和$objfile(表示的是要编译成的文件)赋值

QUOTE:

CODE:

        if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) {
                return template($file, 1, './templates/default/');
        }
防止TEMPLATEID不等于1且$templateid不等于1,而且模板文件不存在导致空白问题。
这里也可以算一个迭代,也可以不算,就是把1作为第二个参数再调用函数本身。

QUOTE:

CODE:

        if($tplrefresh == 1 || ($tplrefresh > 1 && substr($GLOBALS['timestamp'], -1) > $tplrefresh)) {
                if(@filemtime($tplfile) > @filemtime($objfile)) {
                        require_once DISCUZ_ROOT.'./include/template.func.php';
                        parse_template($file, $templateid, $tpldir);
                }
        }
        return $objfile;
很 巧妙的一段,Discuz的模板缓存就体现在这里,如果你没打开模板刷新的话(config.inc.php->$tplrefresh=0),这 里就直接返回一个$objfile了,而这个文件是你第一次建论坛的时候就写入的,如果你不改模板的话,关了是能提高相当一部分效率的!反之,如果你打开 了模板刷新的话就接着判断是不是模板文件的建立时间大于forumdata/templates下的文件,是的话就引用. /include/template.func.php写入模板文件到forumdata/templates中,否则的话还是直接返回一个已经编译好的 模板文件。

discuz源代码分析  笨才  发布于2007-05-28 12:08:37
关于template.func.php文件中函数的分析在下面:

CODE:

//防止非法引用
if(!defined('IN_DISCUZ')) {
        exit('Access Denied');
}

CODE:

/**
* 这个是关键模板解析函数,通过正则表达式来替换掉模板中的相关语句,使之变成标准的php语句,写入缓存(./forumdata/template),从而include到页面中显示出来
* @para string $file //解析的模板名,只要文件名就可以了,会自动加上htm,如果有缓存的话就变成"模板id_文件名.tpl.php"
* @para int $templateid //模板的id
* @para string $tpldir //模板所在的目录
*
*/

function parse_template($file, $templateid, $tpldir) {
        global $language;

        $nest = 5;
        $tplfile = DISCUZ_ROOT."./$tpldir/$file.htm";
        $objfile = DISCUZ_ROOT."./forumdata/templates/{$templateid}_$file.tpl.php";

        if(!@$fp = fopen($tplfile, 'r')) {
                dexit("Current template file './$tpldir/$file.htm' not found or have no access!");
        } elseif(!include_once language('templates', $templateid, $tpldir)) {
                dexit("<br>Current template pack do not have a necessary language file 'templates.lang.php' or have syntax error!");
        }

        $template = fread($fp, filesize($tplfile));
        fclose($fp);

        $var_regexp = "((///$[a-zA-Z_/x7f-/xff][a-zA-Z0-9_/x7f-/xff]*)(/[[a-zA-Z0-9_/-/./"/'/[/]/$/x7f-/xff]+/])*)";
        $const_regexp = "([a-zA-Z_/x7f-/xff][a-zA-Z0-9_/x7f-/xff]*)";

        $template = preg_replace("/([/n/r]+)/t+/s", "//1", $template);
        $template = preg_replace("//</!/-/-/{(.+?)/}/-/-/>/s", "{//1}", $template);
        $template = preg_replace("//{lang/s+(.+?)/}/ies", "languagevar('//1')", $template);
        $template = preg_replace("//{faq/s+(.+?)/}/ies", "faqvar('//1')", $template);
        $template = str_replace("{LF}", "<?=/"//n/"?>", $template);

        $template = preg_replace("//{(///$[a-zA-Z0-9_/[/]/'/"/$/./x7f-/xff]+)/}/s", "<?=//1?>", $template);
        $template = preg_replace("/$var_regexp/es", "addquote('<?=//1?>')", $template);
        $template = preg_replace("//</?/=/</?/=$var_regexp/?/>/?/>/es", "addquote('<?=//1?>')", $template);

        $template = "<? if(!defined('IN_DISCUZ')) exit('Access Denied'); ?>/n$template";
        $template = preg_replace("/[/n/r/t]*/{template/s+([a-z0-9_]+)/}[/n/r/t]*/is", "/n<? include template('//1'); ?>/n", $template);
        $template = preg_replace("/[/n/r/t]*/{template/s+(.+?)/}[/n/r/t]*/is", "/n<? include template(//1); ?>/n", $template);
        $template = preg_replace("/[/n/r/t]*/{eval/s+(.+?)/}[/n/r/t]*/ies", "stripvtags('/n<? //1 ?>/n','')", $template);
        $template = preg_replace("/[/n/r/t]*/{echo/s+(.+?)/}[/n/r/t]*/ies", "stripvtags('/n<? echo //1; ?>/n','')", $template);
        $template = preg_replace("/[/n/r/t]*/{elseif/s+(.+?)/}[/n/r/t]*/ies", "stripvtags('/n<? } elseif(//1) { ?>/n','')", $template);
        $template = preg_replace("/[/n/r/t]*/{else/}[/n/r/t]*/is", "/n<? } else { ?>/n", $template);

        for($i = 0; $i < $nest; $i++) {
                $template = preg_replace("/[/n/r/t]*/{loop/s+(/S+)/s+(/S+)/}[/n/r]*(.+?)[/n/r]*/{//loop/}[/n/r/t]*/ies", "stripvtags('/n<? if(is_array(//1)) { foreach(//1 as //2) { ?>','/n//3/n<? } } ?>/n')", $template);
                $template = preg_replace("/[/n/r/t]*/{loop/s+(/S+)/s+(/S+)/s+(/S+)/}[/n/r/t]*(.+?)[/n/r/t]*/{//loop/}[/n/r/t]*/ies", "stripvtags('/n<? if(is_array(//1)) { foreach(//1 as //2 => //3) { ?>','/n//4/n<? } } ?>/n')", $template);
                $template = preg_replace("/[/n/r/t]*/{if/s+(.+?)/}[/n/r]*(.+?)[/n/r]*/{//if/}[/n/r/t]*/ies", "stripvtags('/n<? if(//1) { ?>','/n//2/n<? } ?>/n')", $template);
        }

        $template = preg_replace("//{$const_regexp/}/s", "<?=//1?>", $template);
        $template = preg_replace("/ /?/>[/n/r]*/</? /s", " ", $template);

        if(!@$fp = fopen($objfile, 'w')) {
                dexit("Directory './forumdata/templates/' not found or have no access!");
        }

        $template = preg_replace("//"(http)?[/w/.//:]+/?[^/"]+?&[^/"]+?/"/e", "transamp('//0')", $template);
        $template = preg_replace("//<script[^/>]*?src=/"(.+?)/".*?/>/s*/<//script/>/ise", "stripscriptamp('//1')", $template);

        flock($fp, 2);
        fwrite($fp, $template);
        fclose($fp);
}

CODE:

/**
* 几个替换,过滤掉一些东西
* @para string $str

* @return string
*/
        
function transamp($str) {
        $str = str_replace('&', '&', $str);
        $str = str_replace('&', '&', $str);
        $str = str_replace('/"', '"', $str);
        return $str;
}

CODE:

/**
* 从正则表达式来看是给ubb代码去掉一个/符号的,应该是为安全性着想的
* @para string $val
*
* @return string
*/

function addquote($var) {
        return str_replace("///"", "/"", preg_replace("//[([a-zA-Z0-9_/-/./x7f-/xff]+)/]/s", "['//1']", $var));
}

CODE:

/**
* 把一个字符用语言包中的来替换,没有找到的话就用!string来替换
* @para string $val
*
* @return string
*/

function languagevar($var) {
        if(isset($GLOBALS['language'][$var])) {
                return $GLOBALS['language'][$var];
        } else {
                return "!$var!";
        }
}

CODE:

/**
* FAQ中把id和关建字用一个有效的链接来替换
* @para array $val
*
* @return string
*/

function faqvar($var) {
        global $_DCACHE;
        include_once DISCUZ_ROOT.'./forumdata/cache/cache_faqs.php';

        if(isset($_DCACHE['faqs'][$var])) {
                return '<a href="faq.php?action=message&id='.$_DCACHE['faqs'][$var]['id'].'" target="_blank">'.$_DCACHE['faqs'][$var]['keyword'].'</a>';
        } else {
                return "!$var!";
        }
}

CODE:

/**
* 去掉自定义的一些tag
* @para string $expr
* @para string $statement
* @return string
*/

function stripvtags($expr, $statement) {
        $expr = str_replace("///"", "/"", preg_replace("//</?/=(///$.+?)/?/>/s", "//1", $expr));
        $statement = str_replace("///"", "/"", $statement);
        return $expr.$statement;
}

CODE:

/**
* 作用是把&符号的html编码形式换成&,然后换成javascript引用的形式。
* @para string $s
*
* @return string
*/

function stripscriptamp($s) {
        $s = str_replace('&', '&', $s);
        return "<script src=/"$s/" type=/"text/javascript/"></script>";
}

discuz源代码分析  笨才  发布于2007-05-28 12:13:35
Discuz!5.5源代码分析系列-AJAX工作原理,底层代码分析
Discuz的ajax原理实际上是很简单的,当然,说到ajax肯定是用XMLHttpRequest这个对象了,不过我曾经也看到国外的牛人写过一篇 文章叫做不用XMLHttpRequest对象来实现ajax,而且还解决了跨域的问题,真是大开了眼界~!好了,说正事,Dz的ajax用到的文件不是 很多,列举如下:

QUOTE:

./include/javascript/common.js  
这个文件把Discuz用到的许多javascript的代码(主要为函数和浏览者的浏览器的判断等等)

QUOTE:

./include/javascript/ajax.js 
不用看就知道是 创建一个可用的XMLHttpRequest对象用的(由于XMLHttpRequest在各个浏览器的创建不同,因此要对各个不同的浏览器进行不同的创 建,还好prototype.js中有一个通用的东东。当然,这里也封装了get, post这类的函数,刚发现5.5封装了更多的东西,有ajaxmenu,updatesecqaa,ignorepm。

QUOTE:

./ajax.php
这个文件是ajax的后台处理文件,作用相当于MVC三层中的M(Model)和C(Controller)层,因为它负责和后台数据库通信并返回和处理 一些信息,比如新的用户在注册的时候通过XMLHttpRequest向ajax.php发送一个请求,这个请求是通过带参数用GET发出去, ajax.php检查数据库中用户名是不是存在,并用global.func.php中定义的ajaxtemplate调用 showmessage_ajax模板返回一个XML文档,ajax这个对象实际上是解析这个xml文档的,具体的解析就是返回root这个元素中的 CDATA中的值,再用register.htm中的javascript调用div对象的innerhtml方法给login模板中的一个div给前端 用户提示用户是不是存在。

工作原理如下图:

discuz源代码分析
20070509_09a536dc97a8d5c6215cNkmJZoIeTid8.jpg


discuz源代码分析  笨才  发布于2007-05-28 12:16:47
Section I 部分common.js函数分析
这里用到ajax部分的不是很多,我只把几个核心的弄出来说一下,要不然工程太大了。。

QUOTE:

CODE:

var userAgent = navigator.userAgent.toLowerCase();
var is_webtv = userAgent.indexOf('webtv') != -1;
var is_kon = userAgent.indexOf('konqueror') != -1;
var is_mac = userAgent.indexOf('mac') != -1;
var is_saf = userAgent.indexOf('applewebkit') != -1 || navigator.vendor == 'Apple Computer, Inc.';
var is_opera = userAgent.indexOf('opera') != -1 && opera.version();
var is_moz = (navigator.product == 'Gecko' && !is_saf) && userAgent.substr(userAgent.indexOf('firefox') + 8, 3);
var is_ns = userAgent.indexOf('compatible') == -1 && userAgent.indexOf('mozilla') != -1 && !is_opera && !is_webtv && !is_saf;
var is_ie = (userAgent.indexOf('msie') != -1 && !is_opera && !is_saf && !is_webtv) && userAgent.substr(userAgent.indexOf('msie') + 5, 3);
这一部分是用来判断来访者的浏览器类型的,很重要的一部分判断。对于构建跨平台的显示效果有至关重要的作用~!

QUOTE:

CODE:

function in_array(needle, haystack) {
        if(typeof needle == 'string') {
                for(var i in haystack) {
                        if(haystack[i] == needle) {
                                        return true;
                        }
                }
        }
        return false;
}
这个函数可以算一个php函数用到了javascript上,作用就是检查是不是needle在haystack这个数组中。

QUOTE:

CODE:

function arraypush(a, value) {
        a[a.length] = value;
        return a.length;
}
同样可以算一个php函数用到了javascript上,作用是把value这个值插到a这个数组的最后一位。

discuz源代码分析  笨才  发布于2007-05-28 12:19:48
./include/javascript/ajax.js 分析

QUOTE:

CODE:

var Ajaxs = new Array();
function Ajax(recvType, statusId) {
        var aj = new Object();
        aj.statusId = statusId ? document.getElementById(statusId) : null;
        aj.targetUrl = '';
        aj.sendString = '';
        aj.recvType = recvType ? recvType : 'XML';
        aj.resultHandle = null;

        aj.createXMLHttpRequest = function() {
                var request = false;
                if(window.XMLHttpRequest) {
                        request = new XMLHttpRequest();
                        if(request.overrideMimeType) {
                                request.overrideMimeType('text/xml');
                        }
                } else if(window.ActiveXObject) {
                        var versions = ['Microsoft.XMLHTTP', 'MSXML.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.7.0', 'Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
                        for(var i=0; i<versions.length; i++) {
                                try {
                                        request = new ActiveXObject(versions[i]);
                                        if(request) {
                                                return request;
                                        }
                                } catch(e) {
                                        //alert(e.message);
                                }
                        }
                }
                return request;
        }

        aj.XMLHttpRequest = aj.createXMLHttpRequest();
这一段是想尽 一切办法建立一个XMLHttpRequest对象,无论是什么浏览器都能通用了。调用的时候是一个函数Ajax,有两个传入函数recvType和 statusId,recvType是ajax返回值的接受类型,有HTML和XML两种类型,Dz一般用的是XML类型;statusID这个是用来指 示状态的div。

QUOTE:

CODE:

        aj.processHandle = function() {
                if(aj.statusId) {
                        aj.statusId.style.display = '';
                }
                if(aj.XMLHttpRequest.readyState == 1 && aj.statusId) {
                        aj.statusId.innerHTML = xml_http_building_link;
                } else if(aj.XMLHttpRequest.readyState == 2 && aj.statusId) {
                        aj.statusId.innerHTML = xml_http_sending;
                } else if(aj.XMLHttpRequest.readyState == 3 && aj.statusId) {
                        aj.statusId.innerHTML = xml_http_loading;
                } else if(aj.XMLHttpRequest.readyState == 4) {
                        if(aj.XMLHttpRequest.status == 200) {
                                for(k in Ajaxs) {
                                        if(Ajaxs[k] == aj.targetUrl) {
                                                Ajaxs[k] = null;
                                        }
                                }

                                if(aj.statusId) {
                                        aj.statusId.innerHTML = xml_http_data_in_processed;
                                        aj.statusId.style.display = 'none';
                                }
                                if(aj.recvType == 'HTML') {
                                        aj.resultHandle(aj.XMLHttpRequest.responseText, aj);
                                } else if(aj.recvType == 'XML') {
                                        aj.resultHandle(aj.XMLHttpRequest.responseXML.lastChild.firstChild.nodeValue, aj);
                                }
                        } else {
                                if(aj.statusId) {
                                        aj.statusId.innerHTML = xml_http_load_failed;
                                }
                        }
                }
        }
Ajax实例化后的对象aj的processHandle方法,作用当然就是处理ajax请求过程的函数。
        具体作用有两点:
        第一点,那就是对statusId这个div进行ajax请求过程全程提示,在这段代码的前三分之一的样子就是做这个用的。
        注意在register.htm中有对过程的定义,以下的代码引用自./templates/default/register.htm

CODE:

                var profile_seccode_invalid = '{lang register_profile_seccode_invalid}';
                var profile_secanswer_invalid = '{lang register_profile_secqaa_invalid}';
                var profile_username_toolong = '{lang register_profile_username_toolong}';
                var profile_username_tooshort = '{lang register_profile_profile_username_tooshort}';
                var profile_username_illegal = '{lang register_profile_username_illegal}';
                var profile_passwd_illegal = '{lang register_profile_passwd_illegal}';
                var profile_passwd_notmatch = '{lang register_profile_passwd_notmatch}';
                var profile_email_illegal = '{lang register_profile_email_illegal}';
                var profile_email_invalid = '{lang register_profile_email_invalid}';
                var profile_email_censor = '{lang register_profile_email_censor}';
                var profile_email_msn = '{lang register_profile_email_msn}';
                var doublee = parseInt('$doublee');
                var lastseccode = lastsecanswer = lastusername = lastpassword = lastemail = '';
                var xml_http_building_link = '{lang xml_http_building_link}';
                var xml_http_sending = '{lang xml_http_sending}';
                var xml_http_loading = '{lang xml_http_loading}';
                var xml_http_load_failed = '{lang xml_http_load_failed}';
                var xml_http_data_in_processed = '{lang xml_http_data_in_processed}';
        
这个便是statusId具体中要提示的文字了,之所以要这样写当然是为了方便多语言。
                
        第二点是最重要的,当XMLHttpRequest.status=200的时候,那么就表示请求成功并返回了东西,这个时候就用 resultHandle这个函数对返回的东西进行处理,可以看到还是分为HTML和XML两种情况分别调用不同的方法,一个是responsText一 个是responseXML。

QUOTE:

CODE:

        aj.get = function(targetUrl, resultHandle) {
                if(in_array(targetUrl, Ajaxs)) {
                        return false;
                } else {
                        arraypush(Ajaxs, targetUrl);
                }
                aj.targetUrl = targetUrl;
                aj.XMLHttpRequest.onreadystatechange = aj.processHandle;
                aj.resultHandle = resultHandle;
                if(window.XMLHttpRequest) {
                        aj.XMLHttpRequest.open('GET', aj.targetUrl);
                        aj.XMLHttpRequest.send(null);
                } else {
                        aj.XMLHttpRequest.open("GET", targetUrl, true);
                        aj.XMLHttpRequest.send();
                }
        }

        aj.post = function(targetUrl, sendString, resultHandle) {
                if(in_array(targetUrl, Ajaxs)) {
                        return false;
                } else {
                        arraypush(Ajaxs, targetUrl);
                }
                aj.targetUrl = targetUrl;
                aj.sendString = sendString;
                aj.XMLHttpRequest.onreadystatechange = aj.processHandle;
                aj.resultHandle = resultHandle;
                aj.XMLHttpRequest.open('POST', targetUrl);
                aj.XMLHttpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                aj.XMLHttpRequest.send(aj.sendString);
        }
        return aj;
}
这里是ajax的两个方法,一个是get,用来提交get数据的,比方说discuz对于注册的时候ajax的表情显示就是get方法;另外一个是post,用来提交post数据。
targetUrl, resultHandle这两个参数分别是要请求的地址和处理函数。

discuz源代码分析  笨才  发布于2007-05-28 12:23:19
有了这些理论的基础就可以分析一下ajax的具体实现了,下面就以注册过程中的检查用户名在数据库是不是存在并给用户提示这样一个ajax过程进行全程分析。
用到如下的几个文件:

QUOTE:

./register.php
./ajax.php
./include/javascript/common.js
./include/javascript/ajax.js
./template/default/register.htm

其实register.php这个文件没什么多大的关系,不过register.htm模板是通过它解析过来的,所以就提出来了。

大家都知道在注册过程中是在输完用户名并输入密码的时候才会触发事件检查是不是用户存在,所以很明显,这个是input的onBlur事件。
好了,现在看看./templates/default/register.htm这个文件。

QUOTE:

CODE:

<tr>
        <td class="altbg1" width="21%"><span class="bold">{lang username}</span></td>
        <td class="altbg2"><div class="input"><input type="text" name="username" size="25" maxlength="15" id="username" onBlur="checkusername()"></div><div id="checkusername"></div>
        </td>
        </tr>
很简单的HTML代码,注意看到 onBlur="checkusername()"这里,继续往下深入,看看这个函数是做什么的。

CODE:

        function checkusername() {
                var username = trim($('username').value);
                if(username == lastusername) {
                        return;
                } else {
                        lastusername = username;
                }
                var cu = $('checkusername');
                var unlen = username.replace(/[^/x00-/xff]/g, "**").length;

                if(unlen < 3 || unlen > 15) {
                        warning(cu, unlen < 3 ? profile_username_tooshort : profile_username_toolong);
                        return;
                }
                ajaxresponse('checkusername', 'action=checkusername&username=' + username);
        }
第一行是得到username的值,也就是我们输入的,lastusername应该是我们在返回的时候不会让用户名消失用的,应该是写入cookie或者是其他。
接下来判断经过了替换的用户名是不是大于15或者小于3,是的话直接调用warning这个函数(稍后讲这样一个函数)并返回,不是的话就调用ajaxresponse函数发出ajax请求,看看ajaxresponse这个函数就是关键所在了。

CODE:

        function ajaxresponse(objname, data) {
                var x = new Ajax('XML', objname);
                x.get('ajax.php?inajax=1&' + data, function(s){
                        var obj = $(objname);
                        if(s == 'succeed') {
                                obj.style.display = '';
                                obj.innerHTML = '<img src="{IMGDIR}/check_right.gif" width="13" height="13">';
                                obj.className = "warning";
                        } else {
                                warning(obj, s);
                        }
                });
        }
ajaxresponse 函数来了,这个作用就是实例化一个ajax对象,注意到我们先前说的recvType,就是第一个传入参数,这里固定成了XML,Discuz喜欢用 XML(^^),然后发出请求,这里全部用的是get(第二行),地址是ajax.php?inajax=1&加上传入的参数,所以结合上面就变 成:ajax.php?inajax=1&action=checkusername&username=用户名,构造出来了一个 URL,(大家可以自己测试一下看看返回的是什么东东,通过http://你的URL/ajax.php?inajax=1&action= checkusername&username=用户名)通过XMLHttpRequest发出去,注意那个处理函数:function(s), 实际这个函数作为参数已经写出来了,如果最后返回的是succed,那么就在obj这个层里(在这个例子中对应id='checkusername'这个 层)显示一个带勾的图,否则的话还是调用warning这个函数。下面就分析这个函数。

CODE:

        function warning(obj, msg) {
                if((ton = obj.id.substr(5, obj.id.length)) != 'password2') {
                        $(ton).select();
                }
                obj.style.display = '';
                obj.innerHTML = '<img src="{IMGDIR}/check_error.gif" width="13" height="13">   ' + msg;
                obj.className = "warning";
        }
warning函数前面两次提到,其实这个函数很简单,实现的作用就是在obj这个层里打一个叉,然后写上错误的原因,把obj这个层的CSS class设置成为warning。当然,最开始也验证了一下两次密码是否一致,这里就不说了。

接下来当然是要分析这个ajax.php是怎么一回事,它做了哪些使function(s)中能返回我们要的东西。由于只分析检查用户名这一个部分,我这里就只分析action=checkuser这一部分了。

QUOTE:

CODE:

elseif($action == 'checkusername') {

                $username = trim($username);

                $guestexp = '/xA1/xA1|^Guest|^/xD3/xCE/xBF/xCD|/xB9/x43/xAB/xC8';
                $censorexp = '/^('.str_replace(array('//*', "/r/n", ' '), array('.*', '|', ''), preg_quote(($censoruser = trim($censoruser)), '/')).')$/i';
                if(preg_match("/^/s*$|^c://con//con$|[%,/*/"/s/t/</>/&]|$guestexp/is", $username) || ($censoruser && @preg_match($censorexp, $username))) {
                        showmessage('profile_username_illegal');
                }

                $query = $db->query("SELECT uid FROM {$tablepre}members WHERE username='$username'");
                $username = dhtmlspecialchars(stripslashes($username));

                if($db->num_rows($query)) {
                        showmessage('register_check_found');
                }
这里可以看到是标准的php判断了,有点点php基础就能看懂了,基本上的功能就是判断一个用户是不是在后台设置的禁用用户名中。
是的话就showmessage不合法(注:这里的showmessage不是我们理解的那个跳转,而是一个xml文档,为什么会这样我等会会介绍)
然后就从数据库找是不是有这样一个用户,如果是的话就showmessage 发现了已注册的用户名,不是话就都跳过,直接到最后的:

CODE:

showmessage('succeed');注意当所有的判断都成功的话就说明合法了,会调用showmessage来显示一个succeed。

QUOTE:

最后说一下为什么这里的showmessage不是我们理解的那个跳转了。

注意在register.htm中的ajaxresponse函数有这样一句:x.get('ajax.php?inajax=1&' + data, function(s){
对了,inajax=1,就是这么一个参数,showmessage就天差万别了。如果你不记得showmessage这个函数是什么样了,请参考:
http://www.discuz.net/viewthread.php?tid=612195

http://www.discuz.net/viewthread.php?tid=612197,我在这里分析了一下。


转自:http://blog.****.net/freefis/article/details/1839545