用户名: 密码: 忘记密码? 注册

discuzx源码分析笔记(二)class_core.php 核心类

作者:  时间: 2010-11-08

先看forum.php文件

  1. define(‘APPTYPEID’, 2);//应用类型
  2. define(‘CURSCRIPT’, ‘forum’);//当前脚本
  3. require ‘./source/class/class_core.php’;//加载核心
  4. require ‘./source/function/function_forum.php’;
  5. $discuz = & discuz_core::instance();//实例化 使用此种方法是为了兼容PHP4 与PHP5

复制代码

这里我们进到./source/class/class_core.php 看看这个核心类

首先来分别看看这些属性

  1. var $db = null;//存储数据库对象
  2. var $mem = null;//存储内存缓存对象
  3. var $session = null;////存储session
  4. var $config = array();//存储配置数组
  5. var $var = array();//系统常用变量
  6. var $cachelist = array();//存储缓存数组

复制代码

我们先根据属性名来标注他们要储存的东西,这些东西肯定非常有用供以后的代码去掉用的

接着往下看看构造函数

  1. function discuz_core() {//构造方法
  2. ????$this->_init_env();//初始化环境变量
  3. ????$this->_init_config();//初始化配置信息
  4. ????$this->_init_input();//初始化用户输入
  5. ????$this->_init_output();//初始化用户输出
  6. }

复制代码

当然到目前为止我们都还只是猜测,我们来看看具体的代码

本帖最后由 蜗牛 于 2010-6-15 17:38 编辑

  1. function _init_env() {
  2. //设置错误报告等级 以及关闭自动转义
  3. error_reporting(E_ERROR);
  4. if(phpversion() < ‘5.3.0′) {
  5. set_magic_quotes_runtime(0);
  6. }
  7. //设置常用常量,最主要是判断是否支持某些函数
  8. define(‘DISCUZ_ROOT’, substr(dirname(__FILE__), 0, -12));//程序根目录
  9. define(‘MAGIC_QUOTES_GPC’, function_exists(‘get_magic_quotes_gpc’) && get_magic_quotes_gpc());//获取是否开启了自动转义
  10. define(‘ICONV_ENABLE’, function_exists(‘iconv’));//iconv函数是否存在
  11. define(‘MB_ENABLE’, function_exists(‘mb_convert_encoding’));//mb_convert_encoding函数是否存在
  12. define(‘EXT_OBGZIP’, function_exists(‘ob_gzhandler’));//ob_gzhandler函数是否存在
  13. define(‘TIMESTAMP’, time());//得到程序执行前的时间戳
  14. $this->timezone_set();//设置时区,但是为何在这个方法里 没有传入8这个参数以设置我们的东八区?
  15. //加载核心函数
  16. if(!defined(‘DISCUZ_CORE_FUNCTION’) && !@include(DISCUZ_ROOT.’./source/function/function_core.php’)) {
  17. $this->error(‘function_core.php is missing’);
  18. }
  19. //得到是否是机器人
  20. define(‘IS_ROBOT’, checkrobot());
  21. /*
  22. 去除’GLOBALS’ => 1,
  23. ‘_GET’ => 1,
  24. ‘_POST’ => 1,
  25. ‘_REQUEST’ => 1,
  26. ‘_COOKIE’ => 1,
  27. ‘_SERVER’ => 1,
  28. ‘_ENV’ => 1,
  29. ‘_FILES’ => 1,
  30. 外的所有超级全局变量
  31. */
  32. foreach ($GLOBALS as $key => $value) {
  33. if (!isset($this->superglobal[$key])) {
  34. $GLOBALS[$key] = null; unset($GLOBALS[$key]);
  35. }
  36. }
  37. global $_G;
  38. $_G = array(
  39. ‘uid’ => 0,//用户ID
  40. ‘username’ => ”,//用户名
  41. ‘adminid’ => 0,//管理员ID
  42. ‘groupid’ => 1,//用户组
  43. ’sid’ => ”,//加密后的SID
  44. ‘formhash’ => ”,//跟提交表单有关的HASH数据
  45. ‘timestamp’ => TIMESTAMP,//程序执行时的时间戳
  46. ’starttime’ => dmicrotime(),//程序开始执行的时间
  47. ‘clientip’ => $this->_get_client_ip(),//用户IP
  48. ‘referer’ => ”,//用来记录来路URL
  49. ‘charset’ => ”,//字符编码
  50. ‘gzipcompress’ => ”,
  51. ‘authkey’ => ”,//跟加密有关的authkey
  52. ‘timenow’ => array(),
  53. ‘PHP_SELF’ => ”,//请求的URL
  54. ’siteurl’ => ”,//站点地址
  55. ’siteroot’ => ”,//站点根目录
  56. ‘config’ => array(),//配置信息
  57. ’setting’ => array(),//设置信息
  58. ‘member’ => array(),//用户有关信息
  59. ‘group’ => array(),//用户组信息
  60. ‘cookie’ => array(),//cookie信息
  61. ’style’ => array(),//风格信息
  62. ‘cache’ => array(),//缓存数组
  63. ’session’ => array(),//session信息
  64. ‘lang’ => array(),//语言包
  65. ‘my_app’ => array(),
  66. ‘my_userapp’ => array(),
  67. ‘fid’ => 0,//论坛版块ID
  68. ‘tid’ => 0,//帖子ID
  69. ‘forum’ => array(),
  70. ‘rssauth’ => ”,
  71. ‘home’ => array(),
  72. ’space’ => array(),
  73. ‘block’ => array(),
  74. ‘article’ => array(),
  75. ‘action’ => array(
  76. ‘action’ => APPTYPEID,
  77. ‘fid’ => 0,
  78. ‘tid’ => 0,
  79. )
  80. );
  81. //得到所执行的脚本 比如index.php
  82. $_G['PHP_SELF'] = htmlspecialchars($_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['PHP_SELF']);
  83. //当前脚本 比如forum
  84. $_G['basescript'] = CURSCRIPT;
  85. //站点地址
  86. $_G['siteurl'] = htmlspecialchars(‘http://’.$_SERVER['HTTP_HOST'].preg_replace(“/\/+(api)?\/*$/i”, ”, substr($_G['PHP_SELF'], 0, strrpos($_G['PHP_SELF'], ‘/’))).’/');
  87. //站点目录
  88. $_G['siteroot'] = substr($_G['PHP_SELF'], 0, -strlen(basename($_G['PHP_SELF'])));
  89. //将数据存在$var属性中
  90. $this->var = & $_G;
  91. }

复制代码

我们从上面的代码可以看出_init_env()的作用是用来初始化程序的执行环境的,其中包含

1:得到一些环境信息,就是那段define 常量设置语句
2:得到一些常用常量:

?? ???? ??DISCUZ_ROOT 程序根目录

?? ???? ??TIMESTAMP 程序执行时间戳

?? ???? ??IS_ROBOT是否为机器人

3:最重要的是初始化一个全局数组(这里说并不是超级全局变量)$_G数组与 $this->var属性,其中$_G数组是供外部代码掉用的,而$this->var是类内部掉用的(这个只是猜测)

这个函数得到了一些重要信息
[code][/code]DISCUZ_ROOT 程序根目录

TIMESTAMP 程序执行时间戳

IS_ROBOT是否为机器人

$_G[‘timestamp’]程序执行时间

$_G[‘starttime’]程序开始执行时间

$_G[‘clientip’]访问者IP

$_G['PHP_SELF']执行的脚本名称 例如index.php

$_G['basescript'] 当前脚本例如forum 这个是在入口处定义的

$_G['siteurl']站点地址

$_G['siteroot'];程序根目录,这个跟DISCUZ_ROOT的区别是, DISCUZ_ROOT是绝对路径,$_G['siteroot'];是相对站点跟目录的 相对路径

本帖最后由 蜗牛 于 2010-6-15 18:01 编辑

  1. function _init_config() {
  2. $_config = array();//初始化配置数组
  3. @include DISCUZ_ROOT.’./config/config_global.php’;//加载配置文件
  4. if(empty($_config)) {//配置文件不存在则提示出错
  5. error(‘config_notfound’);
  6. }

  7. //得到加密用的密钥
  8. $_config['security']['authkey'] = empty($_config['security']['authkey']) ? md5($_config['cookie']['cookiepre'].$_config['db'][1]['dbname']) : ($_config['security']['authkey']);

  9. //将配置信息存在config属性中
  10. $this->config = & $_config;

  11. //是否开启debug模式
  12. if(empty($this->config['debug']) || !file_exists(libfile(‘function/debug’))) {
  13. define(‘DISCUZ_DEBUG’, false);
  14. } elseif($this->config['debug'] === 1 || $this->config['debug'] === 2 || !empty($_REQUEST['debug']) && $_REQUEST['debug'] === $this->config['debug']) {
  15. define(‘DISCUZ_DEBUG’, true);
  16. if($this->config['debug'] == 2) {
  17. error_reporting(E_ALL);
  18. }
  19. } else {
  20. define(‘DISCUZ_DEBUG’, false);
  21. }

  22. $GLOBALS['_G']['config'] = & $this->config;
  23. $GLOBALS['_G']['authkey'] = md5($this->config['security']['authkey'].$_SERVER['HTTP_USER_AGENT']);

  24. //静态文件目录
  25. define(‘STATICURL’, !empty($this->config['output']['staticurl']) ? $this->config['output']['staticurl'] : ’static/’);

  26. }

复制代码

这个方法做了下面几件事:
1:设置加密密钥
2:是否开启debug模式
3:将配置文件/config/config_global.php中的数据存在$this->config 属性与$_G['config']中

得到有用的数据是
1:$_G['authkey'] 全局加密密钥
2:STATICURL 静态文件目录

同时我们知道如何开启DiscuzX的debug模式
1: 在配置文件/config/config_global.php中设置$_config['debug']=1或 者$_config['debug']=2 如果为2就直接开启debug模式否则让DISCUZ_DEBUG常量为true,还有中方法是通过POST或者GET方式传递一个debug参数进去, 并且这个参数与$_config['debug']的值一样。

当然这些所有的前提是source\function\function_debug.php文件要存在,但是官方下的程序里面并没有这个文件,也许这个是官方自己用来测试的吧

  1. function _init_input() {//输入信息初始化
  2. ????
  3. ????//防止注入变量
  4. ????if (isset($_GET['GLOBALS']) ||isset($_POST['GLOBALS']) ||????isset($_COOKIE['GLOBALS']) || isset($_FILES['GLOBALS'])) {
  5. ?? ??error(‘request_tainting’);
  6. ????}
  7. ????
  8. ????//是否开启rewrite
  9. ????if(!empty($_GET['rewrite'])) {
  10. ?? ??$query_string = ‘?mod=’;
  11. ?? ??$param = explode(‘-’, $_GET['rewrite']);
  12. ?? ??$query_string .= $_GET['mod'] = $param[0];
  13. ?? ??array_shift($param);
  14. ?? ??$paramc = count($param);
  15. ?? ??for($i = 0;$i < $paramc;$i+=2) {
  16. ?? ?? $_REQUEST[$param[$i]] = $_GET[$param[$i]] = $param[$i + 1];
  17. ?? ?? $query_string .= ‘&’.$param[$i].’=’.$param[$i + 1];
  18. ?? ??}
  19. ?? ??$_SERVER['QUERY_STRING'] = $query_string;
  20. ?? ??unset($param, $paramc, $query_string);
  21. ????}
  22. ????
  23. ????//如果没有开启自动转义 则将GPCF自动转义
  24. ????if(!MAGIC_QUOTES_GPC) {
  25. ?? ??$_GET = daddslashes($_GET);
  26. ?? ??$_POST = daddslashes($_POST);
  27. ?? ??$_COOKIE = daddslashes($_COOKIE);
  28. ?? ??$_FILES = daddslashes($_FILES);
  29. ????}
  30. ????
  31. ????/*将cookie中加上了前缀的数据 去掉前缀存在$this->var['cookie']数组中*/
  32. ????$prelength = strlen($this->config['cookie']['cookiepre']);
  33. ????foreach($_COOKIE as $key => $val) {
  34. ?? ??if(substr($key, 0, $prelength) == $this->config['cookie']['cookiepre']) {
  35. ?? ?? $this->var['cookie'][substr($key, $prelength)] = $val;
  36. ?? ??}
  37. ????}
  38. ????//是否开启diy
  39. ????$_GET['diy'] = empty($_GET['diy']) ? ” : $_GET['diy'];
  40. ????
  41. ????//将所有POST,GET来的数据存在$this->var['gp_'.$k]中
  42. ????foreach(array_merge($_POST, $_GET) as $k => $v) {
  43. ?? ??$this->var['gp_'.$k] = $v;
  44. ????}
  45. ????//mod方法,DX根据不同的MOD来掉用不同的文件,例如forum.php中require DISCUZ_ROOT.’./source/module/forum/forum_’.$mod.’.php’;
  46. ????$this->var['mod'] = empty($this->var['gp_mod']) ? ” : htmlspecialchars($this->var['gp_mod']);
  47. ????//是否是AJAX掉用的
  48. ????$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);
  49. ????//页码
  50. ????$this->var['page'] = empty($this->var['gp_page']) ? 1 : max(1, intval($this->var['gp_page']));
  51. ????//用户登录后的COOKIE信息
  52. ????$this->var['sid'] = $this->var['cookie']['sid'] = isset($this->var['cookie']['sid']) ? htmlspecialchars($this->var['cookie']['sid']) : ”;
  53. }

复制代码

从上面代码可以看出这个方法做了下面几件事
1:实现GPC机制,对用户提交来的数据进行自动转义
2:防止变量注入
3:启用rewrite机制后的 网址分发
4:设置COOKIE
5:将所有GET以及POST来的数据存在$this->var['gp_'.$k]中

得到的有用数据为:
1:$this->var['cookie']中存有所有COOKIE信息
2:$_GET['diy'],是否启用DIY
3:$this->var['gp_'.$k] 存有所有用户POST GET来的数据
4:$this->var['mod'],掉用哪个模块
5:$this->var['inajax']是否是AJAX掉用该页面
6:$this->var['page']页码
7:$this->var['sid'] 用户的登录验证信息

  1. function _init_output() {

  2. //防止用户提交恶意的URL地址?疑问 为何不放在_init_input里面
  3. if($this->config['security']['urlxssdefend'] && !empty($_SERVER['REQUEST_URI'])) {
  4. $temp = urldecode($_SERVER['REQUEST_URI']);
  5. if(strpos($temp, ‘<’) !== false || strpos($temp, ‘”‘) !== false) {
  6. error(‘request_tainting’);
  7. }
  8. }

  9. //是否开启GZIP压缩,并且不是通过AJAX掉用的并且掉用的模型不是attachment(也就是说不是附件)并且压缩函数存在
  10. if($this->config['output']['gzip'] && empty($this->var['gp_inajax']) && $this->var['gp_mod'] != ‘attachment’ && EXT_OBGZIP) {
  11. ob_start(‘ob_gzhandler’);
  12. setglobal(‘gzipcompress’, true);
  13. } else {
  14. ob_start();
  15. setglobal(‘gzipcompress’, false);
  16. }

  17. //是否强制输出头部信息,这里最主要是强制按照一定编码输出
  18. if($this->config['output']['forceheader']) {
  19. @header(‘Content-Type: text/html; charset=’.$this->config['output']['charset']);
  20. }
  21. //设置$_G['charset'];为配置文件中的字符编码
  22. setglobal(‘charset’, $this->config['output']['charset']);

  23. //设置字符编码
  24. define(‘CHARSET’, $this->config['output']['charset']);
  25. }

