php discuz核心类函数分析

原创
2016-06-13 10:41:38 798浏览


Java代码

/**
* [Discuz!] (C)2001-2099 Comsenz Inc.
* This is NOT a freeware, use is subject to license terms
*
* $Id: class_core.php 6914 2010-03-26 12:52:36Z cnteacher $
*/

///TODO 是将要完成的功能,包括禁止ip和禁止访问
//TODO 禁止ip
//TODO 禁止访问

///和DZ72一样,确保所有文件需要先加载核心文件
define('IN_DISCUZ', true);

/**
* Discuz 核心引擎
* 其他处理代码当中用到的变量不要在本核心 new 之前设置, 否则会自动清空
*
*/
class discuz_core {

// 数据库存储引擎
var $db = null;

// 内存缓冲object
var $mem = null;

// 会话 object
var $session = null;

// 程序配置
var $config = array();

// $_G 数组的映射
var $var = array();

// 加载缓存的数组
var $cachelist = array();

// 是否初始化
var $init_setting = true; //设置
var $init_user = true;//用户
var $init_session = true;//会话
var $init_cron = true;//任务计划
var $init_misc = true;//其他功能
var $init_memory = true;//内存

// 是否已经初始化
var $initated = false;

var $superglobal = array(
'GLOBALS' => 1,
'_GET' = 1,
'_POST' = 1,
'_REQUEST' = 1,
'_COOKIE' = 1,
'_SERVER' = 1,
'_ENV' = 1,
'_FILES' = 1,
);

function &instance() {
static $object;
if(empty($object)) {
$object = new discuz_core();
}
return $object;
}

function discuz_core() {
$this->_init_env();
$this->_init_config();
$this->_init_input();
$this->_init_output();
}

function init() {
if(!$this->initated) {
$this->_init_db();
$this->_init_memory();
$this->_init_user();
$this->_init_session();
$this->_init_setting();
$this->_init_cron();
$this->_init_misc();
}
$this->initated = true;
}

function _init_env() {

error_reporting(E_ALL ^ E_NOTICE);
// error_reporting(E_ALL);
///php 5.3前则关闭魔法引号匹配(自动转义)
if(phpversion() set_magic_quotes_runtime(0);
}

///程序主目录,根据核心文件确定,原来是-7 代表在include下,现在是12代表在source/class下
define('DISCUZ_ROOT', substr(dirname(__FILE__), 0, -12));
///和dz72一样,设置魔法引用(自动转义,单双引号反斜线)
define('MAGIC_QUOTES_GPC', function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc());
///判断字符编码转换函数是否存在
define('ICONV_ENABLE', function_exists('iconv'));
///亚洲字符转码函数是否存在, 因为mb_开头字符处理亚洲字符会比较高效,初步判断用于转码时先用mb_来处理:
define('MB_ENABLE', function_exists('mb_convert_encoding'));
///是否存在输出缓存压缩函数,这个函数的目的,是在确认浏览器支持页面压缩后,才用该函数来压缩所有输出内容,否则直接传输
define('EXT_OBGZIP', function_exists('ob_gzhandler'));

///和DZ72一样,不过当前时间戳被定义为一个常量,效率更高吧,也不用global了。
define('TIMESTAMP', time());
///获取默认时区
discuz_core::timezone_set();

///加载function_core.php,作用推测类似global.func.php
if(!defined('DISCUZ_CORE_FUNCTION') && !@include(DISCUZ_ROOT.'./source/function/function_core.php')) {
$this->error('function_core.php is missing');
}

//判断浏览器是否是蜘蛛
define('IS_ROBOT', checkrobot());

//清理全局变量
///全清理了,真是彻底把所有变量都从内存中注销了
foreach ($GLOBALS as $key = $value) {
if (!isset($this->superglobal[$key])) {
$GLOBALS[$key] = null; unset($GLOBALS[$key]);
}
}

// 配置全局变量
///和上一步结合,只留下自己需要的变量,并初始化。
///这么做够狠,只要稍微小心点,就不会出现因为变量未初始化而出现的安全问题
global $_G;
$_G = array(
//公用全局定义
'uid' = 0,
'username' = '',
'adminid' = 0,
'groupid' = 1,
'sid' = '',
'formhash' = '',
'timestamp' = TIMESTAMP,
'starttime' = dmicrotime(),
'clientip' = $this->_get_client_ip(),
'referer' = '',
'charset' = '',
'gzipcompress' = '',
'authkey' = '',
'timenow' = array(),

'PHP_SELF' = '',
'siteurl' = '',

//公用全局数组定义
'config' = array(),
'setting' = array(),
'member' = array(),
'group' = array(),
'cookie' = array(),
'style' = array(),
'cache' = array(),
'session' = array(),
'lang' = array(),
'my_app' = array(),//默认应用
'my_userapp' = array(),//用户自添加应用

//论坛全局定义
'fid' = 0,
'tid' = 0,
'forum' = array(),
'rssauth' = '',

//uch 全局定义
'home' = array(),
'space' = array(),

//portal 全局定义
'block' = array(),
'article' = array(),

//Action
'action' = array(
'action' = APPTYPEID,
'fid' = 0,
'tid' = 0,
)
);
//相对主目录的相对地址及文件名
$_G['PHP_SELF'] = htmlspecialchars($_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_NAME']);
//基本脚本名,每个功能脚本首页前都会定义
//比如forum.php,则定义CURSCRIPT为forum,而forum_forumdisplay.php则不定义,因为属于forum
$_G['basescript'] = CURSCRIPT;
//站点网址
$_G['siteurl'] = htmlspecialchars('http://'.$_SERVER['HTTP_HOST'].preg_replace("/\/+(api)?\/*$/i", '', substr($_G['PHP_SELF'], 0, strrpos($_G['PHP_SELF'], '//m.sbmmt.com/m/'))).'//m.sbmmt.com/m/');

///$_G的映射,也即超级全局变量
$this->var = & $_G;

}

function _init_input() {

//note 禁止对全局变量注入
///和dz72类似,禁止GLOBALS=xxx的方式注入
if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
error('request_tainting');
}

if(!empty($_GET['rewrite'])) {
$query_string = '?mod=';
$param = explode('-', $_GET['rewrite']);
$query_string .= $_GET['mod'] = $param[0];
array_shift($param);
$paramc = count($param);
for($i = 0;$i $_REQUEST[$param[$i]] = $_GET[$param[$i]] = $param[$i + 1];
$query_string .= '&'.$param[$i].'='.$param[$i + 1];
}
$_SERVER['QUERY_STRING'] = $query_string;
unset($param, $paramc, $query_string);
}

// slashes 处理,如果没有魔法引号处理(自动转义),则手动转义GET/POST/COOKIE/FILES中的单双引号、null反斜线\
if(!MAGIC_QUOTES_GPC) {
$_GET = daddslashes($_GET);
$_POST = daddslashes($_POST);
$_COOKIE = daddslashes($_COOKIE);
$_FILES = daddslashes($_FILES);
}

//cookie 处理
///验证cookie前缀与config中的设置值是否一致,一致则转为$cookie数组中的值
$prelength = strlen($this->config['cookie']['cookiepre']);
foreach($_COOKIE as $key = $val) {
if(substr($key, 0, $prelength) == $this->config['cookie']['cookiepre']) {
$this->var['cookie'][substr($key, $prelength)] = $val;
}
}

$_GET['diy'] = empty($_GET['diy']) ? '' : $_GET['diy'];

///$_GET和$_POST转成与索引同名加"gp_"前缀的变量
///如$_GET['username']直接用$gp_username来访问
foreach(array_merge($_POST, $_GET) as $k = $v) {
$this->var['gp_'.$k] = $v;
}

///根据$_GET['mod']来确定m的值,$this->var为全局数组,gp_为上个语句的附加前缀
$this->var['mod'] = empty($this->var['gp_mod']) ? '' : htmlspecialchars($this->var['gp_mod']);
///如果使用ajax,再判断是post传值或get和xmlhttprequest同时有效
$this->var['inajax'] = empty($this->var['gp_inajax']) ? 0 : ($_SERVER['REQUEST_METHOD'] == 'GET' && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' || $_SERVER['REQUEST_METHOD'] == 'POST' ? 1 : 0);
///当前页码
$this->var['page'] = empty($this->var['gp_page']) ? 1 : max(1, intval($this->var['gp_page']));
///确定cookie中的sid值
$this->var['sid'] = $this->var['cookie']['sid'] = isset($this->var['cookie']['sid']) ? htmlspecialchars($this->var['cookie']['sid']) : '';
}

///初始化设置
function _init_config() {
///加载设置文件
$_config = array();
@include DISCUZ_ROOT.'./config/config_global.php';
if(empty($_config)) {
error('config_notfound');
}
///确定密钥,如果值为空,则密钥默认为cookie前缀与数据库名拼接的md5值,否则为配置文件中的值
///authkey密钥是sid等参数加解密的重要参数
$_config['security']['authkey'] = empty($config['security']['authkey']) ? md5($_config['cookie']['cookiepre'].$_config['db'][1]['dbname']) : ($config['security']['authkey']);

$this->config = & $_config;
///Discuz的调试
if(empty($this->config['debug']) || !file_exists(libfile('function/debug'))) {
define('DISCUZ_DEBUG', false);
} elseif($this->config['debug'] === 1 || $this->config['debug'] === 2 || !empty($_REQUEST['debug']) && $_REQUEST['debug'] === $this->config['debug']) {
define('DISCUZ_DEBUG', true);
if($this->config['debug'] == 2) {
error_reporting(E_ALL);
}
}

$GLOBALS['_G']['config'] = & $this->config;
///以浏览器版本为参考,进行密钥的二次md5加密
$GLOBALS['_G']['authkey'] = md5($this->config['security']['authkey'].$_SERVER['HTTP_USER_AGENT']);
}

function _init_output() {
///如果设置中打开xss跨站脚本的防御模式,且网址中存在" if($this->config['security']['urlxssdefend'] && !empty($_SERVER['REQUEST_URI'])) {
$temp = urldecode($_SERVER['REQUEST_URI']);
if(strpos($temp, ' error('request_tainting');
}
}

///存在ob_gzhandler则启用输出缓存压缩
if($this->config['output']['gzip'] && EXT_OBGZIP) {
ob_start('ob_gzhandler');
setglobal('gzipcompress', true);
} else {
ob_start();
setglobal('gzipcompress', false);
}

///确定HTML页面编码,及其他编码
if($this->config['output']['forceheader']) {
@header('Content-Type: text/html; charset='.$this->config['output']['charset']);
}

setglobal('charset', $this->config['output']['charset']);
define('CHARSET', $this->config['output']['charset']);
}

///拒绝机器人访问
function reject_robot() {
if(IS_ROBOT) {
exit(header("HTTP/1.1 403 Forbidden"));
}
}

///获取客户端ip,
function _get_client_ip() {
$clientip = '';
///环境变量客户端ip有值且字符长度大于unknown,则说明该变量有效,确定为客户端ip
if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
$clientip = getenv('HTTP_CLIENT_IP');
///否则取当前浏览用户的网关ip地址
} elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
$clientip = getenv('HTTP_X_FORWARDED_FOR');
///用户计算机的ip地址
} elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
$clientip = getenv('REMOTE_ADDR');
} elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
$clientip = $_SERVER['REMOTE_ADDR'];
}

