Discuz 7.2坑爹集锦-PHP篇 update 20120525

Discuz 7.2坑爹集锦-PHP篇

ucc代表uc_client目录
ucs代表uc_server目录


类型:        代码风格
坑爹指数:     ☆
点评:        DZ代码不错,不过其代码风格对于维护来说比较痛苦。
本来应该尽量避免在判断中赋值表达式避免习惯之后本意的逻辑比较操作误写成赋值操作符而加大debug困难,而DZ恰恰相反不喜欢使用中间变量往往是变量赋值表达式直接用于IF判断。估计康盛幸福的程序猿用的都是宽屏显示器,所以他们相当吝啬换行,人类已经已经阻止不了一条语句可以超出19寸16:9宽屏液晶许多许多犹如黄河泛滥一发不可收拾直达长江入海口~
对于小括号使用不严格,IF/ELSEIF/WHILE/FOREACH后面紧接小括号而没有加空格,虽然毫不影响执行结果,但对于非函数调用还是在关键字和括号之间留个空吧。

 

----------------------------------------------------------------------------------------------------------------------------


类型:        表情符号
坑爹指数:    ☆
代码:        http://yourdomain/faq.php?action=faq&id=5&messageid=32
点评:        cry的表情符号代码 :\'(,正确应该是 :'( ,建表SQL插入数据时代码有问题。

----------------------------------------------------------------------------------------------------------------------------

类型:        代码风格
坑爹指数:    ☆

代码:        include/global.func.php ~400

function forumperm($permstr) {
    global $groupid, $extgroupids;

    $groupidarray = array($groupid);
    foreach(explode("\t", $extgroupids) as $extgroupid) {
        if($extgroupid = intval(trim($extgroupid))) {    <------赋值还是判断? 太容易混淆人了
            $groupidarray[] = $extgroupid;
        }
    }
}
点评:    赋值还是判断? 太容易混淆人了
FIX:
    foreach(explode("\t", $extgroupids) as $extgroupid) {
        $extgroupid = intval($extgroupid);
        $extgroupid > 0 && $groupidarray[] = $extgroupid;
    }

----------------------------------------------------------------------------------------------------------------------------

类型:        变量类型
坑爹指数:    ★
代码:       
include/newreply.inc.php=240
    $parseurloff = !empty($parseurloff);
include/newthreads.inc.php~375
    $parseurloff = !empty($parseurloff);

点评:        会导致变量为空字符串非int类型。因为mysql比较宽松对SQL标准支持不佳,对于字段类型输入值校检不严格:如果是int类型字段,你输入的是空字符那么会自动给你转换成0。但这对以后的移植不利,对于个人以后在技术上的发展未必好。类似的是mysql LIMIT的写法,既支持LIMIT {[offset,] row_count}格式也支持LIMIT {row_count OFFSET offset}标准,但大部分人就只知道使用前者而不知道后者标准格式。

FIX:

$parseurloff = empty($parseurloff) ? 0 : 1;


----------------------------------------------------------------------------------------------------------------------------

类型:        未定义变量(Undefined Variable/Index)
坑爹指数:    ★★
代码:        多处。以 ucs/view/default下htm模板文件居多。
Undefined index: allowadminlog
----
Modified : ucs/view/default/admin_admin.htm
Modified : ucs/view/default/admin_feed.htm
Modified : ucs/view/default/admin_mail.htm
Modified : ucs/view/default/admin_note.htm

<li><input type="checkbox" name="allowadminlog" value="1" class="checkbox" {if $admin[allowadminlog]} checked="checked" {/if}/>{lang admin_allow_log}</li>
点评:        PHP弱类型语言,变量不预声明即可使用,方便。但对未定义变量进行操作时会导致PHP抛出一个Notice,这在PHP中不算啥错误,但会给DZ调试带来一些意想不到的问题:DZ采用XML作为ajax交流格式,不知道是js代码编写问题还是XML本身问题,如果php.ini中打开display_error开关,当前台页面调用ajax操作遇上PHP抛出日志信息时前台将会出错,firebug中往往不是提示XML错误而是显示common.js某行错误(比如 Error:s is NULL, $ is not exists之类的)。不熟悉的还以为是JS代码有问题,其实根源在于PHP代码不严谨,而XML格式复杂严格。个人觉得网站ajax使用JSON比XML更佳,无论是后台程序处理输出代码还是前台JS解析数据。对于PHP数组,一条echo json_encode($array)即可快捷返回JSON格式信息给前台;而前台JS一条eval "{data}"命令即可立刻解析成JS变量对象(处于安全考虑,现在不推荐使用eval来解析,如果使用jQuery那么可以使用$.ajax()的dataType:json或者直接$.getJSON()来直接获得数组变量)。

FIX:    关闭php.ini中display_error选项。或者修改DZ代码使用isset()或者!empty()判断变量,比如 

{if isset($admin[allowadminlog]) && $admin[allowadminlog]} 或者 {if !empty($admin[allowadminlog])} 

 

----------------------------------------------------------------------------------------------------------------------------

类型:        未定义变量
坑爹指数:    ★★
代码:        pm.php=47
    $pmstatus = uc_pm_checknew($discuz_uid, 4);
    $filter = !empty($filter) && in_array($filter, array('newpm', 'privatepm', 'announcepm')) ? $filter : ($pmstatus['newpm'] ? 'newpm' : 'privatepm'); 
点评:         未对返回值$pmstatus['newpm']有效性进行判断

 

----------------------------------------------------------------------------------------------------------------------------

类型:        未定义变量
坑爹指数:    ★★
代码:        pm.php=61
    foreach($ucdata['data'] as $pm) {
    ....
    } 
点评:        未对 $ucdata变量'data'键有效做判断就直接开始循环,相当于对一个可能不存在的变量进行访问并迭代。问题出在line49调用uc_pm_list()对$ucdata赋值,而ucc/control/pm.php: onls() 函数返回值$result未初始化'data'键名。
虽然PHP是弱类型,但好歹对函数返回值先做个判断再操作吧。偷懒也就少些几行代码,可调试维护时花的时间就多了。

----------------------------------------------------------------------------------------------------------------------------

类型:        变量错误
坑爹指数:    ★★
代码:        memcp.php line264.
        
$styleid = empty($styleidnew) ? $styleid : $styleidnew; 
点评:    这位兄弟是不是求加薪不成功,没有新生活导致见new就失望于是也不给变量new生活。
FIX:    $styleidnew = empty($styleidnew) ? $styleid : $styleidnew;

----------------------------------------------------------------------------------------------------------------------------

 

类型:        变量使用
坑爹指数:    ★★
代码:        include/global.func.php 1514
updateprompt()函数中$db->query("UPDATE {$tablepre}members SET prompt=prompt^1 WHERE uid='$discuz_uid' AND prompt=prompt|1", 'UNBUFFERED'); 
点评:        函数并未global声明$discuz_uid变量
FIX:        使用$uid替代

----------------------------------------------------------------------------------------------------------------------------

 

类型:        变量使用
坑爹指数:    ★★★★
代码:        admin/member=990:
    $db->query("INSERT INTO {$tablepre}medallog (uid, medalid, type, dateline, expiration, status) VALUES ('$uid', '".$modmedal[medalid]."', '0', '$timestamp', '".$modmedal['expiration']."', '$medalstatus')"); 
点评:        $modmedal[medalid]缺少单引号。在双引号中使用数组变量DZ都采用不带单引号的方式,比如 "... $modmedal[medalid] "而不使用繁琐但更安全的大括号方式 "... {$modmedal['medalid']} ",很少使用例子中拼接字符串方式。这次难道用一次拼接,结果习惯的力量那么大还是用了不带单引号的访问方式。也许,也许小哥加班熬夜,眼花,没看到前后的两个小点还以为是在双引号的范围内。
好的代码习惯有时候能减少不少错误,也便于快速debug。比如尽量少在判断中使用赋值表达式,比如用.号拼接字符串时在其后或其前加个空格。后者还有个小好处就是用鼠标双击变量时可以正确选定期望的范围。


FIX:        把变量键名加上单引号 $modmedal['medalid']


----------------------------------------------------------------------------------------------------------------------------

类型:        变量类型
坑爹指数:    ★★★
代码:        include/global.func.php=1339
        $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' $pageviewsadd WHERE sid='$sid'"); 
点评:        更新状态时seccode值有时会出现验证码字符串而非int。写入失败,后台时常有此日志。问题出在那儿一直没找到,算一个悬疑历史问题坑
FIX:        暂时解决办法判断类型,使用is_numberic($seccode)判断是否执行SQL

----------------------------------------------------------------------------------------------------------------------------


类型:        输入错误
坑爹指数:    ★★
位置:        members.inc.php~1900
    $db->query("UPDATE {$tablepre}members set uid=uid $updatesql WHERE $conditions", 'UNBUFFTERED'); 
点评:        俺自从用上gVim之后写代码彻底抛弃庞大臃肿的zend statio了,emeditor用得也很少。感觉vim用熟悉之后实在太舒服,不但提高速度并且更加高效,难怪对于VIM和Emacs,一条经典评价“VIM是编辑器之神,Emacs是神之编辑器”。对于“伪装成文本编辑器而实际干的是操作系统活的Emacs”,我不想买脚踏板所以还是安心用VIM吧。经常出现用j下移鼠标时文字排版大幅度变化或者进入莫名其妙的Ex模式一时退不出去,后来才发现是大写锁定了————以前输入大写习惯是使用Caps Locks锁定大写然后再输入字符,不过有时候忘记解除大写锁定有时候解除操作却按到大写上的帽子上……哦是Tab上,于是j变成了<S-j>删除段尾回车让下一行并入当前行。所以后来我就改变习惯,除非特定情况,大写字符使用Shift组合键来输入。但这又产生一个新问题,因为输入时手掌形态改变,肌肉还没习惯新的活动范围所以以前顺手的敲击活动多少带点别扭,结果就是有些长字符串输入错误。写代码的这位兄弟估计没弹过钢琴,一激情下蹦哒出一个不和谐音符。不过还好没严重危害。

修改DZ时我使用TC进行了全文搜索替换,但后来又见着UNBUFFERED这位老哥。心中还奇怪难道TC没搜索到么,仔细一看,原来中间带了个套~

FIX: UNBUFFTERED 修改为 UNBUFFERED

对于爱好Totalcommand的,右手腕下老茧厚的极力这儿插队推荐VIM:
【简明 Vim 练级攻略】
http://coolshell.cn/articles/5426.html 
http://www.oschina.net/question/55577_27380

对于嫌鼠标多余的键盘控推荐Emacs:
【为何Emacs和Vim被称为两大神器】
http://www.oschina.net/question/12_15010

 

 

----------------------------------------------------------------------------------------------------------------------------

类型:        除零错误
坑爹指数:    ★★★★
代码:        topicadmin.php 330
$db->query("UPDATE {$tablepre}threads SET .... rate='".intval(@($fpost['rate'] / abs($fpost['rate'])))."', moderated='1' WHERE tid='$newtid'"); 
代码:        topicadmin.php~240    
@$firstpost['rate'] = $firstpost['rate'] / abs($firstpost['rate']); 
代码:        space.php 95
@$percent = round($member['posts'] * 100 / $db->result_first("SELECT COUNT(*) FROM {$tablepre}posts"), 2); 
代码:        stats.php~260
    $pageviewavg = sprintf ("%01.2f", ($stats_total['visitors'] ? $stats_total['hits'] / $stats_total['visitors'] : 0));
    !$post && $post = 1;
    $activeindex = round(($membersaddavg / $members + $postsaddavg / $posts) * 1500 + $threadreplyavg * 10 + $mempostavg * 1 + $mempostpercent / 10 + $pageviewavg); 
代码:        stats.php 多处
    $avgmodactioncount = @($totalmodactioncount / count($members));
    @$width = intval(370 * $count / $max);
    @$percent = sprintf ("%01.1f", 100 * $count / $sum);
    $membersaddavg = round($members / $runtime); 
代码:        admin/forums.inc.php~640
    $forum['autoclose'] = $forum['autoclose'] / abs($forum['autoclose']); 
代码:        stats.php~550
    foreach($extendedcredit as $i => $members) {
                @$width = intval(370 * $members['credits'] / $max);
                $width += 2; 
代码:        stats.php~750:
        'avgoffdays' => @($totaloffdays / count($members)),
        'avgthismonthposts' => @($totalthismonthposts / count($members)),
        'avgtotalol' => @($totalol / count($members)),
        'avgthismonthol' => @($totalthismonthol / count($members)),
        'avgmodactions' => @($totalmodactions / count($members)),
                'avgthismonthposts' => @($totalthismonthposts / count($members)),
                'avgtotalol' => @($totalol / count($members)),
                'avgthismonthol' => @($totalthismonthol / count($members)),
                'avgmodactions' => @($totalmodactions / count($members)),


代码:    misc.php~720
        $threadrate = @intval(@($post['rate'] + $rate) / abs($post['rate'] + $rate)); 
点评:    避免除零错误是学习编程时的基本概念,没想到在DZ中还能挖出这么多来。某些问题除零错误是在建站初始无对应数据时发生,待正常运作之后就不会发生。而多数将伴随你网站终身,不断充实你的error-log文件~ 如果说某位程序大猿数学不好不知道除零错误还可以理解,但恶劣的是有些人明知道这个问题却使用@来抑制错误,这就属于有意找抽的……

----------------------------------------------------------------------------------------------------------------------------

类型:        字段名错误
坑爹指数:    ★★★
代码:        include/global.func.php 1622-1623
//$sql = "SELECT * FROM {$tablepre}feeds WHERE $where ORDER BY feed_id DESC LIMIT $start_limit, $conf[num]"; // DZ自己注释掉的
$sql = "SELECT * FROM {$tablepre}feeds WHERE $where ORDER BY feedid DESC LIMIT $start_limit, $conf[num]"; 
点评:        dz.cdb_feeds这个表的主键是feed_id, ucenter.feeds表的主键是feedid.你们程序员通过注释把feed_id改成feedid,但是dz数据表没升级啊。难道我补丁没下全?这个坑不影响结果集,但对feed读取时的排序有影响(使用在ORDER BY中)

 

FIX:        修改feed_id 为 feedid



----------------------------------------------------------------------------------------------------------------------------
类型:        流程错误
坑爹指数:    ★
代码:        my.php~710
if($db->result_first("SELECT COUNT(*) FROM {$tablepre}favoritethreads WHERE tid='$tid' AND uid='$discuz_uid'")) {
showmessage('favoritethreads_exists', dreferer());
}
$timestamp = time();
$attention_exists = $db->result_first("SELECT COUNT(*) FROM {$tablepre}favoritethreads WHERE tid='$tid' AND uid='$discuz_uid'");  // <=== 
点评:        前面查询如果存在结果(即有收藏)就已经退出执行流程并提示用户 'favoritethreads_exists',下面干嘛又执行一次呢,难道真会再次运行?

 

---------------------------------------------------------------------------------------------------------------------------

类型:        流程错误
坑爹指数:    ★★
代码:        admin/cpanel.share.php 29 adminsession()函数
            $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:        先判断再赋值

            } 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
    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
        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 在线时间统计
    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: 把此值作为字符串类型处理即可



----------------------------------------------------------------------------------------------------------------------------

类型:        函数调用
坑爹指数:    ★★★
代码:        memcp.php 544,615
        $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
$db->query("DELETE FROM {$tablepre}rewardlog WHERE tid IN ($tidsdelete)", 'UNBUFFERED'); 
代码:        modcp/threads.inc.php 232,233
$db->query("DELETE FROM {$tablepre}threadsmod WHERE tid IN ($tidsdelete)", 'UNBUFFERED'); 
点评:        当第一次删除之后执行第二次时虽然不会有实际删除操作但一样要做索引查找以匹配记录给数据库带来多余的负担。难道DZ程序员以前玩过linux,关机重启之前要输入sync && sync重复来确保缓冲写入磁盘。mysql好像没这个特性也不健忘吧,它可是数据库耶,不需要你一个命令重复n次才会磨磨蹭蹭去做的呀。

----------------------------------------------------------------------------------------------------------------------------

类型:        变量使用
坑爹指数:    ★
代码:        ucs/avatar.php
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']
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()
$anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0; 
点评:        $anew数组中并没有 'perm'这个键名.下面SQL插入时也未使用此键名变量!难道是个彩蛋?可怎么调出来呢,↑↑↓↓←→←→AB没效果耶~

 

----------------------------------------------------------------------------------------------------------------------------

类型:        变量使用
坑爹指数:    ★★★
代码:        include/post.func.php=468 updateattach()
$anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0; 
代码:        include/post.func.php=472
$db->query("UPDATE {$tablepre}attachments SET readperm='$anew[readperm]', 
点评:        未做键名检查————发帖或修改时如果用户删除了附件读取权限值(默认0)为空则页面表单中 name="attachnew[aid][readperm]"这个input对象不会提交,后台接收到的POST变量无此键名。SQL执行无效。

FIX:       

$anew['readperm'] = $allowsetattachperm && isset($anew['readperm']) ? intval($anew['readperm']) : 0;

 

----------------------------------------------------------------------------------------------------------------------------

类型:        变量使用
坑爹指数:    ★★★
代码:        include/post.func.php=216
$anew['perm'] = $allowsetattachperm ? $anew['perm'] : 0; 
代码:        include/post.func.php=472
$attach['perm'] = $allowsetattachperm ? intval($attachperm[$key]) : 0;
点评:        同上

----------------------------------------------------------------------------------------------------------------------------

类型:        变量使用
坑爹指数:    ★★
代码:        pm.php=47
    $pmstatus = uc_pm_checknew($discuz_uid, 4);
    $filter = !empty($filter) && in_array($filter, array('newpm', 'privatepm', 'announcepm')) ? $filter : ($pmstatus['newpm'] ? 'newpm' : 'privatepm');
点评:         未对返回值$pmstatus['newpm']有效性进行判断

 

----------------------------------------------------------------------------------------------------------------------------

类型:        变量使用
坑爹指数:    ★★
代码:        pm.php=61
    foreach($ucdata['data'] as $pm) {
    ....
    } 
点评:        未对 $ucdata变量'data'键有效做判断就直接开始循环,相当于对一个可能不存在的变量进行访问并迭代。问题出在line49调用uc_pm_list()对$ucdata赋值,而ucc/control/pm.php: onls() 函数返回值$result未初始化'data'键名。
虽然PHP是若类型,但好歹对函数返回值先做个判断再操作吧。偷懒也就少些几行代码,可调试维护时花的时间就多了。

----------------------------------------------------------------------------------------------------------------------------



类型:        字符处理
坑爹指数:    ★★★★
症状:        边栏模块最新帖最新回复对标题中单引号显示为&#39;
点评:        不知道为何一直没修复这个bug,难道是我修改其他代码关联影响到这儿?反正根源是DZ在入库时htmlspecialchars()只对双引号处理而未对单引号转义
FIX:        修改如下文件调用带ENT_QUOTES参数的htmlspecialchars()函数来替代str_replace()函数处理
include/request.func.php
    $datalist[$data['tid']]['subject'] = isset($data['subject']) ? str_replace('\\\'', '&#39;', addslashes($data['subject'])) : NULL;
FIXTO:
    $datalist[$data['tid']]['subject'] = isset($data['subject']) ? htmlspecialchars(htmlspecialchars_decode($data['subject']), ENT_QUOTES) : NULL;
然后修改global.func.php, ucs/mode/base.php, ucclient/mode/base.php的cutstr()函数:
    //$string = str_replace(array('&amp;', '&quot;', '&lt;', '&gt;', '&#39;'), array('&', '"', '<', '>', '\''), $string);
    $string = htmlspecialchars_decode($string, ENT_QUOTES);
....
    //$strcut = str_replace(array('&', '"', '<', '>', '\''), array('&amp;', '&quot;', '&lt;', '&gt;', '&#39;'), $strcut);
    $strcut = htmlspecialchars($strcut, ENT_QUOTES);


----------------------------------------------------------------------------------------------------------------------------

类型:        页面效果
坑爹指数:    ★
症状:        当bbcodeoff时帖子中‘最后修改’的标签混乱
FIX:        include/discuzcode.func.php: 添加一段判断
line126开始的判断拆分开
    if(!$bbcodeoff && $allowbbcode) {// line126
        ....
    }            // line201
修改成
    if($allowbbcode) {        // line126
        if (!$bbcodeoff) {
            .....
        }               
        // 添加开始
        elseif ($bbcodeoff && substr($message, 0, 5) === '[i=s]') {        // allow parse '[i=s]last modified by [/i]' even if bbcodeoff
            $message = preg_replace('/^\[i=s\](.*)\[\/i\]/', '<i class="pstatus">\\1</i>', $message );
        } //添加结束
    }        // line201+n




----------------------------------------------------------------------------------------------------------------------------

类型:        逻辑错误
坑爹指数:    ★★★★
代码:        topicadmin.php ~320    分割主题
$db->query("UPDATE {$tablepre}posts SET first='1', subject='$subject' WHERE fid='$waiting_fid' AND pid='".$splitauthors[0]['pid']."'" );    
点评:        first='1'只设置了一次,如果分割主题时选择包含了1楼那么原主题内变成1楼的帖子的first依然为0. 本来在不支持事务的MyISAM引擎上做分隔主题这种操作就具有一定危险性,不过DZ更直接增加了这个这个机率。提醒你分割主题时不要把顶楼分割出去哟,不然剩下变成1楼的帖子将会成为孤儿。多来几次你就会明确记住这个准则了,也不会因为数据库偶尔非原子性操作带来的随机故障而烦恼。这多么简单啊。呵呵
FIX:        line327   
$db->query("UPDATE {$tablepre}posts SET subject='".addslashes($thread['subject'])."' WHERE pid='$fpost[pid]'");
修改为
$db->query("UPDATE {$tablepre}posts SET first=1, subject='".addslashes($thread['subject'])."' WHERE pid='$fpost[pid]'");




----------------------------------------------------------------------------------------------------------------------------

类型:        执行流程
坑爹指数:    ★★★
代码:        include/common.inc.php 349
        $forum = $db->fetch_first("SELECT t.tid, t.closed,".(defined('SQL_ADD_THREAD') ? SQL_ADD_THREAD : '')." f.*, ff.* $accessadd1 $modadd1, f.fid AS fid
            FROM {$tablepre}threads t ....
        $tid = $forum['tid'];
点评:        如果查询结果空$forum将会false,不做判断而直接赋值给$tid会出错,否则就可能要继续执行到后继的viewthreads.php中的判断,浪费系统资源。另外viewthreads.php 也未对$tid判断即以此为条件直接查询,徒增DB负担(MySQL会有 ‘Impossible WHERE noticed after reading const tables’ )
FIX:        应该查询结束后立即对$forum做判断并设置一个变量作标志再考虑给$tid赋值然后在当前页面最底部判断,如果标志真则立即输出404头直接退出。

----------------------------------------------------------------------------------------------------------------------------

 

类型:        未知
坑爹指数:    ★★★★
代码:        uc_client/model/note.php=64
        foreach((array)$this->apps as $appid => $app) {
            $appid = $app['appid'];        <---------??
            if($appid == intval($appid)) {
                if($appids && !in_array($appid, $appids)) {
                    $appadd[] = 'app'.$appid."='1'";
                } else {
                    $varadd[] = "('noteexists{$appid}', '1')";
                }
            }
        }
点评:        一直没研究明白这个赋值要表达什么意思。难道这位当时正在韩大嘴语录,看到“瞄的是A,想的是B,解说的是C,观众以为是D,其实指的是E”这一段,顿悟,遂看到是代码,想的是妹妹,说的是工资,同事以为是八卦,领导以为是抽风~
FIX:    以我类人猿的智商估计可能是这样:
        foreach((array)$this->apps as $appid => $app) {
            if(intval($appid) == $app['appid']) {        // 帮你精简一行代码
                if($appids && !in_array($appid, $appids)) {
                    $appadd[] = 'app'.$appid."='1'";
                } else {
                    $varadd[] = "('noteexists{$appid}', '1')";
                }
            }
        }

-------------------------------------------------------------------------------------------------------------------------

类型:        代码错误
坑爹指数:    ★★
代码:        include/request.func.php=372

            case 'hourposts';

点评:        这个芝麻坑真难发现啊
FIX:       

case 'hourposts':

 

-------------------------------------------------------------------------------------------------------------------------

类型:        安全漏洞
坑爹指数:    ★★★
代码:        include/newreply.inc.php~381

    if($modnewreplies) {
        $db->query("UPDATE {$tablepre}forums SET todayposts=todayposts+1 WHERE fid='$fid'", 'UNBUFFERED');
        showmessage('post_reply_mod_succeed', "forumdisplay.php?fid=$fid");
    } else {

代码:        include/newthread.inc.php~440

    if($modnewthreads) {
        $db->query("UPDATE {$tablepre}forums SET todayposts=todayposts+1 WHERE fid='$fid'", 'UNBUFFERED' );
        ...
    } else {

点评:        如果此帖发布需要审核,那么这段代码将会造成一个漏洞:此用户就可以发帖而不受发帖间隔时间的限制!如果“会员每小时发帖数限制”也无限制那么虽然帖子不会直接显示出来但将会造成数据库增大给后台管理造成麻烦。
FIX:        在showmessage()之前更新用户最近发帖时间戳

        $db->exec("UPDATE {$tablepre}members SET lastpost='$timestamp' WHERE uid='$discuz_uid'", 'UNBUFFERED');    



-------------------------------------------------------------------------------------------------------------------------

 类型:        代码错误
坑爹指数:    ★
代码:        admin/db.inc.php=82

        showtablerow('', '', '<input class="checkbox" name="chkall" onclick="checkAll(\'prefix\', this.form, \'customtables\', \'chkall\', true)" checked="checked" type="checkbox" id="chkalltables" /><label for="chkalltables"> '.lang('db_export_custom_select_all').' - '.lang('db_export_discuz_table')).'</label>';


点评:        老眼昏花括弧没包好哟
FIX:        

showtablerow('', '', '<input class="checkbox" name="chkall" onclick="checkAll(\'prefix\', this.form, \'customtables\', \'chkall\', true)" checked="checked" type="checkbox" id="chkalltables" /><label for="chkalltables"> '.lang('db_export_custom_select_all').' - '.lang('db_export_discuz_table').'</label>');

 

本来计划单独开一PHP优化篇。后来发现下面坑爹代码多数会影响性能(PHP以及数据库执行),修复了bug即优化。故合并为一篇。

补充个优化PHP的:
如果你的服务器http server支持Gzip/deflate压缩,那么就使用http serer提供的功能,并到后台,全局-优化设置-服务器优化把“页面 Gzip 压缩”选项设定为否。
如果设定“是”,那么将使用DZ提供的一个gzip PHP插件来实现压缩页面。缺点是耗费PHP脚本执行时间,对于nginx+php-fpm模式运行更容易出现502错误。

 


版权曾经拥有,欢迎网上分享

转载请保留链接 http://my.oschina.net/u/126398/blog/38873


你可能感兴趣的:(PHP,mysql,discuz)