背景

对建站有研究的朋友,应该知道这个网站是基于PHP和Typecho搭建的。问题起因是我今年年初手贱将Typecho更新到nightly版本,我发现更新完之后页面自定义的头图全部加载失败,这严重影响了浏览体验。考虑到头图懒加载功能是Miracles主题实现的,所以我先将网站主题切换到了其他主题,但总体来讲我还是最喜欢Miracles,网站用这个主题好几年了,实在不忍心换掉。

也是年底了,终于有空打理一下这个网站,细看了一下代码才发现这个问题其实是一个非常抽象的bug导致的,也不知道为什么头图懒加载逻辑里面会有这么明显的低级错误。

捉虫

简单读了一下源代码,发现页面头图的实现如下(访问源代码):

<img src="<?php echo Utils::addLoadingImages($this->options->CDN, $this->options->loading_image, 'normal'); ?>" data-gisrc="<?php Utils::postBanner($this); ?>">

网站通过 data-gisrc 字段配合gazeimg.js实现前端图片懒加载,进一步追踪前端代码发现 data-gisrc 字段的赋值结果是字符串1。显然问题出在这个字段上,进一步追踪 Utils::postBanner($this); (访问源代码) ,得到:

    /**
     * 缩略图
     */
    public static function postBanner($post){
        if($post->fields->banner && $post->fields->banner=!''): 
            $banner = $post->fields->banner; 
        else: 
            if($GLOBALS['miraclesOptions_randomBanner']==''){
                $banner = '/usr/themes/Miracles/images/postbg/';
                $banner .= srand(mb_strlen($post->title));
                $banner .= mt_rand(1,15).'.jpg';
            }
            else{
                $banner_url = explode(',', $GLOBALS['miraclesOptions_randomBanner']);
                $banner = $banner_url[mt_rand(0,count($banner_url)-1)];
            }
        endif;
        //使用 TimThumb 剪裁
        if($GLOBALS['miraclesIfTimThumb']=='on') {
            if($GLOBALS['miraclesIfTimThumbSize']=='regular'){
                $banner_size = '&h=336&w=564';
            }
            elseif($GLOBALS['miraclesIfTimThumbSize']=='big'){
                $banner_size = '&h=420&w=705';
            }
            elseif($GLOBALS['miraclesIfTimThumbSize']=='large'){
                $banner_size = '&h=504&w=846';
            }
            elseif($GLOBALS['miraclesIfTimThumbSize']=='huge'){
                $banner_size = '&h=560&w=940';
            }
            else{
                //避免有憨憨打错单词,当成 big 来算
                $banner_size = '&h=420&w=705';
            }
            $banner_url = '/usr/themes/Miracles/libs/TimThumb.php';
            $banner = $banner_url.'?src='.$banner.$banner_size;
        }
        //不知道为什么会有奇怪的空格,所以这里用暴力的方法去掉
        $banner = trim($banner);

        echo $banner;
    }

盯着这段代码看了许久,实在不明白为何执行结果严重不符预期,甚至连缩进排版都没有问题...直到外卖送到,我喝了一口豆浆,抬头猛然看见 $post->fields->banner && $post->fields->banner=!'' 这个条件!

如果读者朋友有一定编程功底,应该能理解这个条件的用意是判断banner是否为空,但此刻右边这个'条件'事实上对post.fields.banner进行了赋值,这么干直接导致了函数返回值异常。正确的条件应该是 isset($post->fields->banner) && $post->fields->banner !== ''

这是一个令人啼笑皆非的问题,修正后该函数实现如下:

    /**
     * 缩略图
     */
    public static function postBanner($post) {
        // 初始化横幅变量
        $banner = '';
    
        // 检查$post对象的banner字段是否存在且不为空
        if (isset($post->fields->banner) && $post->fields->banner !== '') {
            $banner = $post->fields->banner;
        } else {
            // 检查是否有随机横幅选项
            if (empty($GLOBALS['miraclesOptions_randomBanner'])) {
                $bannerDir = '/usr/themes/Miracles/images/postbg/';
                $bannerNumber = mt_rand(1, 15); // 生成1到15之间的随机数
                $banner = $bannerDir . $bannerNumber . '.jpg';
            } else {
                // 如果有随机横幅选项,则从选项中随机选择一个
                $bannerUrls = explode(',', $GLOBALS['miraclesOptions_randomBanner']);
                $randomIndex = mt_rand(0, count($bannerUrls) - 1);
                $banner = $bannerUrls[$randomIndex];
            }
        }
    
        // 检查是否使用TimThumb进行图片处理
        if ($GLOBALS['miraclesIfTimThumb'] === 'on') {
            // 根据配置确定图片尺寸
            switch ($GLOBALS['miraclesIfTimThumbSize']) {
                case 'regular':
                    $bannerSize = '&h=336&w=564';
                    break;
                case 'big':
                    $bannerSize = '&h=420&w=705';
                    break;
                case 'large':
                    $bannerSize = '&h=504&w=846';
                    break;
                case 'huge':
                    $bannerSize = '&h=560&w=940';
                    break;
                default:
                    // 默认使用'big'尺寸
                    $bannerSize = '&h=420&w=705';
            }
    
            // 构造TimThumb处理后的URL
            $timThumbUrl = '/usr/themes/Miracles/libs/TimThumb.php';
            $banner = $timThumbUrl . '?src=' . urlencode($banner) . $bannerSize;
        }

        $banner = trim($banner);

        echo $banner;
    }

将更改上传至服务端,问题解决。

碎碎念

这么明显的问题,之前页面头图到底是怎么正确显示的?难道还有其他地方依赖这个bug运行吗?虽然修好了,但是我愈发感到不安...

对 对吗?


Q.E.D.
0xC4A1

2024-12-15 07:55 提笔于福州
2024-12-15 08:33 完稿于福州

最后修改:2025 年 01 月 15 日
如果这对你有用,我乐意之至。