网站背景图
🏡丰盈润金 江流天地外,山色有无中。 (唐·王维·汉江临泛)
🏡丰盈润金
绿树村边合,青山郭外斜。 (唐·孟浩然·过故人庄)
鲁ICP备2024116425号鲁公网安备37011202002267号晚乔@2024自豪地使用 Typecho 建站搭配使用 🌻Sunny 主题博主 2小时前 在线 · 当前 10 人活跃

鲁ICP备2024116425号

鲁公网安备37011202002267号

晚乔@2024

已运行 1 年 347 天 5 小时 57 分

Powered by Typecho & Sunny

2小时前在线 · 11 人活跃 · 约 28 ms

文章

Typecho 1.3.0 升级踩坑实录:从 Server Error 到又拍云验证失败

晚乔

·

把博客从旧版 Typecho 升级到 1.3.0 之后,后台「新建文章」和「新建页面」直接打不开,页面只显示一行 Server Error

第一反应是主题或插件冲突——于是换回默认主题、停掉所有插件,结果照样报错

这个"怎么试都不行"的现象,恰恰是最有价值的线索。接下来是一段连踩四个坑的排查实录。


第一坑:Server Error,换主题停插件都没用

让错误现形

Typecho 对非预期的异常会统一渲染成 Server Error 字样,真实报错被藏起来了。想看真凶,需要临时打开调试模式——在 config.inc.php 里加一行:

define('__TYPECHO_DEBUG__', true);

刷新页面后,真凶浮出水面:

TypeError: Widget\Options::tryDeserialize():
Argument #1 ($value) must be of type string, null given

真凶:强类型 + 缺失的选项

Typecho 1.3.0 的核心代码 Widget\Options 用了 PHP 强类型签名:

private function tryDeserialize(string $value)

___panelTable() 会去读数据库里的 panelTable 选项。如果 options 表里根本没有这条记录,读出来就是 null,强类型模式下 PHP 直接抛 TypeError

那为什么老站会缺这条记录?因为 panelTableactionTable 这类选项是较新版本才引入的。而 Typecho 自带的升级程序只做序列化格式的转换,不会补写缺失的项——你升级前没有,升级后依然没有。

为什么换主题、停插件都无效?

因为报错发生在 Options 组件初始化阶段,比主题加载、插件加载都要早。

这解释了我一开始的困惑:不是主题或插件的问题,是核心自己在这一步就炸了,换什么、停什么都绕不过去。

💡 经验:当"换主题、停用所有插件"都无效时,基本可以确定问题出在更早的阶段——核心或环境,而不是主题插件。

解决

options 表补写缺失的记录:

INSERT IGNORE INTO typecho_options (name, value, user)
VALUES ('panelTable', '[]', 0);

INSERT IGNORE INTO typecho_options (name, value, user)
VALUES ('actionTable', '[]', 0);

(表前缀按你自己数据库实际情况调整。)顺带检查 secretgenerator 是否也缺失,一并补上。


第二坑:又拍云插件的 TypeError(array → Config)

修好新建文章后,后台「管理 → 文件」又炸了:

TypeError: UpyunFile_Plugin::attachmentHandle():
Argument #1 ($content) must be of type array, Typecho\Config given

根因:1.3.0 改了类型契约

这是 1.2 → 1.3 的一次破坏性变更。核心把附件信息的传递类型从数组改成了对象

版本attachmentHandle 签名传入的是什么
1.2.x 及以前attachmentHandle(array $content)数组
1.3.0attachmentHandle(Config $attachment)Config 对象

插件里还写着 array $content,收到 Config 对象,PHP 直接抛 TypeError

这不是配置问题,是插件没跟上核心的变更。 我用的又拍云插件(UpyunFile)是老版本,作者没适配 1.3.0。


第三坑:Undefined array key "attachment"

按常规思路,把类型改成兼容写法(收到对象就 toArray() 转成数组)应该就完了。改完类型错误确实消失了,但页面又冒出满屏警告:

Warning: Undefined array key "attachment" in .../Plugin.php on line 296
Warning: Attempt to read property "path" on null in .../Plugin.php on line 296

根因:不只是类型变了,层级也变了

toArray() 只解决了类型,没解决数据结构层级。两个版本传给插件的东西完全不是一回事:

// 1.2.x:传的是外层数组,附件信息藏在 ['attachment'] 里
$value['attachment']->url = Upload::attachmentHandle($value);
// 插件里写 $content['attachment']->path  →  ✔ 成立

// 1.3.0:传的就是附件本身,根本没有 attachment 这一层
$attachment->url = Upload::attachmentHandle($attachment);
// 插件里写 $content['attachment']        →  ✘ 不存在 → 第一条警告
//            ->path                      →  ✘ 对 null 取属性 → 第二条警告

解决:换用适配 1.3.0 的新版插件

与其手工打补丁,不如直接用作者已适配的版本。新版(v1.2,明确标注支持 Typecho 1.3.0+)把取路径改成了三分支兼容

if (isset($content->attachment) && isset($content->attachment->path)) {
    $path = $content->attachment->path;      // 1.2.x 结构
} else if (isset($content->path)) {
    $path = $content->path;                  // 1.3.0 结构 ← 走这条
} else if (isset($content->text)) {
    // 从 text 字段兜底
}

替换提醒:解压出来的目录常是 UpyunFile-master,但 Typecho 按目录名识别插件,必须改名为 UpyunFile 再覆盖,否则后台认不到。覆盖前记得先备份原插件目录。


第四坑:又拍云"操作员验证失败"

换上新插件后,填完服务名、操作员、密码点保存,表单报:

