升级至PHP8.1时gmdate报类型错误,RECTOR未检测该问题
PHP8.1升级后gmdate函数报错:Unsupported operand types: string + float
升级CMS系统到PHP8.1时,触发Unsupported operand types: string + float错误,该错误与gmdate函数的时间戳参数计算有关。原代码在PHP7.4环境下可正常运行,多次使用RECTOR工具未检测到问题。
问题代码
if ( $userdata['user_id'] != ANONYMOUS ) { switch ( $userdata['user_time_mode'] ) { case MANUAL_DST: $dst_sec = $userdata['user_dst_time_lag'] * 60; return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec), $translate) : gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec); break; case SERVER_SWITCH: if (!empty($gmepoch) && is_long($gmepoch)) { $dst_sec = date('I', $gmepoch) * $userdata['user_dst_time_lag'] * 60; } else { $dst_sec = date('I') * $userdata['user_dst_time_lag'] * 60; } return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec), $translate) : gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec); break; case FULL_SERVER: return ( !empty($translate) ) ? strtr(date($format, $gmepoch), $translate) : date($format, $gmepoch); break; case SERVER_PC: if ( isset($pc_dateTime['pc_timezoneOffset']) ) { $tzo_sec = $pc_dateTime['pc_timezoneOffset']; } else { $user_pc_timeOffsets = explode("/", (string) $userdata['user_pc_timeOffsets']); $tzo_sec = $user_pc_timeOffsets[0]; } return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + $tzo_sec), $translate) : gmdate($format, $gmepoch + $tzo_sec); break; case FULL_PC: if ( isset($pc_dateTime['pc_timeOffset']) ) { $tzo_sec = $pc_dateTime['pc_timeOffset']; } else { $user_pc_timeOffsets = explode("/", (string) $userdata['user_pc_timeOffsets']); $tzo_sec = $user_pc_timeOffsets[1] ?? ''; } return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + $tzo_sec), $translate) : gmdate($format, $gmepoch + $tzo_sec); break; default: return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + (3600 * $tz)), $translate) : gmdate($format, $gmepoch + (3600 * $tz)); break; } } else { switch ( $board_config['default_time_mode'] ) { case MANUAL_DST: $dst_sec = $board_config['default_dst_time_lag'] * 60; return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec), $translate) : @gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec); break; case SERVER_SWITCH: if (!empty($gmepoch) && is_long($gmepoch)) { $dst_sec = date('I', $gmepoch) * $userdata['user_dst_time_lag'] * 60; } else { $dst_sec = date('I') * $userdata['user_dst_time_lag'] * 60; } return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec), $translate) : @gmdate($format, $gmepoch + (3600 * $tz) + $dst_sec); break; case FULL_SERVER: return ( !empty($translate) ) ? strtr(@date($format, $gmepoch), $translate) : @date($format, $gmepoch); break; case SERVER_PC: if ( isset($pc_dateTime['pc_timezoneOffset']) ) { $tzo_sec = $pc_dateTime['pc_timezoneOffset']; } else { $tzo_sec = 0; } return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + $tzo_sec), $translate) : @gmdate($format, $gmepoch + $tzo_sec); break; case FULL_PC: if ( isset($pc_dateTime['pc_timeOffset']) ) { $tzo_sec = $pc_dateTime['pc_timeOffset']; } else { $tzo_sec = 0; } return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + $tzo_sec), $translate) : @gmdate($format, $gmepoch + $tzo_sec); break; default: return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + (3600 * $tz)), $translate) : @gmdate($format, $gmepoch + (3600 * $tz)); break; } }
错误原因
PHP8.0及以上版本对类型运算的严格性显著提升,不再自动将非数值字符串隐式转换为数字。原代码中存在多个风险点:
FULL_PC分支中,当user_pc_timeOffsets拆分后无对应值时,$tzo_sec默认赋值为空字符串'',与数字类型的$gmepoch相加触发类型错误。- 从
$userdata、$board_config读取的配置字段(如user_dst_time_lag、user_pc_timeOffsets拆分后的值)可能为字符串类型,PHP7.4会自动转换为数字,PHP8.1则直接报错。 is_long()函数已被废弃,PHP8.0+应使用is_int()或is_integer(),虽然这不是直接报错原因,但会导致逻辑判断异常。
修复方案
- 强制转换数值类型:对所有参与算术运算的变量,使用
(int)或intval()强制转换为整数类型,确保运算时类型统一。 - 修正默认值:将空字符串默认值改为
0,避免字符串与数字相加。 - 替换废弃函数:将
is_long()替换为is_int(),适配PHP8+的类型判断。
修改后的代码
if ( $userdata['user_id'] != ANONYMOUS ) { switch ( $userdata['user_time_mode'] ) { case MANUAL_DST: // 强制转换为整数 $dst_sec = (int)$userdata['user_dst_time_lag'] * 60; return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec), $translate) : gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec); break; case SERVER_SWITCH: if (!empty($gmepoch) && is_int($gmepoch)) { // 替换is_long为is_int $dst_sec = date('I', $gmepoch) * (int)$userdata['user_dst_time_lag'] * 60; } else { $dst_sec = date('I') * (int)$userdata['user_dst_time_lag'] * 60; } return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec), $translate) : gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec); break; case FULL_SERVER: return ( !empty($translate) ) ? strtr(date($format, $gmepoch), $translate) : date($format, $gmepoch); break; case SERVER_PC: if ( isset($pc_dateTime['pc_timezoneOffset']) ) { $tzo_sec = (int)$pc_dateTime['pc_timezoneOffset']; } else { $user_pc_timeOffsets = explode("/", (string) $userdata['user_pc_timeOffsets']); // 确保转换为整数,避免空字符串 $tzo_sec = isset($user_pc_timeOffsets[0]) ? (int)$user_pc_timeOffsets[0] : 0; } return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + $tzo_sec), $translate) : gmdate($format, $gmepoch + $tzo_sec); break; case FULL_PC: if ( isset($pc_dateTime['pc_timeOffset']) ) { $tzo_sec = (int)$pc_dateTime['pc_timeOffset']; } else { $user_pc_timeOffsets = explode("/", (string) $userdata['user_pc_timeOffsets']); // 默认值改为0,而非空字符串 $tzo_sec = $user_pc_timeOffsets[1] ?? 0; // 强制转换为整数 $tzo_sec = (int)$tzo_sec; } return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + $tzo_sec), $translate) : gmdate($format, $gmepoch + $tzo_sec); break; default: return ( !empty($translate) ) ? strtr(gmdate($format, $gmepoch + (3600 * (int)$tz)), $translate) : gmdate($format, $gmepoch + (3600 * (int)$tz)); break; } } else { switch ( $board_config['default_time_mode'] ) { case MANUAL_DST: $dst_sec = (int)$board_config['default_dst_time_lag'] * 60; return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec), $translate) : @gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec); break; case SERVER_SWITCH: if (!empty($gmepoch) && is_int($gmepoch)) { // 替换is_long为is_int $dst_sec = date('I', $gmepoch) * (int)$userdata['user_dst_time_lag'] * 60; } else { $dst_sec = date('I') * (int)$userdata['user_dst_time_lag'] * 60; } return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec), $translate) : @gmdate($format, $gmepoch + (3600 * (int)$tz) + $dst_sec); break; case FULL_SERVER: return ( !empty($translate) ) ? strtr(@date($format, $gmepoch), $translate) : @date($format, $gmepoch); break; case SERVER_PC: if ( isset($pc_dateTime['pc_timezoneOffset']) ) { $tzo_sec = (int)$pc_dateTime['pc_timezoneOffset']; } else { $tzo_sec = 0; } return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + $tzo_sec), $translate) : @gmdate($format, $gmepoch + $tzo_sec); break; case FULL_PC: if ( isset($pc_dateTime['pc_timeOffset']) ) { $tzo_sec = (int)$pc_dateTime['pc_timeOffset']; } else { $tzo_sec = 0; } return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + $tzo_sec), $translate) : @gmdate($format, $gmepoch + $tzo_sec); break; default: return ( !empty($translate) ) ? strtr(@gmdate($format, $gmepoch + (3600 * (int)$tz)), $translate) : @gmdate($format, $gmepoch + (3600 * (int)$tz)); break; } }
内容的提问来源于stack exchange,提问作者Ernest Allen Buffington
相关产品推荐
相关产品推荐

