Discuz!源码分析


http://www.55like.com 网站建设 作者:边城浪子 discuz!源码分析 源代码分析[1] 第一个文件当然是分析./include/common.inc.php 这个文件,这个是 Discuz 的核心中的核心,基本上每次操作都 include 到了这个文件,下面就分七段来分 析这个文件: Section One: 以下内容为程序代码: //定义 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; http://www.55like.com 网站建设 作者:边城浪子 $_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 这样的方式得到,有通用性~! 以下内容为程序代码: require_once DISCUZ_ROOT.'./include/global.func.php'; 把 include/global.inc.php 引用进来,这个文件是 Discuz 的核心函数文件,包含了 Discuz 用到的很多通用的函数,可以说它就是一个大的通用函数库。 以下内容为程序代码: define('ISROBOT', getrobot()); if(defined('NOROBOT') && ISROBOT) { exit(header("HTTP/1.1 403 Forbidden")); } 这里是定义一个 ISROBOT 常量,看看浏览者是什么东东,比方说如果浏览者是一个 robot 那么就直接来一个 403 Forbidden 了…… 以下内容为程序代码: define('MAGIC_QUOTES_GPC', get_magic_quotes_gpc()); isset($_REQUEST['GLOBALS']) && exit('Access Error'); foreach(array('_COOKIE', '_POST', '_GET') as $_request) { foreach($$_request as $_key => $_value) { $_key{0} != '_' && $$_key = daddslashes($_value); } } http://www.55like.com 网站建设 作者:边城浪子 (!MAGIC_QUOTES_GPC) && $_FILES = daddslashes($_FILES); 此处是过滤提交的变量用的,提高安全性的用法。。 以下内容为程序代码: $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 中去。在下面一段会有取出的代码。 以下内容为程序代码: unset($prelength, $_request, $_key, $_value); $timestamp = time(); if($attackevasive) { require_once DISCUZ_ROOT.'./include/security.inc.php'; } 这一部分代码是提高安全用的,防一些非法的入侵,include/security.inc.php 文件中就是这样一些检查。 以下内容为程序代码: http://www.55like.com 网站建设 作者:边城浪子 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 地址, 浏览器类型等等。 以下内容为程序代码: preg_match("/[\d\.]{7,15}/", $onlineip, $onlineipmatches); $onlineip = $onlineipmatches[0] ? $onlineipmatches[0] : 'unknown'; unset($onlineipmatches); 看看 ip 是不是点分段,7-15 个数字之间,用到了一个正则表达式, 以下内容为程序代码: $cachelost = (@include DISCUZ_ROOT.'./forumdata/cache/cache_settings.php') ? '' : 'settings'; http://www.55like.com 网站建设 作者:边城浪子 @extract($_DCACHE['settings']); 这一段是获得./forumdata/cache/cache_settings.php(即缓存下的设置数组,并展开,方面以后的写法 以下内容为程序代码: if($gzipcompress && function_exists('ob_gzhandler') && CURSCRIPT != 'wap') { ob_start('ob_gzhandler'); } else { $gzipcompress = 0; ob_start(); } 检查 gzip 是不是打开了,打开就用 ob_gzhandler,没有就用 ob_start。 以下内容为程序代码: if(!empty($loadctrl) && substr(PHP_OS, 0, 3) != 'WIN') { if($fp = @fopen('/proc/loadavg', 'r')) { 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 了吧?呵呵,平衡负载用的。 以下内容为程序代码: 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; } http://www.55like.com 网站建设 作者:边城浪子 看看是不是 index, forumdisplay, viewthread 这些文件是不是缓存了,有的话把它装到$cachelost 这个变量中。 Section Four: 以下内容为程序代码: $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 掉,安全性考虑得真多,武装到牙齿! 以下内容为程序代码: $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 中获得。 以下内容为程序代码: $discuz_auth_key = md5($_DCACHE['settings']['authkey'].$_SERVER['HTTP_USER_AGENT']); 设置一个$discuz_auth_key,md5 加密。。 以下内容为程序代码: if(isset($_DCOOKIE['auth']) && $_DCOOKIE['auth']) { list($discuz_pw, $discuz_secques, $discuz_uid) = daddslashes(explode("\t", authcode($_DCOOKIE['auth'], 'DECODE')), 1); 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 这三个变量,分别 http://www.55like.com 网站建设 作者:边城浪子 对应密码,提示问题和 uid。 Section Five: 以下内容为程序代码: $newpm = $newpmexists = $sessionexists = $seccode = $bloguid = 0;//初始化变量 $membertablefields = 'm.uid AS discuz_uid, m.username AS discuz_user, m.password AS discuz_pw, m.secques AS discuz_secques, m.adminid, m.groupid, m.groupexpiry, m.extgroupids, m.email, m.timeoffset, 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 http://www.55like.com 网站建设 作者:边城浪子 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 变量,表示这个会员是在线的。 以下内容为程序代码: 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 数 组中 以下内容为程序代码: http://www.55like.com 网站建设 作者:边城浪子 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 这个变量。 以下内容为程序代码: $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] http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: $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); } http://www.55like.com 网站建设 作者:边城浪子 首先初始化变量,然后看看$discuz_user$和$discuz_user 这两个变量是不是存在,不存在的话一系列的变量全部置 0(没登陆当然不存在版主权限管理员什 么的),存在的话就来两个赋值,这就是为什么在 Discuz UserGuide 里面说$discuz_userss 是没有过滤的,而$discuz_user 是过滤掉的,能直接进行数据库 操作的。 然后看看怎么样设置 PHP 的出错级别,注意如果你的管理员的话,Discuz 给的是一个更高的错误显示级别! 以下内容为程序代码: 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 每页的回复数 以下内容为程序代码: $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; http://www.55like.com 网站建设 作者:边城浪子 } else { $grouptype = 'member'; } } 用来得到用户组的,首先看看是不是缓存中有,没有的话就访问数据库了,有的话当然就用缓存的。。 以下内容为程序代码: 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 通行证用的。主要就是得到一些应用程序的地址。 以下内容为程序代码: 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)) { http://www.55like.com 网站建设 作者:边城浪子 @extract($_DSESSION); $cachelost .= (@include DISCUZ_ROOT.'./forumdata/cache/usergroup_'.intval($groupid).'.php') ? '' : ' usergroup_'.$groupid; } } 这一段用来判断你的用户组是不是过期了的,如果过期了很不幸,就被告知用户组过期了。接下来如果没有过期的话就再展开一下$_DSESSION,因为此 时的$_DSESSION 包含了更多的东西了。 以下内容为程序代码: 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,那么你的什么权限都没。。。 以下内容为程序代码: $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 http://www.55like.com 网站建设 作者:边城浪子 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 这个 数组里面写一些东西进去,比方说:发帖子得到的分数,回复得到的分数,帖子分类的类型等… 以下内容为程序代码: http://www.55like.com 网站建设 作者:边城浪子 $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 这个里面写点东西。 以下内容为程序代码: if($cachelost) { require_once DISCUZ_ROOT.'./include/cache.func.php'; updatecache(); dexit('Cache List: '.$cachelost.'
Caches successfully created, please refresh.'); } 如果 cachelost 中有东西的话,那么就调用 include/cache.func.php,并用这个文件里面定义的 updatecache();来更新缓存,并强制访问者刷新论坛,使缓存立 即生效。 以下内容为程序代码: 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"); http://www.55like.com 网站建设 作者:边城浪子 } 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 这个文件中定义的。 以下内容为程序代码: if($cronnextrun && $cronnextrun <= $timestamp) { require_once DISCUZ_ROOT.'./include/cron.func.php'; runcron(); } 呵呵,后台的计划任务来了。。。。当然,这种情况是要至少有一个会员访问论坛,如果说没有人访问,你再设置也没有用,PHP 的一大痛处,不能自己执 行… 以下内容为程序代码: 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'; } } } 这里的加载一些插件的文件。 http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: if((!empty($_DCACHE['advs']) || $globaladvs || $redirectadvs) && !defined('IN_ADMINCP')) { require_once DISCUZ_ROOT.'./include/advertisements.inc.php'; } 这里是看看是不是在论坛的 cache 中有广告的存在(注:$_DCACHE 这个数组中存放的是论坛的一些设置之类的缓存),有广告的话就引用广告的文件。 以下内容为程序代码: 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 这个文件中的定义。当然,下一期我就会分析这个文件。 以下内容为程序代码: if((!empty($fromuid) || !empty($fromuser)) && ($creditspolicy['promotion_visit'] || $creditspolicy['promotion_register'])) { require_once DISCUZ_ROOT.'/include/promotion.inc.php'; } 这个当然是推荐注册用的。应该没看错,呵呵。 以下内容为程序代码: $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'; http://www.55like.com 网站建设 作者:边城浪子 Rss 检查 好了,到此./include/common.inc.php 这个文件就分析完了 discuz 源代码分析[2] include/global.func.php 的注释 以下内容为程序代码: if(!defined('IN_DISCUZ')) { exit('Access Denied'); } //防非法引用的 以下内容为程序代码: /** * 这一个部分是生成 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; http://www.55like.com 网站建设 作者:边城浪子 $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') { http://www.55like.com 网站建设 作者:边城浪子 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)); } } 以下内容为程序代码: /** * 这个是用来清除所有的 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 = ''; } 以下内容为程序代码: /** http://www.55like.com 网站建设 作者:边城浪子 * 用来检查积分的最低要求的 * @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'); } } } } 以下内容为程序代码: /** * 用来完美分词的,也就是把一段中文字只取前面一段,再加一个… * @para string $string 用来分词的串 * @para int $length 保留的长度 * @para string $dot 最后加点什么 * * @return string */ function cutstr($string, $length, $dot = ' ...') { global $charset; http://www.55like.com 网站建设 作者:边城浪子 if(strlen($string) <= $length) { return $string; } $string = str_replace(array('&', '"', '<', '>'), 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 { http://www.55like.com 网站建设 作者:边城浪子 $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('&', '"', '<', '>'), $strcut); return $strcut.$dot; } 以下内容为程序代码: /** * 用来过滤字串的,之所以要这样一个函数是考虑到是不是打开了 magic_quotes_gpc. http://www.55like.com 网站建设 作者:边城浪子 * @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; } 以下内容为程序代码: /** * 检查一个日期是否合法 * @para string $ymd 日期字串 * @para string $sep 分隔符 * * @return boolean */ http://www.55like.com 网站建设 作者:边城浪子 function datecheck($ymd, $sep='-') { if(!empty($ymd)) { list($year, $month, $day) = explode($sep, $ymd); return checkdate($month, $day, $year); } else { return FALSE; } } 以下内容为程序代码: /** * 用来计算程序运行时间的,论坛底部的 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; } } 以下内容为程序代码: /** http://www.55like.com 网站建设 作者:边城浪子 * 强制退出 * @para string $message * */ function dexit($message = '') { echo $message; output(); exit(); } 以下内容为程序代码: /** * 过滤 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('&', '"', '<', '>'), $string)); } return $string; } http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: /** * 用来设置 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(); } } 以下内容为程序代码: /** * 判断是不是文件上传了 * @return boolean */ function disuploadedfile($file) { return function_exists('is_uploaded_file') && (is_uploaded_file($file) || is_uploaded_file(str_replace('\\\\', '\\', $file))); discuz 源代码分析[3] http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: /** * 用来得到上一个页面的地址,也就是来路。 * @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; } 以下内容为程序代码: /** * 设置 cookie 用的,我觉得这个和 clearcookies 放到一起比较好,不过好像这个是按字母排的… http://www.55like.com 网站建设 作者:边城浪子 * @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); } 以下内容为程序代码: /** * 删除论坛的附件用的 * @para string $filename 附件名 * @para int $havethumb 是否有缩略图 * @para int $remote 是否为远程附件 * */ function dunlink($filename, $havethumb = 0, $remote = 0) { global $authkey, $ftp, $attachdir; if($remote) { http://www.55like.com 网站建设 作者:边城浪子 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'); } } 以下内容为程序代码: /** * 生成 email 连接用的,比如把 nicollelord@yahoo.com 换成:nicollelord@yahoo.com这样的形式 * @para string $email * @para int $tolink */ function emailconv($email, $tolink = 1) { $email = str_replace(array('@', '.'), array('@', '.'), $email); return $tolink ? ''.$email.'': $email; } 以下内容为程序代码: /** http://www.55like.com 网站建设 作者:边城浪子 * 记录错误日志用的 * @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.'
'; $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(); } } 以下内容为程序代码: /** * 判断访问者是不是 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'; http://www.55like.com 网站建设 作者:边城浪子 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; } 以下内容为程序代码: /** * 得到一个文件的扩展名 * @para string $filename * * @return string */ function fileext($filename) { return trim(substr(strrchr($filename, '.'), 1, 10)); } 以下内容为程序代码: /** * 用当前时间,会员名,uid,密码,authkey 生成一个 form hash(哈希) * * @return string */ http://www.55like.com 网站建设 作者:边城浪子 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); } 以下内容为程序代码: /** * 生成论坛访问权限的字串,以|隔开 * @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); } 以下内容为程序代码: /** * 得到用户组,同步 groupid 和 member['groupid'],当会员积分和当前积分不一致更新 members 表。 http://www.55like.com 网站建设 作者:边城浪子 * @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]num_rows($query)) { $member['groupid'] = $db->result($query, 0); $updatearray[] = "groupid='$member[groupid]'"; } } if($updatearray) { http://www.55like.com 网站建设 作者:边城浪子 $db->query("UPDATE {$tablepre}members SET ".implode(', ', $updatearray)." WHERE uid='$uid'"); } } return $member['groupid']; } 以下内容为程序代码: /** * 这个的作用主要是把序列化后存在于数据库中的会员组到期信息取出来 * @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; } 以下内容为程序代码: /** http://www.55like.com 网站建设 作者:边城浪子 * 看看一个 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); } 以下内容为程序代码: /** * 判断 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'])) { http://www.55like.com 网站建设 作者:边城浪子 return FALSE; } else { if($_DCACHE['ipbanned']['expiration'] < $timestamp) { @unlink(DISCUZ_ROOT.'./forumdata/cache/cache_ipbanned.php'); } return preg_match("/^(".$_DCACHE['ipbanned']['regexp'].")$/", $onlineip); } } 以下内容为程序代码: /** * 检查一个 email 的合法性 * @para string $email * * @return boolean */ function isemail($email) { return strlen($email) > 6 && preg_match("/^[\w\-\.]+@[\w\-\.]+(\.\w+)+$/", $email); } 以下内容为程序代码: /** * Discuz 语言解析用到的东西,即按照需要装入语言包这个数组 * @para string $file 语言文件(如:templates, email, actions 等) * @para int $templateid 用的是哪套模板中的,若没有的话用 TEMPLATEID 这个常数取代 * @para string $tpldir 模板所在的目录 * * @return array or false http://www.55like.com 网站建设 作者:边城浪子 */ 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; } } 以下内容为程序代码: /** * 超经典的分页函数来了 * @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 用的。 http://www.55like.com 网站建设 作者:边城浪子 * * @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) { http://www.55like.com 网站建设 作者:边城浪子 $from = $pages - $page + 1; $to = $pages; } } $multipage = ($curpage - $offset > 1 && $pages > $page ? '|?' : ''). ($curpage > 1 && !$simple ? ' ' : ''); for($i = $from; $i <= $to; $i++) { $multipage .= $i == $curpage ? ''.$i.'' : ''.$i.''; } $multipage .= ($curpage < $pages && !$simple ? ' ' : ''). ($to < $pages ? '?|' : ''). ($curpage == $maxpages ? ' ' : ''). (!$simple && $pages > $page ? '' : ''); $multipage = $multipage ? '
'.(!$simple ? ' '.$num.'  '.$curpage.'/'.$realpages.' ' : '').$multipage.'
' : ''; } return $multipage; } discuz 源代码分析[4] 以下内容为程序代码: http://www.55like.com 网站建设 作者:边城浪子 /** * 输出用的,这个时候可以做一点事情,比如说:把 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 ( "/\]+\s*)href\=([\"|\']?)([^\"\'\s]+)/ies", "/(\)/is" ); $replacearray = array ( "transsid('\\3','" ); } else { $searcharray = array ( //"/\/", "/\]*)\>/e", "/\]*)\>/e", http://www.55like.com 网站建设 作者:边城浪子 "/\]*)\>/e", "/\]*)\>/e" ); $replacearray = array ( //"", "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) { http://www.55like.com 网站建设 作者:边城浪子 $fp = fopen(CACHE_FILE, 'w'); if($fp) { flock($fp, LOCK_EX); fwrite($fp, empty($content) ? ob_get_contents() : $content); } @fclose($fp); } } } 以下内容为程序代码: /**这一段都是 rewrite 规则,和上一个函数相映成趣~ * @para int $tid 帖子的 id * @para int $page 页数 * @para int $prevpage 上一页 * @para string $extra 附加的东东 * * @return string 返回一个链接,如: */ function rewrite_thread($tid, $page = 0, $prevpage = 0, $extra = '') { return ''; } function rewrite_forum($fid, $page = 0, $extra = '') { return ''; } http://www.55like.com 网站建设 作者:边城浪子 function rewrite_profile($uid, $username, $extra = '') { return ''; } function rewrite_space($uid, $username, $extra = '') { return ''; } 以下内容为程序代码: /** * 访问时段检查 * @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) { http://www.55like.com 网站建设 作者:边城浪子 showmessage('period_nopermission', NULL, 'NOPERM'); } else { return TRUE; } } } } return FALSE; } 以下内容为程序代码: /** * 加密安全提问的,MD5 加密 * @para int $qestionid * @para string $anser * * @return string 返回加密后的字串 */ function quescrypt($questionid, $answer) { return $questionid > 0 && $answer != '' ? substr(md5($answer.md5($questionid)), 16, 8) : ''; } 以下内容为程序代码: /** * 生成随机数(这里返回$hash 名字有点点不合适,hash 是包含 key 和 value 的,这里没有这样的意思) * @para int $length 要生成的长度 * @para int $numeric 传入一个非零的数表示要生成的全是数字 * http://www.55like.com 网站建设 作者:边城浪子 * @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; } 以下内容为程序代码: /** * 删除目录和文件用的 * @para string $dirname 要删除的目录名 * @para boolean $keepdir 是否在删除文件后保留目录,也就是只清空目录 * * @return boolean */ function removedir($dirname, $keepdir = FALSE) { http://www.55like.com 网站建设 作者:边城浪子 $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; } 以下内容为程序代码: /** * 发信件用的,用到了 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 = '') { http://www.55like.com 网站建设 作者:边城浪子 extract($GLOBALS, EXTR_SKIP); require DISCUZ_ROOT.'./include/sendmail.inc.php'; } discuz 源代码分析[5] 以下内容为程序代码: /** * 发送 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) { http://www.55like.com 网站建设 作者:边城浪子 $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)"); } } 以下内容为程序代码: /** * 大家熟悉的 showmesage 终于出现~!包含了 ajax 效果了 * @para string $message 显示在跳转页面的信息,比如:您已成功登陆…… * @para string $url_forword 下一个 url,过三秒会自动跳转过去 * @para string $extra 这个用来控制一些特殊的显示,比如:HALTED, NOPERM,这样可以控制一些特殊场合,Discuz 想得真是太全面了~! */ http://www.55like.com 网站建设 作者:边城浪子 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; } http://www.55like.com 网站建设 作者:边城浪子 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 ? '' : ''; 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'); http://www.55like.com 网站建设 作者:边城浪子 } dexit(); } 以下内容为程序代码: /** * 用来计算星星月亮太阳显示的 * @para $num 等级数 * */ function showstars($num) { global $starthreshold; $alt = 'alt="Rank: '.$num.'"'; if(empty($starthreshold)) { for($i = 0; $i < $num; $i++) { echo ''; } } 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 ''; } } } http://www.55like.com 网站建设 作者:边城浪子 } 以下内容为程序代码: /** * 得到站点 * * @return string 如: http://discuz.net */ function site() { return $_SERVER['HTTP_HOST']; } 以下内容为程序代码: /** * 这个看成函数重载也无妨,功能就是查找$haystack 是不是在$needle 中存在 * @para string $haystack * @para string $needle * * @return boolean */ function strexists($haystack, $needle) { return !(strpos($haystack, $needle) === FALSE); } 以下内容为程序代码: /** * 验证码转换,具体功能用处还没研究 * @para string $seccode 验证码 http://www.55like.com 网站建设 作者:边城浪子 * */ 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]; } } 以下内容为程序代码: /** * 提交后的检查,主要是检查验证码,安全提问和来路是不是正常。 * @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; http://www.55like.com 网站建设 作者:边城浪子 } 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'); } } } http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: /** * 另一个提交检查,检查 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; } 以下内容为程序代码: /** * 另外一个重大函数来了,那就是模板解析,绝对 Discuz 核心 * @para string $file 模板文件(如:discuz, forumdata, viewthread 等) http://www.55like.com 网站建设 作者:边城浪子 * @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; } 以下内容为程序代码: /** * 得到 url 中的 sid http://www.55like.com 网站建设 作者:边城浪子 * @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; } 以下内容为程序代码: /** * 生成主题分类下拉列表 * @para int $curtypeid 当前选择的 id * * @return string */ http://www.55like.com 网站建设 作者:边城浪子 function typeselect($curtypeid = 0) { if($threadtypes = $GLOBALS['forum']['threadtypes']) { $html = ''; return $html; } else { return ''; } } discuz 源代码分析[6] 以下内容为程序代码: /** * 更新积分用到的函数 * @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; http://www.55like.com 网站建设 作者:边城浪子 $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'); } } } 以下内容为程序代码: /** * 把 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); http://www.55like.com 网站建设 作者:边城浪子 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'"); http://www.55like.com 网站建设 作者:边城浪子 } 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; } discuz 源代码分析[7] 以下内容为程序代码: http://www.55like.com 网站建设 作者:边城浪子 /** * 更新版主工作统计的 * @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')"); } } } 以下内容为程序代码: /** * 写入系统日志 * @para string $file 写入的日志文件 * @para string $log 要写入的内容提要 * */ function writelog($file, $log) { global $timestamp, $_DCACHE; http://www.55like.com 网站建设 作者:边城浪子 $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, "\t".str_replace(array(''), '', $tmp)."\n"); } fclose($fp); } } http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: /** * 看成函数的重载,即把 implode 这个函数重载成在两边加上'(单引号)的函数 * @para array $array * * @return string */ function implodeids($array) { if(!empty($array)) { return "'".implode("','", is_array($array) ? $array : array($array))."'"; } else { return ''; } } 以下内容为程序代码: /** * 把这三个函数放一起,因为它们三个是一起实现的,即传说中的 ajax… * 这里说一下 ajax 的实现方式,一种方法是用 XMLHttpRequest 异步提交给服务器,服务器返回一个带有数据结点的 xml 文件 * javascript 或者 php 等解析这个 xml 文档,从而得到数据;另外一种是这个 xml 文件中含一个 CDATA,把已经用 HTML 格式化好的 * 内容返回过来直接用,显然第二种好用,Discuz 用的就是这样一个方式。其他用 iframe 等等就不在这里介绍了,那个一般 * 在提交数据检查、异步文件上传的时候用。 */ //ajaxshowheader 生成一个 xml 头和一个根以及一个 CDATA function ajaxshowheader() { global $charset; @header("Expires: -1"); @header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE); http://www.55like.com 网站建设 作者:边城浪子 @header("Pragma: no-cache"); header("Content-type: application/xml"); echo "\n'; } //ajaxtemplate 组合了 ajaxshowheader + 模板 + ajaxshowfooter,使之完整。 function ajaxtemplate($tplname) { if(!empty($_GET['inajax'])) { extract($GLOBALS); updatesession(); ajaxshowheader(); include template($tplname); ajaxshowfooter(); die(); } } 以下内容为程序代码: /** * 过滤.., \n, \r 用的 * @para string $str 要过滤的 string * * @return string http://www.55like.com 网站建设 作者:边城浪子 */ function wipespecial($str) { return str_replace(array('..', "\n", "\r"), array('', '', ''), $str); } 以下内容为程序代码: /** * 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 源代码分析[8] 以下内容为程序代码: //同样,防止非法引用用的。 if(!defined('IN_DISCUZ')) { exit('Access Denied'); } http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: /** * 这个函数看着很长,其实要实现的功能很少,就是通过 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; http://www.55like.com 网站建设 作者:边城浪子 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) { http://www.55like.com 网站建设 作者:边城浪子 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); } http://www.55like.com 网站建设 作者:边城浪子 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; http://www.55like.com 网站建设 作者:边城浪子 } 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); http://www.55like.com 网站建设 作者:边城浪子 $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]) { http://www.55like.com 网站建设 作者:边城浪子 fclose($ip_fdata); return '- '.$ip_data_detail[2].$ip_data_detail[3]; } } while(!feof($ip_fdata)); fclose($ip_fdata); return '- UNKNOWN'; } } 以下内容为程序代码: /** * 目前发现这个函数在两个地方用到,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); http://www.55like.com 网站建设 作者:边城浪子 $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 .= ''.$i.' '; if($i == 6) { $i = $topicpages + 1; } } if($topicpages > 6) { $pagelinks .= ' .. '.$topicpages.' '; } $thread['multipage'] = ' ( '.$pagelinks.')'; } else { $thread['multipage'] = ''; } if($thread['highlight']) { $string = sprintf('%02d', $thread['highlight']); http://www.55like.com 网站建设 作者:边城浪子 $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; } 以下内容为程序代码: /** * 用来更新点击数的 * @para string $table //用来更新的数据表 * @para string $idcol //用来更新的数据的 id * @para string $viewscol //用来更新的列 http://www.55like.com 网站建设 作者:边城浪子 * @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'); } } } 以下内容为程序代码: /** * 给版主的工作写入日志的 * @para array $thread //对哪个帖子的操作 * @para string $action //进行的什么操作 * */ function modlog($thread, $action) { global $discuz_user, $adminid, $onlineip, $timestamp, $forum, $reason; writelog('modslog', http://www.55like.com 网站建设 作者:边城浪子 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")); } 以下内容为程序代码: /** * 检查是不是写了理由 * */ function checkreasonpm() { global $reason; $reason = trim(strip_tags($reason)); if(($GLOBALS['reasonpm'] == 1 || $GLOBALS['reasonpm'] == 3) && !$reason) { showmessage('admin_reason_invalid'); } } 以下内容为程序代码: /** * 发送操作理由报告给帖子作者 * @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'); } http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: /** * 得到一个下拉列表,这个列表是版主操作理由选择 * * @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 ? '' : ''; } return $select; } 以下内容为程序代码: /** * 登陆检查,看看是不是在十五分钟已经登陆五次了 * * @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)) { http://www.55like.com 网站建设 作者:边城浪子 if($timestamp - $login['lastupdate'] > 900) { return 3; } elseif($login['count'] < 5) { return 2; } else { return 0; } } else { return 1; } } 以下内容为程序代码: /** * 和上面的那个函数(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; http://www.55like.com 网站建设 作者:边城浪子 } } Discuz 源代码分析[9] 本帖不仅仅是分析文件,还把 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 以下内容为程序代码: function template($file, $templateid = 0, $tpldir = '') { global $tplrefresh; $tpldir = $tpldir ? $tpldir : TPLDIR; $templateid = $templateid ? $templateid : TEMPLATEID; $tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm'; http://www.55like.com 网站建设 作者:边城浪子 $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 表示模板目录 以下内容为程序代码: global $tplrefresh; 这个是把$tplrefresh 作为一个全局变量,tplrefresh 表示是不是刷新模板 以下内容为程序代码: $tpldir = $tpldir ? $tpldir : TPLDIR; $templateid = $templateid ? $templateid : TEMPLATEID; 给$tpldir 和$templateid 赋值,如果没有传进来就用常量 TPLDIR 和 TEMPLATEID 给它们值 以下内容为程序代码: http://www.55like.com 网站建设 作者:边城浪子 $tplfile = DISCUZ_ROOT.'./'.$tpldir.'/'.$file.'.htm'; $objfile = DISCUZ_ROOT.'./forumdata/templates/'.$templateid.'_'.$file.'.tpl.php'; 这里是把$tplfile(表示的是模板文件)和$objfile(表示的是要编译成的文件)赋值 以下内容为程序代码: if(TEMPLATEID != 1 && $templateid != 1 && !file_exists($tplfile)) { return template($file, 1, './templates/default/'); } 防止 TEMPLATEID 不等于 1 且$templateid 不等于 1,而且模板文件不存在导致空白问题。 这里也可以算一个迭代,也可以不算,就是把 1 作为第二个参数再调用函数本身。 以下内容为程序代码: 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 源代码分析[10] 关于 template.func.php 文件中函数的分析在下面: http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: //防止非法引用 if(!defined('IN_DISCUZ')) { exit('Access Denied'); } 以下内容为程序代码: /** * 这个是关键模板解析函数,通过正则表达式来替换掉模板中的相关语句,使之变成标准的 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("
Current template pack do not have a necessary language file 'templates.lang.php' or have syntax error!"); } http://www.55like.com 网站建设 作者:边城浪子 $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}", "", $template); $template = preg_replace("/\{(\\\$[a-zA-Z0-9_\[\]\'\"\$\.\x7f-\xff]+)\}/s", "", $template); $template = preg_replace("/$var_regexp/es", "addquote('')", $template); $template = preg_replace("/\<\?\=\<\?\=$var_regexp\?\>\?\>/es", "addquote('')", $template); $template = "\n$template"; $template = preg_replace("/[\n\r\t]*\{template\s+([a-z0-9_]+)\}[\n\r\t]*/is", "\n\n", $template); $template = preg_replace("/[\n\r\t]*\{template\s+(.+?)\}[\n\r\t]*/is", "\n\n", $template); $template = preg_replace("/[\n\r\t]*\{eval\s+(.+?)\}[\n\r\t]*/ies", "stripvtags('\n\n','')", $template); $template = preg_replace("/[\n\r\t]*\{echo\s+(.+?)\}[\n\r\t]*/ies", "stripvtags('\n\n','')", $template); $template = preg_replace("/[\n\r\t]*\{elseif\s+(.+?)\}[\n\r\t]*/ies", "stripvtags('\n\n','')", $template); $template = preg_replace("/[\n\r\t]*\{else\}[\n\r\t]*/is", "\n\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','\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 \\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','\n\\2\n\n')", $template); } $template = preg_replace("/\{$const_regexp\}/s", "", $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("/\]*?src=\"(.+?)\".*?\>\s*\<\/script\>/ise", "stripscriptamp('\\1')", $template); flock($fp, 2); fwrite($fp, $template); fclose($fp); } 以下内容为程序代码: /** * 几个替换,过滤掉一些东西 * @para string $str * * @return string http://www.55like.com 网站建设 作者:边城浪子 */ function transamp($str) { $str = str_replace('&', '&', $str); $str = str_replace('&', '&', $str); $str = str_replace('\"', '"', $str); return $str; } 以下内容为程序代码: /** * 从正则表达式来看是给 ubb 代码去掉一个\符号的,应该是为安全性着想的 * @para string $val * * @return string */ function addquote($var) { return str_replace("\\\"", "\"", preg_replace("/\[([a-zA-Z0-9_\-\.\x7f-\xff]+)\]/s", "['\\1']", $var)); } 以下内容为程序代码: /** * 把一个字符用语言包中的来替换,没有找到的话就用!string 来替换 * @para string $val * * @return string */ http://www.55like.com 网站建设 作者:边城浪子 function languagevar($var) { if(isset($GLOBALS['language'][$var])) { return $GLOBALS['language'][$var]; } else { return "!$var!"; } } 以下内容为程序代码: /** * 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 ''.$_DCACHE['faqs'][$var]['keyword'].''; } else { return "!$var!"; } } 以下内容为程序代码: /** http://www.55like.com 网站建设 作者:边城浪子 * 去掉自定义的一些 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; } 以下内容为程序代码: /** * 作用是把&符号的 html 编码形式换成&,然后换成 javascript 引用的形式。 * @para string $s * * @return string */ function stripscriptamp($s) { $s = str_replace('&', '&', $s); return ""; } discuz 源代码分析[11] Discuz 的 ajax 原理实际上是很简单的,当然,说到 ajax 肯定是用 XMLHttpRequest 这个对象了,不过我曾经也看到国外的牛人写过一篇文章叫做不用 http://www.55like.com 网站建设 作者:边城浪子 XMLHttpRequest 对象来实现 ajax,而且还解决了跨域的问题,真是大开了眼界~!好了,说正事,Dz 的 ajax 用到的文件不是很多,列举如下: ./include/javascript/common.js 这个文件把 Discuz 用到的许多 javascript 的代码(主要为函数和浏览者的浏览器的判断等等) ./include/javascript/ajax.js 不用看就知道是创建一个可用的 XMLHttpRequest 对象用的(由于 XMLHttpRequest 在各个浏览器的创建不同,因此要对各个不同的浏览器进行不同的创 建,还好 prototype.js 中有一个通用的东东。当然,这里也封装了 get, post 这类的函数,刚发现 5.5 封装了更多的东西,有 ajaxmenu,updatesecqaa,ignorepm。 ./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 给前端用户提示用户 是不是存在。 工作原理如下图: 这里用到 ajax 部分的不是很多,我只把几个核心的弄出来说一下,要不然工程太大了。。 以下内容为程序代码: 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); 这一部分是用来判断来访者的浏览器类型的,很重要的一部分判断。对于构建跨平台的显示效果有至关重要的作用~! http://www.55like.com 网站建设 作者:边城浪子 以下内容为程序代码: 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 这个数组中。 以下内容为程序代码: function arraypush(a, value) { a[a.length] = value; return a.length; } 同样可以算一个 php 函数用到了 javascript 上,作用是把 value 这个值插到 a 这个数组的最后一位。 以下内容为程序代码: var Ajaxs = new Array(); function Ajax(recvType, statusId) { var aj = new Object(); aj.statusId = statusId ? document.getElementById(statusId) : null; aj.targetUrl = ''; aj.sendString = ''; http://www.55like.com 网站建设 作者:边城浪子 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 {lang username}
很简单的 HTML 代码,注意看到 onBlur="checkusername()"这里,继续往下深入,看看这个函数是做什么的。 以下内容为程序代码: function checkusername() { var username = trim($('username').value); if(username == lastusername) { return; } else { lastusername = username; } http://www.55like.com 网站建设 作者:边城浪子 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 这个函数就是关键所在了。 以下内容为程序代码: 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 = ''; obj.className = "warning"; } else { warning(obj, s); } }); } ajaxresponse 函数来了,这个作用就是实例化一个 ajax 对象,注意到我们先前说的 recvType,就是第一个传入参数,这里固定成了 XML,Discuz 喜欢用 http://www.55like.com 网站建设 作者:边城浪子 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 这个函数。下面就分析这个函数。 以下内容为程序代码: function warning(obj, msg) { if((ton = obj.id.substr(5, obj.id.length)) != 'password2') { $(ton).select(); } obj.style.display = ''; obj.innerHTML = ' ' + msg; obj.className = "warning"; } warning 函数前面两次提到,其实这个函数很简单,实现的作用就是在 obj 这个层里打一个叉,然后写上错误的原因,把 obj 这个层的 CSS class 设置成为 warning。当然,最开始也验证了一下两次密码是否一致,这里就不说了。 接下来当然是要分析这个 ajax.php 是怎么一回事,它做了哪些使 function(s)中能返回我们要的东西。由于只分析检查用户名这一个部分,我这里就只分析 action=checkuser 这一部分了。 以下内容为程序代码: 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, http://www.55like.com 网站建设 作者:边城浪子 $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 发现了已注册的用户名,不是话就都跳过,直接到最后的: 以下内容为程序代码: showmessage('succeed'); 注意当所有的判断都成功的话就说明合法了,会调用 showmessage 来显示一个 succeed。 最后说一下为什么这里的 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,我在这里分析了一下。
还剩99页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 15 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

路边拾草人

贡献于2012-01-12

下载需要 15 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf