问题原因
Typecho 1.3.0 对 PHP 类型校验更严格了,1.2.1 能兼容 null 传递给需要字符串的函数,1.3.0 会直接抛出 TypeError(类型错误),所以说这就是为什么有些主题在1.2.1可以使用,而1.3.0会直接报错。
解决思路
所以下一步就需要判断是哪个函数报错,首先开启Typecho的debug模式。在根目录的config.inc.php文件中,增加:
/** 开启调试模式 */
define('__TYPECHO_DEBUG__', true);
以我现在使用的Typecho IceFox主题为例:
- 通过报错溯源到主题
icefox的functions.php第 675 行调用了$this->permalink - 搜索无结果时,
Contents.php里的path()方法返回null,传给Router::url()就会报错 - Typecho 1.3.0 对 PHP 类型校验更严格,1.2.1 能兼容的
null参数,1.3.0 直接抛出 TypeError
通过报错定位到具体函数 seoInfo() ,找到包含 $this->permalink 的代码段,给$archive->permalink 做空值兜底,例如:
// 原代码(类似这样)
function seoInfo() {
// ... 其他代码 ...
$permalink = $this->permalink; // 第675行左右
// ... 其他代码 ...
}
// 修改后的代码(增加空值判断)
function seoInfo() {
if ($archive->is('search') && $archive->total == 0) {
// 搜索无结果时,强制把permalink设为网站首页地址
$permalink = $options->siteUrl;
} else {
// 其他情况:有值就用原值,无值就兜底为首页
$permalink = $archive->permalink ?? $options->siteUrl;
}
// ... 其他代码保持不变 ...
}
我使用的主题搜索无结果时,$archive->permalink 返回 null,传给 htmlspecialchars() 后再传给 Router::url(),触发类型错误,所以还需要处理htmlspecialchars。处理完成后完整的seoInfo()函数代码:
/**
* 输出SEO信息
*/
function seoInfo($archive)
{
$options = Helper::options();
if ($archive->is('index')) {
$title = $options->title;
$description = $options->description;
$keywords = $options->keywords;
} elseif ($archive->is('post') || $archive->is('page')) {
$title = $archive->title . ' - ' . $options->title;
// 清理文章摘要:移除HTML标签、换行符,并截取前150个字符
$description = strip_tags($archive->excerpt);
$description = preg_replace('/\s+/', ' ', $description); // 将多个空白字符替换为单个空格
$description = trim($description);
$description = mb_substr($description, 0, 150, 'UTF-8');
$keywords = '';
if ($archive->tags) {
foreach ($archive->tags as $tag) {
$keywords .= $tag['name'] . ',';
}
}
$keywords = rtrim($keywords, ',');
} else {
// 归档页面:根据不同类型生成标题
$archiveTitle = '';
if ($archive->is('category')) {
$archiveTitle = '分类 ' . $archive->getDescription() . ' 下的文章';
} elseif ($archive->is('tag')) {
$archiveTitle = '标签 ' . $archive->getDescription() . ' 下的文章';
} elseif ($archive->is('author')) {
$archiveTitle = $archive->getDescription() . ' 发布的文章';
} elseif ($archive->is('search')) {
// 尝试多种方式获取搜索关键词
$searchKeywords = '';
// 调试:检查所有可能的搜索参数来源
$request = $archive->request;
if ($request) {
$searchKeywords = $request->get('keywords', '');
if (empty($searchKeywords)) {
$searchKeywords = $request->get('s', '');
}
}
// 如果还是空,尝试从$_GET获取
if (empty($searchKeywords) && isset($_GET['keywords'])) {
$searchKeywords = $_GET['keywords'];
}
if (empty($searchKeywords) && isset($_GET['s'])) {
$searchKeywords = $_GET['s'];
}
$archiveTitle = '包含关键字 ' . $searchKeywords . ' 的文章';
} else {
// 使用默认归档标题
$archiveTitle = '归档';
}
$title = $archiveTitle . ' - ' . $options->title;
$description = $options->description;
$keywords = $options->keywords;
}
// 搜索无结果时,permalink指向首页;其他情况用原permalink(无值则兜底首页)
if ($archive->is('search') && $archive->total == 0) {
$permalink = $options->siteUrl;
} else {
$permalink = $archive->permalink ?? $options->siteUrl;
}
// 转义所有输出内容,防止HTML注入
$title = htmlspecialchars($title, ENT_QUOTES, 'UTF-8');
$description = htmlspecialchars($description, ENT_QUOTES, 'UTF-8');
$keywords = htmlspecialchars($keywords, ENT_QUOTES, 'UTF-8');
$permalink = htmlspecialchars($permalink, ENT_QUOTES, 'UTF-8'); // 转义permalink
echo '<title>' . $title . '</title>' . "\n";
echo ' <meta name="description" content="' . $description . '">' . "\n";
echo ' <meta name="keywords" content="' . $keywords . '">' . "\n";
// Open Graph
echo ' <meta property="og:title" content="' . $title . '">' . "\n";
echo ' <meta property="og:description" content="' . $description . '">' . "\n";
echo ' <meta property="og:type" content="website">' . "\n";
echo ' <meta property="og:url" content="' . $permalink . '">' . "\n"; // 替换原$archive->permalink
echo ' <meta property="og:site_name" content="' . htmlspecialchars($options->title, ENT_QUOTES, 'UTF-8') . '">' . "\n";
// Twitter Card
echo ' <meta name="twitter:card" content="summary">' . "\n";
echo ' <meta name="twitter:title" content="' . $title . '">' . "\n";
echo ' <meta name="twitter:description" content="' . $description . '">';
}
山峙