///判断是否是数字与点组成的7-15位字符
preg_match("/[\d\.]{7,15}/", $clientip, $clientipmatches);
$clientip = $clientipmatches[0] ? $clientipmatches[0] : 'unknown';
return $clientip;
}

function _init_db() {
///生成数据库对象
$this->db = & DB::object();
///加载设置文件并连接数据库
$this->db->set_config($this->config['db']);
$this->db->connect();
}

function _init_session() {

$this->session = new discuz_session();

if($this->init_session)
{
///传入sid,客户端ip与uid作为session判断机制,分新老用户,老用户则查session表,否则创建
$this->session->init($this->var['cookie']['sid'], $this->var['clientip'], $this->var['uid']);
$this->var['sid'] = $this->session->sid;
$this->var['session'] = $this->session->var;

if($this->var['sid'] != $this->var['cookie']['sid']) {
dsetcookie('sid', $this->var['sid'], 86400);
}

// 首次登陆更新最后访问时间,每隔 10 分钟更新用户最后动作时间
if($this->var['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600)
$this->session->set('lastactivity', TIMESTAMP);

$update = array('lastip' = $this->var['clientip'], 'lastactivity' = TIMESTAMP);
if($this->session->isnew) {
$update['lastvisit'] = TIMESTAMP;
}
更新会员状态
DB::update('common_member_status', $update, "uid='".$this->var['uid']."'");
}
}
}

function _init_user() {

if($this->init_user) {
if($auth = getglobal('auth', 'cookie')) {
$auth = daddslashes(explode("\t", authcode($auth, 'DECODE')));
}
list($discuz_pw, $discuz_uid) = empty($auth) || count($auth)
if($discuz_uid) {
$user = getuserbyuid($discuz_uid);
}

if(!empty($user) && $user['password'] == $discuz_pw) {
$this->var['member'] = $user;
} else {
$user = array();
$this->_init_guest();
}

$this->cachelist[] = 'usergroup_'.$this->var['member']['groupid'];
if($user && $user['adminid'] 0 && $user['groupid'] != $user['adminid']) {
$this->cachelist[] = 'admingroup_'.$this->var['member']['adminid'];
}

} else {
$this->_init_guest();
}

if(empty($this->var['cookie']['lastvisit'])) {
$this->var['member']['lastvisit'] = TIMESTAMP - 3600;
dsetcookie('lastvisit', TIMESTAMP - 3600, 86400 * 30);
} else {
$this->var['member']['lastvisit'] = empty($this->var['cookie']['lastvisit']);
}

setglobal('uid', getglobal('uid', 'member'));
setglobal('username', addslashes(getglobal('username', 'member')));
setglobal('adminid', getglobal('adminid', 'member'));
setglobal('groupid', getglobal('groupid', 'member'));
}

function _init_guest() {
setglobal('member', array( 'uid' = 0, 'username' = '', 'groupid' = 7, 'credits' = 0, 'timeoffset' = 9999));
}

function _init_cron() {
if($this->init_cron && $this->init_setting) {
if($this->var['cache']['cronnextrun'] discuz_cron::run();
}
}
}

function _init_misc() {

if(!$this->init_misc) {
return false;
}
// 调入核心语言包
lang('core');

//处理全局时区设置
if($this->init_setting && $this->init_user) {
if(!isset($this->var['member']['timeoffset']) || $this->var['member']['timeoffset'] == 9999 || $this->var['member']['timeoffset'] === '') {
$this->var['member']['timeoffset'] = $this->var['setting']['timeoffset'];
}
}

$timeoffset = $this->init_setting ? $this->var['member']['timeoffset'] : $this->var['setting']['timeoffset'];
$this->var['timenow'] = array(
'time' = dgmdate(TIMESTAMP),
'offset' = $timeoffset = 0 ? ($timeoffset == 0 ? '' : '+'.$timeoffset) : $timeoffset
);
$this->timezone_set($timeoffset);

$this->var['formhash'] = formhash();
define('FORMHASH', $this->var['formhash']);

// 定义风格常量
if(is_array($this->var['style'])) {
foreach ($this->var['style'] as $key = $val) {
$key = strtoupper($key);
if(!defined($key) && !is_array($val)) {
define($key, $val);
}
}
}

//论坛开关检查
if($this->var['setting']['**losed'] && !(in_array($this->var['mod'], array('logging', 'seccode')) || getglobal('adminid', 'member') == 1)) {
$closedreason = DB::result_first("SELECT svalue FROM ".DB::table('common_setting')." WHERE skey='closedreason'");
showmessage($closedreason ? $closedreason : 'board_closed', NULL, array(), array('login' = 1));
}

$this->var['tpp'] = $this->var['setting']['topicperpage'] ? intval($this->var['setting']['topicperpage']) : 20;
$this->var['ppp'] = $this->var['setting']['postperpage'] ? intval($this->var['setting']['postperpage']) : 10;

if($this->var['setting']['nocacheheaders']) {
@header("Expires: -1");
@header("Cache-Control: no-store, private, post-check=0, pre-check=0, max-age=0", FALSE);
@header("Pragma: no-cache");
}

$lastact = TIMESTAMP."\t".htmlspecialchars(basename($this->var['PHP_SELF']))."\t".htmlspecialchars($this->var['mod']);
dsetcookie('lastact', $lastact, 86400);

}

function _init_setting() {

if($this->init_setting) {

if(empty($this->var['setting'])) {
$this->cachelist[] = 'setting';
}

if(empty($this->var['style'])) {
$this->cachelist[] = 'style_default';
}

if(!isset($this->var['cache']['cronnextrun'])) {
$this->cachelist[] = 'cronnextrun';
}
}

!empty($this->cachelist) && loadcache($this->cachelist);

if(!is_array($this->var['setting'])) {
$this->var['setting'] = array();
}
if($this->var['member'] && $this->var['member']['adminid'] 0 && $this->var['member']['groupid'] != $this->var['member']['adminid'] && !empty($this->var['cache']['admingroup_'.$this->var['member']['adminid']])) {
$this->var['group'] = array_merge($this->var['group'], $this->var['cache']['admingroup_'.$this->var['member']['adminid']]);
}
}

function _init_memory() {
$this->mem = new discuz_memory();
if($this->init_memory) {
$this->mem->init($this->config['memory']);
}
$this->var['memory'] = $this->mem->type;
}

function timezone_set($timeoffset = 0) {
if(function_exists('date_default_timezone_set')) {
@date_default_timezone_set('Etc/GMT'.($timeoffset 0 ? '-' : '+').(abs($timeoffset)));
}
}

function error($msg, $halt = true) {
$this->error_log($msg);
echo $msg;
$halt && exit();
}

function error_log($message) {
$time = date("Y-m-d H:i:s", TIMESTAMP);
$file = DISCUZ_ROOT.'./data/log/errorlog_'.date("Ym").'.txt';
$message = "\n#{$time}:\t".str_replace(array("\t", "\r", "\n"), " ", $message);
error_log($message, 3, $file);
}

}