验证不通过,请核对 Upyun 操作员和密码是否输入正确

根因:相对路径少了一层目录

插件在验证时会真的去请求一次又拍云 API。而加载 SDK 的代码是这样的:

require_once 'Upyun/vendor/autoload.php';   // 相对路径

但 SDK 的真实位置是:

usr/plugins/UpyunFile/Upyun/vendor/autoload.php
                ↑↑↑↑↑↑↑↑  少了这一层

Typecho 1.3.0 没有任何 include_path 机制,插件是用完整路径 require 的。所以这句相对路径的解析基准是网站根目录,在合理环境下都找不到文件——我实测过,stream_resolve_include_path() 在两种常见场景下都返回 false

SDK 没加载 → 验证走不到真实请求 → 直接失败。

更坑的是:异常被吞了

validate() 里是这样的:

} catch (Exception $e) {
    $hostUsage = -1;      // ← 真实异常被丢掉,只返回一个 -1
}

所以你永远只能看到那句万能的"验证不通过",看不到是"类找不到"、"密码错"还是"网络超时"。

解决

把两处 require_oncevalidate()upyunInit() 里各一处)都改成绝对路径:

require_once __DIR__ . '/Upyun/vendor/autoload.php';

__DIR__ 在插件文件里就是 usr/plugins/UpyunFile,拼出来正好是 SDK 的真实位置。

顺带建议把 catch (Exception $e) 改成 catch (\Throwable $e)——PHP 7+ 的 Error 不是 Exception 的子类,只捕获 Exception 会漏掉一部分错误。

⚠️ 潜在隐患:插件自带的 SDK 依赖 Guzzle 6.3(2017 年版本)。如果你的服务器是 PHP 8,这个组合可能有兼容性问题。我这边验证通过了,但若你遇到 Guzzle 相关报错,需要考虑升级 SDK。

番外:一次"白屏"事故

为了安全加固,我给插件加了一行防直接访问:

if (!defined('__TYPECHO_ROOT_DIR__')) exit;

结果改完之后,主页变成纯白,而后台却能正常打开

定位过程

页面状态
主页HTTP 200,0 字节
后台200,5315 字节(正常
文章页正常

后台正常,说明核心、数据库、插件加载都没问题;白屏只影响前台渲染。而 HTTP 200 + 0 字节 这个组合,说明脚本"正常跑完了却没输出任何东西"。

根因:exit 加错了位置

那行被我误加进了 beforeRender() 函数体内。而这个插件有个前台专属钩子:

public static function Widget_Archive_beforeRender() {
    ob_start('UpyunFile_Plugin::beforeRender');   // 开启输出缓冲
}

页面渲染完成后,PHP 刷新缓冲区并调用回调 beforeRender($text)——回调一执行到 exit整个缓冲区的输出全部被丢弃,于是浏览器收到 HTTP 200 和 0 字节的空白页。

因为后台不触发这个前台钩子,所以后台一切正常。这个"后台正常、前台白屏"的组合,正是定位到输出缓冲回调的关键线索。

教训

  1. 防直接访问的那行必须放在文件顶部、所有 function 之外
  2. HTTP 200 + 0 字节 是输出被中途丢弃的典型特征;
  3. 关掉 display_errors 后,错误不会消失,只会变成白屏——排查期间建议临时打开。

经验总结

回头看,这四个坑有一个共同背景:核心往前跑了,插件还留在原地

  1. 1.3.0 是一次破坏性升级:数组 → Config 对象、强类型签名全面引入。老插件几乎必然中招,报错多表现为 must be of type array, Typecho\Config given
  2. "换主题停插件都无效"本身就是强线索——它说明问题在更早的初始化阶段。
  3. 升级后先查 options:确认有没有新增字段缺失,再看插件兼容性。
  4. 老插件逐个确认是否有适配版本,别急着手工打补丁——官方适配版通常更完整。
  5. 调试开关用完立刻删__TYPECHO_DEBUG__ 会把服务器路径、调用堆栈暴露在网页上。
  6. 改插件前先备份,且改完要验证前后台都正常。

附录:同类问题速查表

报错信息含义处理
Server Error(新建文章/页面)核心强类型读到 null补写 options 表缺失项
must be of type array, Typecho\Config given1.3.0 把数组改成了对象去掉 array 类型声明 + instanceof \Typecho\Config 转换
Undefined array key "attachment"数据结构层级变了用适配版插件,或按 $content->path 取值
Undefined array key ...(其它)多为同类变更同上思路
operator/验证失败多为 SDK 没加载或凭据错检查 SDK 路径,把异常暴露出来看真因
白屏 + HTTP 200 + 0 字节输出被中途丢弃ob_start 回调里是否有 exit / 提前终止

如果你也在升级 Typecho 的路上遇到了类似的报错,希望这篇记录能帮你少走点弯路。
声明:本文由AI生成,整个处理过程由Workbuddy协助,本人对代码一窍不通。再次感谢AI。

现在已有 19 次阅读,0 条评论,0 人点赞
作者:晚乔
作者
Typecho 1.3.0 升级踩坑实录:从 Server Error 到又拍云验证失败
当前文章累计共 6253 字,阅读大概需要 5 分钟。
深耕批发行业,一次刷新认知的临沂之行
2026年6月16日 - 2评论
爱的治愈
2026年8月8日 - 4评论
拯救大兵瑞恩
2026年4月29日 - 0评论
评论:共0条
发表
功能 探索 消息

足迹

你还不曾留下足迹..

音乐单

随机文章

夜晚

💬
你还不曾留言过
博主 不再显示
博主
未知作品 歌曲封面
立即安装