- 论坛徽章:
- 0
|
Discuz 7.2坑爹集锦-PHP篇 2........
类型: 流程错误
坑爹指数: ★★
代码: admin/cpanel.share.php 29 adminsession()函数
Php代码- $session = $this->_loadsession($uid, $ip, $GLOBALS['admincp']['checkip']);
- $this->errorcount = $session['errorcount']; // <----
- $this->storage = $session['storage'];
- if(emptyempty($session)) { // <------
- $this->creatsession($uid, $adminid, $ip);
- $cpaccess = 1;
- } elseif($session['errorcount'] == -1) {
- $session = $this->_loadsession($uid, $ip, $GLOBALS['admincp']['checkip']);
- $this->errorcount = $session['errorcount']; // <----
- $this->storage = $session['storage'];
- if(empty($session)) { // <------
- $this->creatsession($uid, $adminid, $ip);
- $cpaccess = 1;
- } elseif($session['errorcount'] == -1) {
复制代码 点评:- 对$this->errorcount赋值先于 if (empty($session)) 判断,会导致 update()方法SQL错误($this->errorcount非数字)
复制代码 FIX: 先判断再赋值
Php代码- } elseif($session['errorcount'] == -1) {
- $this->errorcount = $session['errorcount']; // must before exec $this->update() !!
- $this->storage = $session['storage'];
- $this->update();
- $cpaccess = 3;
- } elseif($session['errorcount'] <= 3) {
- } elseif($session['errorcount'] == -1) {
- $this->errorcount = $session['errorcount']; // must before exec $this->update() !!
- $this->storage = $session['storage'];
- $this->update();
- $cpaccess = 3;
- } elseif($session['errorcount'] <= 3) {
复制代码 -------------------------------------------------------------------------------------------------------------------------
类型: 流程错误
坑爹指数: ★★★
代码: include/magic/magic_del.inc.php 29
Php代码- if($post['first']) {
- foreach(array('threads', 'threadsmod', ... 'attachments', ...) as $value) {
- $db->query("DELETE FROM {$tablepre}$value WHERE tid='$post[tid]'", 'UNBUFFERED');
- }
-
- $query = $db->query("SELECT uid, attachment, dateline, thumb, remote FROM {$tablepre}attachments WHERE tid='$post[tid]'");
- while($attach = $db->fetch_array($query)) {
- dunlink($attach['attachment'], $attach['thumb'], $attach['remote']);
- }
- if($post['first']) {
- foreach(array('threads', 'threadsmod', ... 'attachments', ...) as $value) {
- $db->query("DELETE FROM {$tablepre}$value WHERE tid='$post[tid]'", 'UNBUFFERED');
- }
- $query = $db->query("SELECT uid, attachment, dateline, thumb, remote FROM {$tablepre}attachments WHERE tid='$post[tid]'");
- while($attach = $db->fetch_array($query)) {
- dunlink($attach['attachment'], $attach['thumb'], $attach['remote']);
- }
复制代码 点评: attachments的记录都被删除了还能再取出记录去unlinke么?虽然再次操作取不出记录但不代表数据库没工作啊,它还是会傻傻地去查询索引的。
-------------------------------------------------------------------------------------------------------------------------
类型: 重复执行
坑爹指数: ★★
代码: admin/forums.inc.php 52-78
Php代码- for($i = 0; $i < count($forums); $i++) { // <-----
- if($forums[$i]['type'] == 'group') {
- echo showforum($i, 'group');
- for($j = 0; $j < count($forums); $j++) { // <-----
- if($forums[$j]['fup'] == $forums[$i]['fid'] && $forums[$j]['type'] == 'forum') {
- ....
- }
- }
- echo showforum($i, '', 'lastboard');
- } elseif(!$forums[$i]['fup'] && $forums[$i]['type'] == 'forum') {
- echo showforum($i);
- for($j = 0; $j < count($forums); $j++) { // <-----
- ....
- }
- echo showforum($i, '', 'lastchildboard');
- }
- }
- for($i = 0; $i < count($forums); $i++) { // <-----
- if($forums[$i]['type'] == 'group') {
- echo showforum($i, 'group');
- for($j = 0; $j < count($forums); $j++) { // <-----
- if($forums[$j]['fup'] == $forums[$i]['fid'] && $forums[$j]['type'] == 'forum') {
- ....
- }
- }
- echo showforum($i, '', 'lastboard');
- } elseif(!$forums[$i]['fup'] && $forums[$i]['type'] == 'forum') {
- echo showforum($i);
- for($j = 0; $j < count($forums); $j++) { // <-----
- ....
- }
- echo showforum($i, '', 'lastchildboard');
- }
- }
复制代码 点评: 内外两层for循环使用的count($forums)条件判断可以在循环开始前就计算出个结果赋值给一个变量然后以后就访问该变量。可能DZ认为一个论坛版块不会太多所以吃多点也不会噎着~
-------------------------------------------------------------------------------------------------------------------------
类型: 数值类型
坑爹指数: ★★★★
代码: stats.php 548 在线时间统计
Php代码- if(isset($statvars['thismonth'])) {
- $thismonth = unserialize($statvars['thismonth']);
- } else {
- $dateline = strtotime(gmdate('Y-n-01', $timestamp));
- $query = $db->query("SELECT o.uid, m.username, o.thismonth AS time .....
- while($online = $db->fetch_array($query)) {
- $online['time'] = round($online['time'] / 60, 2); // <------
- $thismonth[] = $online;
- }
- $newstatvars[] = "'onlines', 'thismonth', '".addslashes(serialize($thismonth))."'";
- }
- if(isset($statvars['thismonth'])) {
- $thismonth = unserialize($statvars['thismonth']);
- } else {
- $dateline = strtotime(gmdate('Y-n-01', $timestamp));
- $query = $db->query("SELECT o.uid, m.username, o.thismonth AS time .....
- while($online = $db->fetch_array($query)) {
- $online['time'] = round($online['time'] / 60, 2); // <------
- $thismonth[] = $online;
- }
- $newstatvars[] = "'onlines', 'thismonth', '".addslashes(serialize($thismonth))."'";
- }
复制代码 点评: 虽然 round($online['time'] / 60, 2) 限定了小数位数2位,但超过1位小数的数字在serialize()时将会变成近似值!得到类似的结果- a:2:{i:0;a:3:{s:3:"uid";i:1;s:8:"username";s:3:"root";s:4:"time";d:36.8299999999999982946974341757595539093017578125;}i:1;
复制代码 看到36.82之后跟随的那么一长串数字吧,过长的字符串将会影响cdb_statvars.(onlines total)的写入速度。不过因为论坛统计频率比较低所以对性能影响不会太明显或者不好查到
同样问题存在于接下来的 thismonth 处理。此坑之精巧在于serialize()与unserialize()对于数字都取近似值,保存进去是近似值但取出来unserialize()结果还就是原来的值~
FIX: 把此值作为字符串类型处理即可
-------------------------------------------------------------------------------------------------------------------------
类型: 函数调用
坑爹指数: ★★★
代码:Php代码- $query = $db->query("SELECT COUNT(*) FROM {$tablepre}paymentlog WHERE uid='$discuz_uid'");
- $totalamount = $db->result($query, 1);
- $query = $db->query("SELECT COUNT(*) FROM {$tablepre}paymentlog WHERE uid='$discuz_uid'");
- $totalamount = $db->result($query, 1);
复制代码 点评: 第二个参数1导致不会有结果,前面的查询条件只可能返回一行记录,而不会有第二行所以指定1是错误的。不是说程序猿数数都从0开始;日子9号过了是A号;向程序员朋友借钱1K他会给你1024块。难道这也是临时工代码……
-------------------------------------------------------------------------------------------------------------------------
类型: 重复执行
坑爹指数: ★★★★
代码: admin/prune.inc.php 144,146
Php代码- $db->query("DELETE FROM {$tablepre}rewardlog WHERE tid IN ($tidsdelete)", 'UNBUFFERED');
- $db->query("DELETE FROM {$tablepre}rewardlog WHERE tid IN ($tidsdelete)", 'UNBUFFERED');
复制代码 代码:- modcp/threads.inc.php 232,233
复制代码 Php代码- $db->query("DELETE FROM {$tablepre}threadsmod WHERE tid IN ($tidsdelete)", 'UNBUFFERED');
- $db->query("DELETE FROM {$tablepre}threadsmod WHERE tid IN ($tidsdelete)", 'UNBUFFERED');
复制代码 点评: 当第一次删除之后执行第二次时虽然不会有实际删除操作但一样要做索引查找以匹配记录给数据库带来多余的负担。难道DZ程序员以前玩过linux,关机重启之前要输入sync && sync重复来确保缓冲写入磁盘。mysql好像没这个特性也不健忘吧,它可是数据库耶,不需要你一个命令重复n次才会磨磨蹭蹭去做的呀。
-------------------------------------------------------------------------------------------------------------------------
类型: 变量使用
坑爹指数: ★
代码: ucs/avatar.php
Php代码- define('UC_API', strtolower(($_SERVER['HTTPS'] == 'on' ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'))));
- define('UC_API', strtolower(($_SERVER['HTTPS'] == 'on' ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'))));
复制代码 点评: 未检测 $_SERVER['HTTPS']变量存在就直接使用,$_SERVER['PHP_SELF']在nginx下可能为空。现在Apache虽然依旧是webserver份额老大,但linux已经不是它的天下,nginx异军突起,增长迅速。top1000网站中已经占据25%的份额超过了IIS成了第二。Nginx+PHP-FPM(fastcgi)的搭配已经被越来越多的网站采用。并且nginx的配置文件简洁,好比程序代码,易读性可配置性要比httpd.conf好不少。不过nginx对HTTP1.1标准支持不完整,导致PHP_INFO/PHP_SELF变量可能为空。鉴于DZ7.2代码比较老,Nginx又很新,康盛也懒得为此打补丁吧。
FIX: 在nginx.conf中正确配置SCRIPT_NAME变量传递给后台,PHP中使用$_SERVER['SCRIPT_NAME']
Php代码- define('UC_API',
- (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http'). '://'. $_SERVER['HTTP_HOST'].
- ($_SERVER['PHP_SELF']
- ? substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'))
- : substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/')))
- );
- define('UC_API',
- (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? 'https' : 'http'). '://'. $_SERVER['HTTP_HOST'].
- ($_SERVER['PHP_SELF']
- ? substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'))
- : substr($_SERVER['SCRIPT_NAME'], 0, strrpos($_SERVER['SCRIPT_NAME'], '/')))
- );
复制代码 -------------------------------------------------------------------------------------------------------------------------
类型: 变量使用
坑爹指数: ★★
代码:- include/post.func.php 468 updateattach()
复制代码 Php代码- $anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0;
- $anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0;
复制代码 点评: $anew数组中并没有 'perm'这个键名.下面SQL插入时也未使用此键名变量!难道是个彩蛋?可怎么调出来呢,↑↑↓↓←→←→AB没效果耶~
--------------------------------------------------------------------------------------------------
类型: 变量使用
坑爹指数: ★★★
代码:- include/post.func.php=468 updateattach()
复制代码 Php代码- $anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0;
- $anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0;
复制代码 代码: include/post.func.php=472
Php代码- $db->query("UPDATE {$tablepre}attachments SET readperm='$anew[readperm]',
- $db->query("UPDATE {$tablepre}attachments SET readperm='$anew[readperm]',
复制代码 点评: 未做键名检查————发帖或修改时如果用户删除了附件读取权限值(默认0)为空则页面表单中 name="attachnew[aid][readperm]"这个input对象不会提交,后台接收到的POST变量无此键名。SQL执行无效。
FIX:
Php代码- $anew['readperm'] = $allowsetattachperm && isset($anew['readperm']) ? intval($anew['readperm']) : 0;
- $anew['readperm'] = $allowsetattachperm && isset($anew['readperm']) ? intval($anew['readperm']) : 0;
复制代码 -----------------------------------------------------------------------------------------------------------------
|
|