- 浏览: 608351 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
kangh:
转载的也拿出来 都不试一下 完全错误
Nginx+ffmpeg的HLS开源服务器搭建配置及开发详解 -
wangtxlz:
#cd builders/cmake#cmake .系统提示命 ...
crtmpserver流媒体服务器的介绍与搭建 -
hnraysir:
支持支持支持
手机Android音视频采集与直播推送,实现单兵、移动监控类应用 -
wuent:
把web服务器和php框架绑定到一起?真不建议这样。。。
Swoole(PHP高级Web开发框架) -
wuent:
有更详细的性能比较吗?php,python,java
PHP中的(伪)多线程与多进程
- Discuz! X 中 SESSION 机制讲解
- 在Discuz! X中一如继往的,SESSION 并没有使用 PHP 自带的 SESSION 机制,而是系统的一套自带的机制。
- 在数据库中可以看到有两个 SESSION 表:
- 一个是pre_common_adminsession,是管理员登录后台的 SESSION 表;
- 另一个是 pre_common_session 表,是所有用户在前台浏览页面时的 SESSION 表。
- 这两个表都是内存表(内存表的读写速度远高于 MYISAM 表及文本文件)。
- 在 Discuz! X 中 SESSION 与 COOKIE 是分不开的,因为 SESSION 就是从客户端读取的 COOKIE ,
- 然后由浏览页面时触发相关的函数执行,再写入数据库 SESSION 表。
- 我以登录流程为例来讲解程序具体是如何执行的。
- 在前台首页,点击登录后,弹出一个登录窗口,填写好数据后,提交。form表单提交的 URL 是:
- 1 <a href="http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1">http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1</a>
-
- 数据提交到了 member.php 文件中,在程序中可看到下面的代码:
- 01 $mod = !in_array($discuz->var['mod'], $modarray) ? 'logging' : $discuz->var['mod']; //mod的值即是接下来加载的php页面
- 02 define('CURMODULE', $mod);
- 03 $modcachelist = array('register' => array('modreasons', 'stamptypeid', 'fields_required', 'fields_optional', 'ipctrl'));
- 04 $cachelist = array();
- 05 if(isset($modcachelist[CURMODULE])) {
- 06 $cachelist = $modcachelist[CURMODULE];
- 07 }
- 08 $discuz->cachelist = $cachelist;
- 09 $discuz->init();
- 10 runhooks();
- 11 require DISCUZ_ROOT.'./source/module/member/member_'.$mod.'.php'; //完成程序的包含操作
- 打开source/module/member/member_logging.php文件,是一个类,在类的前面可看到下面三句代码:
-
- $ctl_obj = new logging_ctl();
- $method = 'on_'.$_G['gp_action']; // $_G['gp_action'] 等于action的值即 login
- $ctl_obj->$method(); //$ctl_obj->on_login();
- 在类中可找到login方法,在方法中,大约 56 行有下面一个判断语句:
-
- if(!submitcheck('loginsubmit', 1, $seccodecheck)) {
- 判断语句是当游客浏览时,submitcheck 函数的返回值是假,取反,为真。
- 当用户登录时,程序走的是else部分,在里面可看到下面五句代码:
-
- } else {
- $_G['uid'] = $_G['member']['uid'] = 0;
- $_G['username'] = $_G['member']['username'] = $_G['member']['password'] = ''; //变量赋值
- $result = userlogin($_G['gp_username'], $_G['gp_password'], $_G['gp_questionid'], $_G['gp_answer'], $_G['setting']['autoidselect'] ? 'auto' : $_G['gp_loginfield']); //从数据库查询用户数据,并返回相应的信息
- if($result['status'] > 0) { //状态值大于 0 ,说明有此用户,可以登录
- setloginstatus($result['member'], $_G['gp_cookietime'] ? 2592000 : 0); //设置登录状态,即是写 COOKIE 操作,COOKIE 中的数据即是 SESSION 中相应的数据,但此函数并不负责写 SESSION 的操作
- 我们来看一下 source/function/function_login.php中的 setloginstatus 函数,是普通的写 COOKIE 操作,不再具体讲解:
-
- function setloginstatus($member, $cookietime) {
- global $_G;
- $_G['uid'] = $member['uid'];
- $_G['username'] = $member['username'];
- $_G['adminid'] = $member['adminid'];
- $_G['groupid'] = $member['groupid'];
- $_G['formhash'] = formhash();
- $_G['session']['invisible'] = getuserprofile('invisible');
- $_G['member'] = $member;
- $_G['core']->session->isnew = 1;
- dsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}", 'ENCODE'), $cookietime, 1, true); //authcode加密
- dsetcookie('loginuser');
- dsetcookie('activationauth');
- dsetcookie('pmnum');
- }
- 到这里可以说是登录流程大部分已经走完,但是 COOKIE 不清除时,会一直存在于客户端,如果超时,程序中会在判断弃用此 COOKIE,并重新写入。
- 下面我们来看一下 DZX 中 SESSION 操作的类,在 source/class/calss_core.php 文件中:
- 程序中每次请求都会加载 SESSION ,这是由核心类 discuz_core 中的 _init_session 方法来执行的,此方法被置于 类的 init方法中,说明每次加载类,会自动将 SESSION 写入。
-
- function _init_session() {
- $this->session = new discuz_session(); //创建 SESSION 类
- if($this->init_session) {
- //从 COOKIE 中读取数据
- $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;
- //判断 SID 是否相等,不等,说明是多个用户在同一主机上登录网站,需要重新写 COOKIE
- if($this->var['sid'] != $this->var['cookie']['sid']) {
- dsetcookie('sid', $this->var['sid'], 86400);
- }
- if($this->session->isnew) {
- if(ipbanned($this->var['clientip'])) {
- $this->session->set('groupid', 6);
- }
- }
- if($this->session->get('groupid') == 6) {
- $this->var['member']['groupid'] = 6;
- sysmessage('user_banned');
- }
- //UID 不为空,且需要更新 SESSION 或是 SESSION 超时,更改用户状态,需要用户重新登录
- if($this->var['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600) < TIMESTAMP)) {
- $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']."'");
- }
- }
- }
- 操作 SESSION 的类是 discuz_session ,我们看这个类里面的两个方法:
-
- //此函数负责产生新的 SESSION,但并不负责写入数据库
- function create($ip, $uid) {
- //创建SESSION,执行插入数据,由随机函数产生一个六位随机数即是session的唯一值时间为当前时间,sid为cookie中的sid
- $this->isnew = true;
- $this->var = $this->newguest;
- $this->set('sid', random(6));
- $this->set('uid', $uid);
- $this->set('ip', $ip);
- $this->set('lastactivity', time());
- $this->sid = $this->var['sid'];
- return $this->var;
- }
- //此函数负责更新 SESSION
- function update() {
- if($this->sid !== null) {
- $data = daddslashes($this->var);
- if($this->isnew) {
- $this->delete();
- DB::insert('common_session', $data, false, false, true);
- } else {
- DB::update('common_session', $data, "sid='$data[sid]'");
- }
- dsetcookie('sid', $this->sid, 86400);
- }
- }
- 至此我们知道了 SESSION 插入数据库的具体函数,与 COOKIE 的联系,但还不清楚是如何触发此操作的。
- 打开 source/function/function_core.php 文件,找到函数,updatesession ,此函数负责更新 SESSION :
-
- function updatesession($force = false) {
- global $_G;
- static $updated = false;
- if(!$updated) {
- $discuz = & discuz_core::instance();
- foreach($discuz->session->var as $k => $v) {
- if(isset($_G['member'][$k]) && $k != 'lastactivity') {
- $discuz->session->set($k, $_G['member'][$k]);
- }
- }
- foreach($_G['action'] as $k => $v) {
- $discuz->session->set($k, $v);
- }
- $discuz->session->update();
- $updated = true;
- }
- return $updated;
- }
- 我们在程序源码中搜索此函数,可以看到在很多的模板中都有下面一句代码:
-
- {eval updatesession();}
- 浏览页面时将触发此函数,并将 SESSION 写入数据库。
- 整理一下思绪:
- 第一步:用户登录,程序将 COOKIE 写入客户端,这些 COOKIE 即是 SESSION 的部分数据,如SID、IP、TIME,不包含用户名、密码等关键信息。
- 第二步,登录成功后,程序会自动刷新页面,向服务器再次发送请求,服务器加载 discuz_core 核心类,并从 COOKIE 中读取到 SESSION 的相关信息,但还没有写入数据库。
- 第三步,核心类加载完成,程序继续执行,最后加载模板,触发 updatesession 函数,SESSION 被写入数据库。
Discuz! X 中 SESSION 机制讲解 在Discuz! X中一如继往的,SESSION 并没有使用 PHP 自带的 SESSION 机制,而是系统的一套自带的机制。 在数据库中可以看到有两个 SESSION 表: 一个是pre_common_adminsession,是管理员登录后台的 SESSION 表; 另一个是 pre_common_session 表,是所有用户在前台浏览页面时的 SESSION 表。 这两个表都是内存表(内存表的读写速度远高于 MYISAM 表及文本文件)。 在 Discuz! X 中 SESSION 与 COOKIE 是分不开的,因为 SESSION 就是从客户端读取的 COOKIE , 然后由浏览页面时触发相关的函数执行,再写入数据库 SESSION 表。 我以登录流程为例来讲解程序具体是如何执行的。 在前台首页,点击登录后,弹出一个登录窗口,填写好数据后,提交。form表单提交的 URL 是: 1 <a href="http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1">http://ux.com/member.php?mod=logging&action=login&loginsubmit=yes&floatlogin=yes&inajax=1</a> 数据提交到了 member.php 文件中,在程序中可看到下面的代码: 01 $mod = !in_array($discuz->var['mod'], $modarray) ? 'logging' : $discuz->var['mod']; //mod的值即是接下来加载的php页面 02 define('CURMODULE', $mod); 03 $modcachelist = array('register' => array('modreasons', 'stamptypeid', 'fields_required', 'fields_optional', 'ipctrl')); 04 $cachelist = array(); 05 if(isset($modcachelist[CURMODULE])) { 06 $cachelist = $modcachelist[CURMODULE]; 07 } 08 $discuz->cachelist = $cachelist; 09 $discuz->init(); 10 runhooks(); 11 require DISCUZ_ROOT.'./source/module/member/member_'.$mod.'.php'; //完成程序的包含操作 打开source/module/member/member_logging.php文件,是一个类,在类的前面可看到下面三句代码: $ctl_obj = new logging_ctl(); $method = 'on_'.$_G['gp_action']; // $_G['gp_action'] 等于action的值即 login $ctl_obj->$method(); //$ctl_obj->on_login(); 在类中可找到login方法,在方法中,大约 56 行有下面一个判断语句: if(!submitcheck('loginsubmit', 1, $seccodecheck)) { 判断语句是当游客浏览时,submitcheck 函数的返回值是假,取反,为真。 当用户登录时,程序走的是else部分,在里面可看到下面五句代码: } else { $_G['uid'] = $_G['member']['uid'] = 0; $_G['username'] = $_G['member']['username'] = $_G['member']['password'] = ''; //变量赋值 $result = userlogin($_G['gp_username'], $_G['gp_password'], $_G['gp_questionid'], $_G['gp_answer'], $_G['setting']['autoidselect'] ? 'auto' : $_G['gp_loginfield']); //从数据库查询用户数据,并返回相应的信息 if($result['status'] > 0) { //状态值大于 0 ,说明有此用户,可以登录 setloginstatus($result['member'], $_G['gp_cookietime'] ? 2592000 : 0); //设置登录状态,即是写 COOKIE 操作,COOKIE 中的数据即是 SESSION 中相应的数据,但此函数并不负责写 SESSION 的操作 我们来看一下 source/function/function_login.php中的 setloginstatus 函数,是普通的写 COOKIE 操作,不再具体讲解: function setloginstatus($member, $cookietime) { global $_G; $_G['uid'] = $member['uid']; $_G['username'] = $member['username']; $_G['adminid'] = $member['adminid']; $_G['groupid'] = $member['groupid']; $_G['formhash'] = formhash(); $_G['session']['invisible'] = getuserprofile('invisible'); $_G['member'] = $member; $_G['core']->session->isnew = 1; dsetcookie('auth', authcode("{$member['password']}\t{$member['uid']}", 'ENCODE'), $cookietime, 1, true); //authcode加密 dsetcookie('loginuser'); dsetcookie('activationauth'); dsetcookie('pmnum'); } 到这里可以说是登录流程大部分已经走完,但是 COOKIE 不清除时,会一直存在于客户端,如果超时,程序中会在判断弃用此 COOKIE,并重新写入。 下面我们来看一下 DZX 中 SESSION 操作的类,在 source/class/calss_core.php 文件中: 程序中每次请求都会加载 SESSION ,这是由核心类 discuz_core 中的 _init_session 方法来执行的,此方法被置于 类的 init方法中,说明每次加载类,会自动将 SESSION 写入。 function _init_session() { $this->session = new discuz_session(); //创建 SESSION 类 if($this->init_session) { //从 COOKIE 中读取数据 $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; //判断 SID 是否相等,不等,说明是多个用户在同一主机上登录网站,需要重新写 COOKIE if($this->var['sid'] != $this->var['cookie']['sid']) { dsetcookie('sid', $this->var['sid'], 86400); } if($this->session->isnew) { if(ipbanned($this->var['clientip'])) { $this->session->set('groupid', 6); } } if($this->session->get('groupid') == 6) { $this->var['member']['groupid'] = 6; sysmessage('user_banned'); } //UID 不为空,且需要更新 SESSION 或是 SESSION 超时,更改用户状态,需要用户重新登录 if($this->var['uid'] && ($this->session->isnew || ($this->session->get('lastactivity') + 600) < TIMESTAMP)) { $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']."'"); } } } 操作 SESSION 的类是 discuz_session ,我们看这个类里面的两个方法: //此函数负责产生新的 SESSION,但并不负责写入数据库 function create($ip, $uid) { //创建SESSION,执行插入数据,由随机函数产生一个六位随机数即是session的唯一值时间为当前时间,sid为cookie中的sid $this->isnew = true; $this->var = $this->newguest; $this->set('sid', random(6)); $this->set('uid', $uid); $this->set('ip', $ip); $this->set('lastactivity', time()); $this->sid = $this->var['sid']; return $this->var; } //此函数负责更新 SESSION function update() { if($this->sid !== null) { $data = daddslashes($this->var); if($this->isnew) { $this->delete(); DB::insert('common_session', $data, false, false, true); } else { DB::update('common_session', $data, "sid='$data[sid]'"); } dsetcookie('sid', $this->sid, 86400); } } 至此我们知道了 SESSION 插入数据库的具体函数,与 COOKIE 的联系,但还不清楚是如何触发此操作的。 打开 source/function/function_core.php 文件,找到函数,updatesession ,此函数负责更新 SESSION : function updatesession($force = false) { global $_G; static $updated = false; if(!$updated) { $discuz = & discuz_core::instance(); foreach($discuz->session->var as $k => $v) { if(isset($_G['member'][$k]) && $k != 'lastactivity') { $discuz->session->set($k, $_G['member'][$k]); } } foreach($_G['action'] as $k => $v) { $discuz->session->set($k, $v); } $discuz->session->update(); $updated = true; } return $updated; } 我们在程序源码中搜索此函数,可以看到在很多的模板中都有下面一句代码: {eval updatesession();} 浏览页面时将触发此函数,并将 SESSION 写入数据库。 整理一下思绪: 第一步:用户登录,程序将 COOKIE 写入客户端,这些 COOKIE 即是 SESSION 的部分数据,如SID、IP、TIME,不包含用户名、密码等关键信息。 第二步,登录成功后,程序会自动刷新页面,向服务器再次发送请求,服务器加载 discuz_core 核心类,并从 COOKIE 中读取到 SESSION 的相关信息,但还没有写入数据库。 第三步,核心类加载完成,程序继续执行,最后加载模板,触发 updatesession 函数,SESSION 被写入数据库。
发表评论
-
nginx、php-fpm默认配置与性能–TCP socket还是unix domain socket
2015-04-02 11:14 1469前几天看到一篇博客,提到php所在服务器在大并发情况下,频 ... -
使用socket方式连接Nginx优化php-fpm性能
2015-04-01 13:49 0Nginx连接fastcgi的方式有 ... -
PHP中include和require的区别详解
2015-04-01 08:32 01、概要 require()语句的性能与includ ... -
PHP 中cookie 和 session 的分析
2015-03-31 12:33 0HP 中cookie 和session 的分析 ... -
php 经典的算法题你懂的
2015-03-31 12:31 0有5个人偷了一堆苹果,准备在第二天分赃。晚上,有一人遛出来, ... -
PHP最常用的2种设计模式工厂模式和单例模式介绍
2015-03-31 12:26 0简单来说,PHP单例模式就是一个功能用一个类来实现,并且在整 ... -
PHP 数据类型
2015-03-31 12:23 0PHP 数据类型 PHP 支持八种原始类型(type)。 ... -
PHP mcrypt启用、加密以及解密过程详解
2015-03-30 11:32 1459Mcrypt扩展库可以实现加密解密功能,就是既能将明文加密, ... -
PHP扩展实现类扩展
2015-03-27 14:08 573在第一篇文章中,我们所开发的扩展是单个函数,本篇文章看一下 ... -
PHP高级工程师的面试题
2015-03-06 10:35 01. 基本知识点 HTTP协议中几个状态码的含义:1x ... -
PHP面试中常见的面试试题与算法例子
2015-03-05 14:14 0下面是四道比较常见的题目,主要考察的是对字符串函数以及文件操 ... -
PHP实现四种常用的排序算法
2015-03-05 14:09 0插入排序(Insertion Sort),选择排序(Sele ... -
用swagger-php/ui做API测试
2015-02-13 09:46 3643功能: 1 swagger-php根据自定义的规则生成API ... -
app后端设计(0)--总目录
2015-01-23 18:03 0做了3年app相关的系统架构,api设计,先后在3个创业公司中 ... -
PHP中eAccelerator、memcached、xcache、APC 4个加速、缓存扩展的区别
2015-01-23 10:26 912这篇文章主要介绍了PHP ... -
PHP内核探索:zend_parse_parameters函数
2015-01-16 14:14 961最简单的获取函数调用者传递过来的参数便是使用zend_pars ... -
实战:用C写php扩展(二)
2015-01-16 11:05 997一、前言 在我的上一篇文章“实战:用C写php扩展(一)”里介 ... -
实战:用C写php扩展(一)
2015-01-16 11:04 10161、 前言 首先,确保你的机器安装了apache和php。假 ... -
JavaScript or PHP 来检测移动设备
2014-09-22 10:04 627iPhone & iPod Detection T ... -
YII Framework学习教程-YII的Modules(模块化)
2014-08-27 11:04 751一个相对来说大的项目。如果按照yii生成的 ...
相关推荐
X中的具体操作流程来进一步分析其SESSION机制。当用户在前台点击登录按钮,弹出登录窗口后,填写好登录信息并提交表单。此时,数据被发送至member.php文件,该文件会根据提交的参数(如mod和action)决定后续加载的...
X3.4在继承和完善Discuz! X3.3的基础上,针对PHP7进行了优化,对于X3.3用户来说,X3.4已继承了X3.3的补丁修复工作。 Discuz! X3.4 繁体中文BIG5 R20171001 更新日志: 增加新触屏版;去除云平台相关代码;优化...
Discuz!X3.5和X3.4是流行的开源社区论坛系统,它们为企业和个人提供了构建互动社区的强大工具。为了增强这些平台的安全性,腾讯云开发了一款名为“tencentcloud_captcha”的官方插件。这款插件的核心功能是集成腾讯...
X3.1"指的是 Discuz! 系统的一个具体版本,即X3.1。Discuz! 是一个非常知名的开源社区论坛软件,由康盛创想(Comsenz)开发。X3.1是其在某个时间点推出的更新版本,通常会包含对前一版本的改进、新功能的添加以及...
X3.4 在继承和完善 Discuz! X3.3 的基础上,针对 PHP7 进行了优化。对于 X3.2 用户来说,X3.3 已继承了 X3.2 的补丁修复工作。安全稳定的程序为站长提供更加可靠的保障。 Discuz! 安装说明: 全新安装:请参照...
X3.4在继承和完善Discuz! X3.3的基础上,针对PHP7进行了优化,对于X3.3用户来说,X3.4已继承了X3.3的补丁修复工作。 Discuz! X2.5 PHP7.0 GBK 更新日志:增加新触屏版;去除云平台相关代码;优化优化缓存机制改为...
X1.0开发手册》是Comsenz公司官方发布的一份详细指导文档,旨在帮助开发者深入了解和熟练运用Discuz!X1.0这一开源社区论坛系统。手册内容涵盖Discuz!X1.0的核心功能、架构设计、模块开发、模板制作、插件集成等多个...
X3.4 在继承和完善 Discuz! X3.3 的基础上,针对 PHP7 进行了优化。对于 X3.2 用户来说,X3.3 已继承了 X3.2 的补丁修复工作。安全稳定的程序为站长提供更加可靠的保障。 Discuz! 安装说明: 全新安装:请参照...
### Discuz!X3.1 全新安装图文教程知识点详解 #### 一、Discuz!X3.1 概述 **Discuz!X3.1**是Comsenz公司开发的一款非常流行的社区论坛软件,它基于PHP语言和MySQL数据库,支持多种操作系统和Web服务器环境。Discuz...
x2.5深黑经典模板" 涉及的主要知识点是Discuz! X2.5论坛系统以及其配套的主题模板设计。Discuz! 是一款非常流行的开源社区建站软件,主要功能是帮助用户快速搭建属于自己的论坛网站。X2.5是Discuz! 的一个特定版本...
标题中的“解决与论坛冲突(Discuz! X3.1)问题”指的是在使用Discuz! X3.1这款开源论坛软件时遇到的兼容性或功能冲突问题。Discuz! 是一个广泛使用的PHP和MySQL构建的社区论坛系统,X3.1是其特定的版本,可能包含了...
X3.5 X3.4腾讯云全局配置插件 1.0.0.zip" 提供了一个专为Discuz! X3.5和X3.4论坛系统设计的插件,它整合了与腾讯云相关的各种服务,并允许用户在一个中心化的界面进行统一管理和配置。 【描述】中的关键信息表明,...
Discuz! X插件开发
X3.1 在继承和完善 Discuz! X3.0 的基础上,针对广告垃圾防御进行了大幅度的调整,新防水墙、帐号保镖、云验证码等功能为社区的健康运转提供更加可靠的保障。 全新安装: 请参照以往版本的安装过程进行,上传程序...
X2.5 愤怒的小鸟插件V1.0》是一款专为Discuz! X2.5论坛系统设计的趣味插件,同时也兼容Discuz! X1.5、X2版本。这款插件的独特之处在于它并未对论坛的系统文件进行任何修改,而是通过外部实现方式,确保了用户在安装...
X1.5导航插件是一款专为Discuz! X1.5平台设计的强大辅助工具,它旨在帮助用户更好地管理和优化社区网站的导航功能,提升用户体验,增强网站的互动性和实用性。这款插件在www.58119.com上有着生动的展示,通过实际...
X3.2 在继承和完善 Discuz! X3.1 的基础上,针对社区移动端进行了新的尝试。推出微信登录、微社区等功能。安全稳定的程序为站长提供更加可靠的保障。 说明: 全新安装:请参照以往版本的安装过程进行,上传程序,...
X3.4在继承和完善Discuz! X3.3的基础上进行了大量工作。这意味着X3.4不仅包含了X3.3的所有功能,还修复了之前版本存在的问题和漏洞,提高了系统的安全性。对于已经使用X3.3的用户来说,升级到X3.4无需担心兼容性...
X3.4在继承和完善Discuz! X3.3的基础上,针对PHP7进行了优化,对于X3.3用户来说,X3.4已继承了X3.3的补丁修复工作。 Discuz! X3.4 简体中文GBK R20180101 更新日志: 增加HTML版头像上传机制;优化Media改成...