这天正在写文章,写完了发布后准备看下,博客主页突然提示 Unsupported operand types: int - string 主页都打不开了

排查问题

"Unsupported operand types: int - string"

PHP 8.x 开始变得更严格之后常见的 类型错误


✅ 意思解释:

代码里某个地方 试图用整数(int)减去一个字符串(string),比如:

$count = 10 - "3"; // 在 PHP 7 里会自动转换,PHP 8 会报错

✅ 为什么突然报错?

最近可能:

  • 升级了 PHP 版本(比如从 7.x 升到 8.x)
  • 安装了新插件或主题
  • 修改了某些代码

✅ 怎么定位问题?

方法一:看完整错误信息

Typecho 默认可能只显示一行错误,可以打开 debug 模式,看到完整堆栈:

  1. 打开 config.inc.php
  2. 加一行:
define('__TYPECHO_DEBUG__', true);
  1. 刷新页面,会看到 文件名 + 行号,比如:
Fatal error: Uncaught TypeError: Unsupported operand types: int - string in /var/www/var/Widget/Archive.php:123

方法二:查插件或主题

  • 如果最近装了 新插件换了主题,先禁用它们,看错误是否消失。
  • 特别是 评论数、浏览数、点赞数 这类地方,最容易出现 int - string 的操作。

✅ 举个例子(真实案例):

某用户用了个老插件,代码里写了:

$comments = $db->fetchRow($sql)['count'] - $post->commentsNum;

结果 $post->commentsNum 是字符串 "0",PHP 8 就报错了。


✅ 快速修复(临时):

改代码,把字符串强制转成整数:

$comments = $db->fetchRow($sql)['count'] - (int)$post->commentsNum;

✅ 总结一句话:

某个插件或主题的代码,在 PHP 8 下试图用整数减字符串,导致报错。

可问题是博客已经很久很久没改过啥东西了,既没新插件,也没动过主题代码,加上调试代码后日志显示之前做美化的一段个性化代码的问题。这段博客右侧的统计代码,已经忘记是从哪里抄来的了 🌚 统计在线人数的时候,会在博客主目录下生成一个 $filename = 'online.txt'; // 数据文件

美化代码

<?php
// 在线人数
function online_users() {
    $filename = 'online.txt'; // 数据文件
    $cookiename = 'Nanlon_OnLineCount'; // Cookie 名称
    $onlinetime = 30; // 在线有效时间
    $online = file($filename); 
    $nowtime = $_SERVER['REQUEST_TIME']; 
    $nowonline = array(); 

    foreach ($online as $line) { 
        $row = explode('|', $line); 
        $sesstime = trim($row[1]); 

        if (($nowtime - $sesstime) <= $onlinetime) {
            $nowonline[$row[0]] = $sesstime;
        } 
    } 

    if (isset($_COOKIE[$cookiename])) {
        $uid = $_COOKIE[$cookiename]; 
    } else {
        $vid = 0;
        do {
            $vid++; 
            $uid = 'U' . $vid; 
        } while (array_key_exists($uid, $nowonline)); 
        setcookie($cookiename, $uid); 
    } 

    $nowonline[$uid] = $nowtime;
    $total_online = count($nowonline); 

    if ($fp = @fopen($filename, 'w')) { 
        if (flock($fp, LOCK_EX)) { 
            rewind($fp); 

            foreach ($nowonline as $fuid => $ftime) { 
                $fline = $fuid . '|' . $ftime . "\n"; 
                @fputs($fp, $fline); 
            } 

            flock($fp, LOCK_UN); 
            fclose($fp); 
        } 
    } 

    echo "$total_online"; 
} 

// 字数统计
function allOfCharacters() {
    $chars = 0;
    $db = Typecho_Db::get();
    $select = $db->select('text')->from('table.contents');
    $rows = $db->fetchAll($select);

    foreach ($rows as $row) { 
        $chars += mb_strlen(trim($row['text']), 'UTF-8'); 
    } 

    $unit = '';

    if ($chars >= 10000) { 
        $chars /= 10000; 
        $unit = '万'; 
    } else if ($chars >= 1000) { 
        $chars /= 1000;  
        $unit = '千'; 
    } 

    $out = sprintf('%.2lf %s', $chars, $unit);
    return $out;
}

// 总访问量
function theAllViews(){
    $db = Typecho_Db::get();
    $row = $db->fetchAll('SELECT SUM(VIEWS) FROM `typecho_contents`');
    echo number_format($row[0]['SUM(VIEWS)']);
}

// 响应时间
function timer_start() {
    global $timestart;
    $mtime = explode(' ', microtime());
    $timestart = $mtime[1] + $mtime[0];
    return true; 
}

timer_start();

function timer_stop($display = 0, $precision = 3) {
    global $timestart, $timeend;
    $mtime = explode(' ', microtime());
    $timeend = $mtime[1] + $mtime[0];
    $timetotal = number_format($timeend - $timestart, $precision);
    $r = $timetotal < 1 ? $timetotal * 1000 . " ms" : $timetotal . " s";

    if ($display) {
        echo $r;
    }

    return $r;
}

online.txt 的记录格式

U1|1760161677
                                                                                                                U10|1760161676
U1|1760161678

U12|1760161676
U13|1760161676
U14|1760161676
U15|1760161676
U16|1760161676
U17|1760161677
U18|1760161676
U19|1760161676
U20|1760161677
U21|1760161676
U22|1760161677
U23|1760161676
U24|1760161677
U25|1760161676
U26|1760161677
U27|1760161677
U28|1760161677
U29|1760161677
U30|1760161677
U31|1760161677
U32|1760161677
U33|1760161677
U34|1760161677
U35|1760161677
U36|1760161677
U37|1760161677
U38|1760161677
U39|1760161677
U40|1760161677
U41|1760161677
U42|1760161677
U43|1760161677
U44|1760161677
U45|1760161677
U46|1760161677
U47|1760161677
U48|1760161677
U49|1760161677
U50|1760161677
U51|1760161677
U52|1760161677
U53|1760161677
U54|1760161677
U55|1760161677
U56|1760161677
U57|1760161677


处理问题

这段代码本身 没有直接出现 int - string,但它非常接近出错边缘,而且在 PHP 8 下确实会触发那个错误:


🔥 问题出在这行:

$sesstime = trim($row[1]);

然后:

if (($nowtime - $sesstime) <= $onlinetime) {

✅ 原因:

  • $row[1] 是字符串,比如 "1760161677"
  • trim() 之后还是 字符串
  • PHP 8 不允许直接用 int - string,会报错:
Unsupported operand types: int - string

✅ 修复方法:

$sesstime 强制转成 int

$sesstime = (int)trim($row[1]);

✅ 改完后的完整片段:

foreach ($online as $line) { 
    $row = explode('|', $line); 
    if (count($row) < 2) continue; // 防止空行或格式错误
    $sesstime = (int)trim($row[1]); // ✅ 强制转 int

    if (($nowtime - $sesstime) <= $onlinetime) {
        $nowonline[$row[0]] = $sesstime;
    } 
}

✅ 建议再补一个容错:

如果 online.txt 有空行或格式不对,explode 可能得不到 [1],会出 Undefined offset,所以建议加:

if (count($row) < 2) continue;

✅ 总结一句话:

$sesstime 强制转成 (int),就能彻底解决这个 PHP 8 报错。
最后修改:2025 年 10 月 13 日
钥匙你想请我喝酱香拿铁的话!