/**
* Discuz MySQL 类的支持
*
*/
class db_mysql
{
var $tablepre;
var $version = '';
var $querynum = 0;
var $curlink;
var $link = array();
var $config = array();
var $sqldebug = array();

function db_mysql($config = array()) {
if(!empty($config)) {
$this->set_config($config);
}
}

function set_config($config) {
$this->config = &$config;
$this->tablepre = $config['1']['tablepre'];
}

function connect() {

if(empty($this->config) || empty($this->config[1])) {
$this->halt('notfound_config');
}

foreach ($this->config as $id = $config) {
$this->link[$id] = $this->_dbconnect(
$config['dbhost'],
$config['dbuser'],
$config['dbpw'],
$config['dbcharset'],
$config['dbname'],
$config['pconnect']
);
}
$this->curlink = $this->link[1];
}

function _dbconnect($dbhost, $dbuser, $dbpw, $dbcharset, $dbname, $pconnect) {
$link = null;
$func = empty($pconnect) ? 'mysql_connect' : 'mysql_pconnect';
if(!$link = @$func($dbhost, $dbuser, $dbpw, 1)) {
$this->halt('notconnect');
} else {
$this->curlink = $link;
if($this->version() '4.1') {
$serverset = $dbcharset ? 'character_set_connection='.$dbcharset.', character_set_results='.$dbcharset.', character_set_client=binary' : '';
$serverset .= $this->version() '5.0.1' ? ((empty($serverset) ? '' : ',').'sql_mode=\'\'') : '';
$serverset && mysql_query("SET $serverset", $link);
}
$dbname && @mysql_select_db($dbname, $link);
}
return $link;
}

function table_name($tablename) {
return $this->tablepre.$tablename;
}

function select_db($dbname) {
return mysql_select_db($dbname, $this->curlink);
}

function fetch_array($query, $result_type = MYSQL_ASSOC) {
return mysql_fetch_array($query, $result_type);
}

function fetch_first($sql) {
return $this->fetch_array($this->query($sql));
}

function result_first($sql) {
return $this->result($this->query($sql), 0);
}

function query($sql, $type = '') {

if(defined('DISCUZ_DEBUG') && DISCUZ_DEBUG) {
$starttime = dmicrotime();
}
$func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
'mysql_unbuffered_query' : 'mysql_query';
if(!($query = $func($sql, $this->curlink))) {
if(in_array($this->errno(), array(2006, 2013)) && substr($type, 0, 5) != 'RETRY') {
$this->connect();
return $this->query($sql, 'RETRY'.$type);
}
if($type != 'SILENT' && substr($type, 5) != 'SILENT') {
$this->halt('query_error', $sql);
}
}

if(defined('DISCUZ_DEBUG') && DISCUZ_DEBUG) {
$this->sqldebug[] = array($sql, number_format((dmicrotime() - $starttime), 6), debug_backtrace());
}

$this->querynum++;
return $query;
}

function affected_rows() {
return mysql_affected_rows($this->curlink);
}

function error() {
return (($this->curlink) ? mysql_error($this->curlink) : mysql_error());
}

function errno() {
return intval(($this->curlink) ? mysql_errno($this->curlink) : mysql_errno());
}

function result($query, $row = 0) {
$query = @mysql_result($query, $row);
return $query;
}

function num_rows($query) {
$query = mysql_num_rows($query);
return $query;
}

function num_fields($query) {
return mysql_num_fields($query);
}

function free_result($query) {
return mysql_free_result($query);
}

function insert_id() {
return ($id = mysql_insert_id($this->curlink)) = 0 ? $id : $this->result($this->query("SELECT last_insert_id()"), 0);
}

function fetch_row($query) {
$query = mysql_fetch_row($query);
return $query;
}

function fetch_fields($query) {
return mysql_fetch_field($query);
}

function version() {
if(empty($this->version)) {
$this->version = mysql_get_server_info($this->curlink);
}
return $this->version;
}

function close() {
return mysql_close($this->curlink);
}

function halt($message = '', $sql = '') {
global $_G;
$dberror = $this->error();
$dberrno = $this->errno();
$phperror = '

';
foreach (debug_backtrace() as $error) {
$error['file'] = str_replace(DISCUZ_ROOT, '', $error['file']);
$error['class'] = isset($error['class']) ? $error['class'] : '';
$error['type'] = isset($error['type']) ? $error['type'] : '';
$error['function'] = isset($error['function']) ? $error['function'] : '';
$phperror .= "";
}
$phperror .= '
FileLineFunction
$error[file]$error[line]$error[class]$error[type]$error[function]()
';
$helplink = "http://faq.comsenz.com/?type=mysql&dberrno=".rawurlencode($dberrno)."&dberror=".rawurlencode($dberror);
@header('Content-Type: text/html; charset='.$_G['config']['output']['charset']);
echo '
'.
error('db_error', array(
'$message' = error('db_'.$message, array(), true),
'$info' = $dberror ? error('db_error_message', array('$dberror' = $dberror), true) : '',
'$sql' = $sql ? error('db_error_sql', array('$sql' = $sql), true) : '',
'$errorno' = $dberrno ? error('db_error_no', array('$dberrno' = $dberrno), true) : '',
'$helplink' = $helplink,
), true);
echo "PHP Backtrace
$phperror
";
exit();
}

}

/**
* 对Discuz CORE 中 DB Object中的主要方法进行二次封装,方便程序调用
*
*/
class DB
{

/**
* 返回表名(pre_$table)
*
* @param 原始表名 $table
* @return 增加pre之后的名字
*/
function table($table) {
$a = & DB::object();
return $a->table_name($table);
}

/**
* 删除一条或者多条记录
*
* @param string $table 原始表名
* @pa
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。