复制代码

这个方法我就不多说了,他最主要是开启GZIP压缩,以及防止用户提交而已的URL,但是为什么

  1. if($this->config['security']['urlxssdefend'] && !empty($_SERVER['REQUEST_URI'])) {
  2. ?? ??$temp = urldecode($_SERVER['REQUEST_URI']);
  3. ?? ??if(strpos($temp, ‘<’) !== false || strpos($temp, ‘”‘) !== false) {
  4. ?? ?? error(‘request_tainting’);
  5. ?? ??}
  6. ????}

复制代码

这段代码不放在_init_input里面呢?

好了,经过实例化class_core.php 核心类之后程序所做的动作就已经读完了,这里最重要的是得到了一些有用的数据以供后面使用,最最重要的就是
$_G数组与$this->var属性

下面来看看都有哪些内容

常量:

DISCUZ_ROOT 程序根目录

TIMESTAMP 程序执行时间戳

IS_ROBOT是否为机器人

STATICURL 静态文件目录

DISCUZ_DEBUG 是否开启DEBUG模式

CHARSET字符编码
$_G数组

$_G[‘timestamp’]程序执行时间

$_G[‘starttime’]程序开始执行时间

$_G[‘clientip’]访问者IP

$_G['PHP_SELF']执行的脚本名称 例如index.php

$_G['basescript'] 当前脚本例如forum 这个是在入口处定义的

$_G['siteurl']站点地址

$_G['siteroot'];程序根目录,这个跟DISCUZ_ROOT的区别是, DISCUZ_ROOT是绝对路径,$_G['siteroot'];是相对站点跟目录的 相对路径

$_G['authkey']全局加密密钥

$_G['charset'] 输出的编码(在配置文件中定义)

$_G['cookie']中存有cookie数组

$_G['gp_'.$k]中存有所有的POST以及GET来的数据

$_G['gp_'.$k] 存有所有用户POST GET来的数据

$_G['mod'],掉用哪个模块

$_G['inajax']是否是AJAX掉用该页面

$_G['page']页码

$_G['sid'] 用户的登录验证信息

$_G['config']=$this->config=配置文件/config/config_global.php中的数据

$_GET['diy'],是否启用DIY


这里注意了$_G=&this->var

看完这个实例化之后,就是最重要的
$discuz->init();了
这个是为了加载一些数据库类,模板类等等……

来自:http://