Xuan's blog - 网站建设
type: channel
link: https://blog.ybyq.wang/category/web-build/
description: 从服务器选择、环境搭建到 Typecho/WordPress 部署与主题配置的全流程建站清单。
items: 对自己的博客做了几下 Apache Bench 测试，出乎意料
items: 解决AI摘要不显示的三种方案 -AIsummary插件
items: 零成本建站：将 Windows 电脑变身为个人网站服务器
items: 使用frp搭建高性能内网穿透服务（结合XAMPP和宝塔面板）
items: Typecho handsome 睡眠记录数据展示插件 - SleepData
items: Typecho handsome 添加评论区OwO表情包 - QQ,抖音,B站等
items: Typecho handsome 侧边栏显示网站请求总数
items: Typecho Handsome 添加实时QPS图表 - 兼容pjax
items: 分享60个有趣实用的API及其Typecho博客集成指南
items: 解决Typecho报错 - Unsupported operand types: int - string

对自己的博客做了几下 Apache Bench 测试，出乎意料
type: item
date: 2025-10-26T16:02:06+08:00
description: 昨晚想着给自己的网站做些安全与压力方面的测试，今天就动手实践了一下。首先下载并准备 Apache Bench（ab 压力测试工具），我用的是 win11 版。在解压后的 Apache 的 bin 目录中打开 cmd。简单看了下常用参数后开始测试，一共跑了三组：abs.exe -n 100 -c 5 https://blog.ybyq.wang/abs.exe -n 10000 -c 50 https://blog.ybyq.wang/abs.exe -n 20000 -c 50 https://blog.ybyq.wang/参数说明（简要）-n: 总请求数（Total requests）。-c: 并发数（Concurrency），同一时刻发起的并行请求数。并发设置过大容易触发 CDN 限速/封顶或放大带宽消耗。最后一轮测试时，网站突然无法访问。打开 CDN 的资源监控一看，果然触发了我设置的带宽封顶限制。手动解除后，访问就恢复了。至于雷池 WAF，一条都没拦截；部署雷池的云服务器负载也基本没有波动。这次测试大概消耗了 CDN 约 2 GB 流量。下次我先关掉 CDN，再猛猛测几轮看看源站的真实表现。

解决AI摘要不显示的三种方案 -AIsummary插件
type: item
date: 2025-09-21T15:18:00+08:00
description: 前言在自定义主题的某天，AI摘要突然不显示了，前前后后改了好几次，通过重启插件无数次、移除全部自定义js和css、关闭pjax、一一恢复改过的页面代码，均未果。这次是通过本文第二个方法改好的，所以至今不知道突然不显示的原因。下面是解决AISummary插件的摘要块不显示的三种方案，前提是摘要内容已正确生成且保存。针对handsome主题，其他主题可借鉴思路。技术环境博客系统: Typecho 1.2.1主题: Handsome 10.1.0插件: AISummary v1.1.0解决方案方案一：修改插件核心逻辑直接修改 Plugin.php 中的 customContent 方法，将摘要插入到内容末尾：// 修改前 $content = $summaryString . $content; // 修改后 $content = $content . $summaryString;方案二：主题模板直接集成（推荐）在主题模板文件中直接添加摘要显示逻辑，绕过插件的 customContent 方法。实施步骤在插件设置中将"正文摘要显示样式"设置为"默认"或者"自定义"，推荐设置为自定义。在主题模板中添加摘要显示代码在 usr/themes/handsome/post.php 第74行的 下方添加：[hide] <?php // 添加AI摘要显示 $options = Typecho_Widget::widget('Widget_Options')->plugin('AISummary'); if ($options->summaryStyle !== '0') { $summaryField = $options->field ?: 'summary'; $db = Typecho_Db::get(); $row = $db->fetchRow($db->select('str_value')->from('table.fields')->where('cid = ?', $this->cid)->where('name = ?', $summaryField)); if ($row && !empty($row['str_value'])) { $pureSummary = str_replace("{{text}}", $row['str_value'], $options->prefix); // 根据插件配置选择样式 if ($options->summaryStyle === '1') { // 使用默认引用样式 $summaryString = ' ' . $pureSummary . ' '; } else if ($options->summaryStyle === '2') { // 使用自定义样式，只添加class，让CSS控制样式 $summaryString = ' ' . $pureSummary . ' '; } else { // 默认样式 $summaryString = ' ' . $pureSummary . ' '; } echo $summaryString; } } ?>[/hide]这种方式不依赖于Typecho的钩子系统，直接在主题模板中添加摘要显示代码。方案三：针对开启了前台Vditor解析方式第一种：1、打开文件：usr/themes/handsome/libs/content/PostContent.php2、找到第382行（大约在第380-385行之间）3、将$content = $obj->text;改为$content = $obj->content;4、保存文件$obj->text是vditor原始的Markdown文本，没有经过任何插件处理$obj->content是经过Typecho插件系统处理后的HTML内容，包括AISummary插件的customContent方法添加的摘要通过使用$obj->content，确保所有插件的钩子都能正常工作第二种：在handsome插件后台切换vidtor为typecho自带的即可下面是自定义模板的文章：[post cid="335" /]

零成本建站：将 Windows 电脑变身为个人网站服务器
type: item
date: 2025-08-08T14:49:21+08:00
description: 一、环境准备1.1 所需软件与资源XAMPP: 集成了 Apache、MariaDB、PHP 和 Perl 的跨平台 Web 开发环境Typecho: 轻量级开源博客程序，基于 PHP公网访问方案: 内网穿透工具（花生壳、frp）或固定 IP 方案文本编辑器: 用于修改配置文件（如 VS Code、Notepad++ 等）二、本地服务器配置2.1 XAMPP 安装与初始化从Apache Friends官网下载适合您操作系统的XAMPP安装包执行安装程序，建议安装路径避开系统目录（如C:\Program Files），本指南使用D:\xampp安装完成后，启动XAMPP控制面板依次启动Apache和MySQL服务如果 MySQL 无法启动，请参见文末“附录A：常见问题与排查”。在浏览器中访问http://localhost验证服务器运行状态2.2 数据库创建Typecho 需要一个 MySQL 数据库来存储内容和配置信息：访问http://localhost/phpmyadmin/进入数据库管理界面点击左侧导航栏中的"新建"选项在表单中输入数据库名称（如typecho_db）选择字符集为utf8mb4_general_ci（支持完整的Unicode字符，包括Emoji）点击"创建"按钮完成数据库初始化安全提示: 在生产环境中，应为数据库创建专用用户并限制权限，而非使用默认的root账户。可在phpMyAdmin的"用户账户"→"添加用户"中完成此操作。三、Typecho博客部署3.1 程序文件安装从Typecho官方网站下载最新版本的程序包解压下载的压缩文件将解压后的所有文件复制到 XAMPP 的网站目录中：D:\xampp\htdocs\typecho\3.2 执行安装向导在浏览器中访问http://localhost/typecho/启动安装程序在欢迎页面点击"我准备好了，开始下一步"在数据库配置页面填写以下信息：数据库适配器: MySQL数据库地址: localhost数据库用户名: root（XAMPP 默认）数据库密码: （默认为空）数据库名: typecho_db（您在上一步创建的数据库）表前缀: typecho_（可保持默认）点击"确认，开始安装"按钮在站点配置页面设置管理员账户信息：站点名称站点描述管理员用户名管理员密码管理员邮箱完成安装后，您可以通过以下地址访问：博客前台：http://localhost/typecho/管理后台：http://localhost/typecho/admin/至此，本地博客系统已完成基础配置，可以进行文章发布和管理。3.3 Apache 网站根目录配置关键步骤: 为了让访问您的域名或IP时直接显示博客内容（而非XAMPP默认欢迎页），需要修改Apache配置，将根目录指向Typecho文件夹：在XAMPP控制面板中停止Apache服务备份原始配置文件：D:\xampp\apache\conf\httpd.conf使用文本编辑器打开httpd.conf文件搜索DocumentRoot并找到以下配置：# 原配置 DocumentRoot "D:/xampp/htdocs" <Directory "D:/xampp/htdocs">修改为指向Typecho目录：# 新配置 DocumentRoot "D:/xampp/htdocs/typecho" <Directory "D:/xampp/htdocs/typecho">保存文件并在XAMPP控制面板中重新启动Apache服务3.4 Typecho 站点 URL 配置当您通过任何域名或 IP 访问博客时，需要确保 Typecho 知道自己的正确地址，否则样式和链接可能无法正常工作：通过http://localhost/admin/访问博客后台在左侧菜单中选择"设置"→"基本设置"找到"站点地址"选项根据您的访问方式，将其设置为对应的地址：本地访问：http://localhost公网域名访问：http://您的域名（如http://myblog.com或http://1ko0641539qv6.vicp.fun）注意: URL 末尾不要添加斜杠/，以避免路径解析问题。点击页面底部的"保存设置"按钮刷新博客首页，此时所有样式和功能应正常显示四、实现公网访问的多种方案本章介绍几种将您的本地服务器发布到公网的方法，您可以根据自身条件选择最适合的方案。如需了解各种内网穿透工具的详细对比，请参阅我们的专题文章《内网穿透工具全面对比：选择最适合您的方案》。4.1 方案一：花生壳内网穿透（适合动态IP）内网穿透允许外部网络通过特定域名访问您本地的 Web 服务：注册并登录花生壳账号，下载安装花生壳客户端在客户端或官网控制台中添加新的映射配置映射参…

使用frp搭建高性能内网穿透服务（结合XAMPP和宝塔面板）
type: item
date: 2025-08-08T13:20:05+08:00
description: 前言这篇教程是用XAMPP已经在本地搭建好了Typecho博客网站，除了用花生壳，也可以用更好的frp做内网穿透，此篇教程就是使用frp搭建高性能内网穿透服务。一、准备工作1.1 所需资源一台具有公网IP的服务器（已安装宝塔面板）域名（可选，用于通过域名访问服务）本地环境（运行Web服务的计算机）1.2 了解frp工作原理frp分为服务端（frps）和客户端（frpc）两部分：服务端（frps）：部署在公网服务器上客户端（frpc）：部署在本地需要穿透的设备上1.3 域名与DNS解析（指向同一IP）将要对外提供访问的域名（如：www.example.com）添加一条 A 记录，指向 frps 所在服务器的公网IP（即宝塔所在服务器IP）。同一台服务器可承载多个域名，均需指向同一公网IP。常用验证方式：在本地终端执行：nslookup www.example.com 或 ping www.example.com，确认解析到的IP与服务器公网IP一致。解析生效后再进行后续的Nginx配置与证书申请。二、服务器端配置2.1 安装与版本说明首选方式（推荐）：在宝塔软件商店中安装并开启「frp管理器 1.1」。该管理器可一键完成服务端安装、版本选择、配置与启动，适合快速部署与可视化管理。可选方式（手动）：从 GitHub 发布页下载对应平台的 frp 二进制包，解压并通过 systemd 方式托管运行（适合有Linux基础的用户）。重要版本提示：自 v0.51.0 起，frp 配置文件已由 INI 切换为 TOML 格式。请确保服务端与客户端均使用 TOML，并注意版本匹配。2.2 配置frps服务端进行服务配置，这些是默认的端口，需要在防火墙中打开进行WEB服务配置，设置好用户名和密码，此默认端口也要打开进行token设置最后，开启服务，并下载此客户端frp注意事项：kcpBindPort 不应与 bindPort 相同，建议分配为不同端口（如 bindPort=15443，kcpBindPort=15444）。vhostHTTPPort/vhostHTTPSPort 为 frps 内部的虚拟主机端口，通常通过 Nginx 80/443 反向代理至此端口对外提供服务。auth.token 为客户端鉴权必须一致的密钥，修改后需重启 frps 并同步更新客户端。2.4 配置防火墙确保服务器防火墙开放了必要的端口：# 根据frps.toml中的配置开放以下端口 firewall-cmd --permanent --add-port=15443/tcp # bindPort firewall-cmd --permanent --add-port=15444/udp # kcpBindPort firewall-cmd --permanent --add-port=18080/tcp # vhostHTTPPort firewall-cmd --permanent --add-port=18443/tcp # vhostHTTPSPort firewall-cmd --permanent --add-port=16337/tcp # tcpmuxHTTPConnectPort firewall-cmd --permanent --add-port=7001/tcp # webServer端口 firewall-cmd --permanent --add-port=80/tcp # HTTP标准端口 firewall-cmd --reload三、客户端配置3.1 下载frp客户端在您本地计算机上，下载与服务器端相同版本的frp：可以下载宝塔中给出的0.53.2版本Windows用户：从GitHub下载对应的64位 X86Windows版本，本教程为第一个下载后解压到一个方便的位置，如D:\frp3.2 配置frpc客户端（TOML格式）在frp文件夹中创建/编辑frpc.toml文件（注意：不是frpc.ini），内容如下：# frpc.toml # 基本配置 - 连接到服务器 serverAddr = "服务器公网IP" serverPort = 15443 # 必须与frps.toml中的bindPort一致 # 认证token，必须与服务器端一致 auth.token = "与服务器端相同的令牌" # 代理规则配置 proxies name = "web-site" # 名称可自定义 type = "http" # 协议类型 localIP = "127.0.0.1" # 本地服务IP localPort = 80 # 本地服务端口 customDomains = ["www.example.com"] # 您的域名（需已解析到与f…

Typecho handsome 睡眠记录数据展示插件 - SleepData
type: item
date: 2025-07-31T23:41:00+08:00
description: SleepData 插件是一个用于在 Typecho 博客系统中记录、存储和分析个人睡眠数据的工具。它解决了部分健康应用（如 OPPO 健康）数据封闭、无公开 API 的问题，允许用户将数据掌握在自己手中。核心问题与解决方案许多健康应用（例如 OPPO 健康）不提供公开的 API 接口，也无法将数据同步到 Google Fit 等开放平台，导致用户的健康数据完全封闭，难以进行统一管理和长期分析。本插件通过提供一个独立的数据录入和存储系统来解决此问题。用户可以通过截图 OCR 识别、手动录入或 API 调用等方式，将睡眠数据保存在自己的服务器上，从而实现数据的自主掌控和长期追踪。工作流程整个数据处理流程从用户手机端的健康应用开始，经过前端的 OCR 识别与数据提交，再到服务器端的处理与存储，最后通过不同的页面进行展示。插件特性数据自主可控: 所有数据存储在用户自己的服务器上。双重存储: 同时保存到数据库和 JSON 文件，保证数据安全。OCR 自动录入: 通过识别截图，简化数据输入过程。API 支持: 开放 API 接口，方便进行二次开发或批量导入。自适应存储: 自动检测服务器环境，寻找可写目录，无需手动配置。双重配置: 支持后台配置和文件配置两种方式，提高灵活性。Handsome 主题集成: 与 Handsome 主题无缝集成，支持顶部导航栏和侧边栏展示。使用指南1. 安装插件从源码地址下载插件压缩包。解压后，将文件夹重命名为 SleepData。上传 SleepData 文件夹到 Typecho 的 usr/plugins 目录下。登录 Typecho 后台，进入"控制台" -> "插件"，找到"SleepData"并启用。2. 配置插件启用插件后，点击"设置"进入配置页面。API 访问令牌: 设置一个足够复杂的字符串作为 API 访问的密钥。这是保证数据安全的关键。此令牌有两种配置方式，系统会优先使用方法一：方法一（推荐）: 直接在后台设置页面填写并保存。方法二（备用）: 编辑插件目录下的 data_config.php 文件，修改 API_ACCESS_TOKEN 的值。3. 数据录入方式一：OCR 识别上传（推荐）这是最便捷的方式，尤其适用于从手机健康 App 录入数据。在浏览器中打开插件目录下的 https://博客地址/sleep-data-uploader/index.html 文件。填写您的 API 地址 和 访问令牌 （一次填写后浏览器会记住）。点击"拍照识别"或"选择截图"按钮，上传您在健康 App 中截取的睡眠数据图片。系统会自动识别图片中的数据并填充到表单中。核对无误后，点击"发送数据"。方式二：手动输入如果 OCR 识别有误或没有截图，可以选择手动填写所有睡眠数据，然后点击"发送数据"。方式三：通过 API 上传对于有开发能力的用户，可以通过 POST 请求直接向 API https://博客地址/usr/plugins/SleepData/simple-api.php 提交数据。请求体为 JSON 格式，需包含access_token及所有睡眠数据字段。4. 查看数据后台查看: 在 Typecho 后台的插件设置页面，会直接展示最近 50 条睡眠记录。独立页面查看: 访问 https://博客地址/usr/plugins/SleepData/view-data.php 可以查看 JSON 文件中的所有数据记录。数据库查看: 在数据库中的typecho_sleep_data表中查看所有数据记录。根目录查看: 在根目录下的temp/sleep_data.json文件中查看所有数据记录。5. 在 Handsome 主题中展示睡眠数据展示一：添加到顶部导航栏效果展示添加代码到主题的headnav.php文件中。[collapse status="false" title="php代码，放在nav navbar-nav hidden-sm的ul中"][hide] <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path> 睡眠统计 今日睡眠 本周平均 本月平均 累计睡眠 平均入睡 本周最晚 质量最佳 质量最差 睡眠趋势 加载中... [/hide][/collapse][collapse status="false" title="JS代码，添加到/header前面"][hide] [/hide][/collapse]展示二：添加到侧边栏效果展示添加代码到主题的sidebar.php文件中。[collapse status="false" title="php代码，放在博客信息的上下即可"][hide] <?php if (class_e…

Typecho handsome 添加评论区OwO表情包 - QQ,抖音,B站等
type: item
date: 2025-07-29T18:46:00+08:00
description: 我对网站的 OwO 表情包进行了一次大规模的扩充，新增和整理了包括 QQ、抖音、哔哩哔哩、微博、贴吧、酷安 等多个平台在内的热门表情。表情总览阿鲁推特B 站抖音酷安QQ微博贴吧滑稽表情包配置详情[collapse status="false" title="QQ"][tabs][tab name="json" active="true"]{ "QQ": { "name": "QQ", "type": "image", "container": [ { "icon": "aini", "text": "爱你" }, { "icon": "aixin", "text": "爱心" }, { "icon": "aoman", "text": "傲慢" }, { "icon": "baiyan", "text": "白眼" }, { "icon": "bangbangtang", "text": "棒棒糖" }, { "icon": "baojin", "text": "抱紧" }, { "icon": "baoquan", "text": "抱拳" }, { "icon": "bishi", "text": "鄙视" }, { "icon": "bizui", "text": "闭嘴" }, { "icon": "cahan", "text": "擦汗" }, { "icon": "daku", "text": "大哭" }, { "icon": "doge", "text": "doge" } // ... 更多QQ表情 ] } }[/tab][tab name="CSS"].emotion-QQ{width: 35px; height: auto;}[/tab][/tabs][/collapse][collapse status="false" title="抖音"][tabs][tab name="json" active="true"]{ "抖音": { "name": "douyin", "type": "image", "container": [ { "icon": "1", "text": "抖音-1" }, { "icon": "2", "text": "抖音-2" }, { "icon": "3", "text": "抖音-3" }, { "icon": "4", "text": "抖音-4" }, { "icon": "5", "text": "抖音-5" }, { "icon": "6", "text": "抖音-6" }, { "icon": "7", "text": "抖音-7" }, { "icon": "8", "text": "抖音-8" }, { "icon": "9", "text": "抖音-9" }, { "icon": "10", "text": "抖音-10" } // ... 更多抖音表情 ] } }[/tab][tab name="CSS"].emotion-douyin{width: 35px; height: auto;}[/tab][/tabs][/collapse][collapse status="false" title="哔哩哔哩"][tabs][tab name="json" active="true"]{ "哔哩哔哩":{ "name": "bilibili", "type": "image", "container":[ { "icon": "baiyan", "text": "白眼" }, { "icon": "bishi", "text": "鄙视" }, { "icon": "bizui", "text": "闭嘴" }, { "icon": "chan", "text": "馋" }, { "icon": "daku", "text": "大哭" }, { "icon": "dalao", "text": "大佬" }, { "icon": "dalian", "text": "打脸" }, { "icon": "dianzan", "text": "点赞" }, { "icon": "doge", "text": "doge" }, { "icon": "facai", "text": "发财" }, { "icon": "fadai", "text": "发呆" }, { "icon": "fanu", "text": "发怒" } // ... 更多B站表情 ] } }[/tab][tab name="CSS"].emotion-bilibili{width: 35px; height: auto;}[/tab][/tabs][/collapse][…

Typecho handsome 侧边栏显示网站请求总数
type: item
date: 2025-07-25T12:16:00+08:00
description: 本文将详细介绍如何在博客侧边栏实现请求总数显示功能，让您随时掌握网站的访问情况。一、请求总数统计预览二、实现思路实现侧边栏请求总数显示主要包含以下几个步骤：数据来源：从 WAF 获取请求总数数据数据缓存：利用本地缓存减少请求次数前端显示：在侧边栏显示数据自动更新：使用 JavaScript 定期刷新数据保持实时性三、后端实现1. 创建访问统计数据获取脚本首先，创建一个 PHP 脚本来获取来自 WAF 的访问统计数据：[hide]<?php // usr/themes/handsome/component/visitor-stats.php // 设置响应头 header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-cache, no-store, must-revalidate'); header('Expires: 0'); // 缓存文件路径 $cache_dir = __DIR__ . '/cache'; if (!is_dir($cache_dir)) { @mkdir($cache_dir, 0755, true); } $cache_file = $cache_dir . '/visitor_stats.json'; $log_file = $cache_dir . '/visitor_stats.log'; // 缓存时间设置为10分钟 $cache_time = 600; // API配置 - WAF接口 $api_url = 'https://your.waf.domain:9443/api/open/dashboard/user/counts'; $api_token = 'YOUR_WAF_API_TOKEN'; // 使用WAF的API Token // 检查缓存是否存在且有效 if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time) && !isset($_GET['refresh'])) { // 缓存有效，直接读取缓存 $content = file_get_contents($cache_file); echo $content; exit; } // 从WAF获取请求总数 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $api_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Accept: application/json', 'X-SLCE-API-TOKEN: ' . $api_token ]); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($http_code === 200 && $response) { $data = json_decode($response, true); if ($data && isset($data['data']['total_requests'])) { // 成功获取数据 $result = [ 'total_pv' => (int)$data['data']['total_requests'], 'timestamp' => time(), 'cached' => false ]; // 保存到缓存 file_put_contents($cache_file, json_encode($result)); echo json_encode($result); } else { // API返回数据格式不正确 echo json_encode([ 'error' => '数据格式错误', 'total_pv' => 0, 'timestamp' => time() ]); } } else { // API请求失败 echo json_encode([ 'error' => $err…

Typecho Handsome 添加实时QPS图表 - 兼容pjax
type: item
date: 2025-07-25T11:05:20+08:00
description: 对于使用 Typecho Handsome 主题的博主来说，在侧边栏添加一个能实时反馈网站负载的 QPS (每秒查询率) 图表，是一项非常酷的功能。然而，Handsome 主题强大的 PJAX 无刷新加载特性，也给这类需要持续运行的动态脚本带来了挑战。本教程将详细介绍如何创建一个与 PJAX 完全兼容的实时 QPS 图表，彻底解决在页面跳转后图表“罢工”、数据不再更新的问题。一、最终效果我们最终实现的效果是，在博客侧边栏有一个动态的QPS图表，无论您是首次加载页面，还是通过PJAX在不同页面间导航，这个图表都能持续、稳定地从服务器获取最新数据并实时渲染。二、实现原理为了完美兼容PJAX，我们将整个功能拆分为三个核心部分，各司其职：后台数据更新器：这是“数据源”的核心。它由一个JavaScript脚本 (qps-updater.js) 和一个PHP脚本 (update-qps.php) 组成。qps-updater.js 会在后台持续运行，每隔5秒就向 update-qps.php 发起请求，后者则负责从您的WAF（如长亭雷池）获取最新的QPS数据，并将其写入一个简单的服务器缓存文件 (safeguard_qps.json)。前台图表渲染器：这是“看得见”的部分。它位于您的侧边栏 (sidebar.php)，包含一个图表容器 ( ) 和一段独立的 ECharts 渲染脚本。这段脚本只做一件事：同样每隔5秒，从另一个PHP脚本 (get-qps.php) 读取上述的缓存文件，然后将数据绘制成图表。PJAX 兼容粘合剂：这是解决问题的关键。我们将前台图表的初始化逻辑，封装在一个全局函数 initQPSChartForHandsome() 中。然后，我们只需在 Handsome 主题后台的PJAX回调设置中填入这个函数名。这样，每次PJAX导航完成，主题就会自动调用这个函数，它会先彻底清理掉旧的图表实例和定时器，再重新初始化一个新的，从而实现了完美的无缝衔接。这种前后端分离、逻辑解耦的架构，确保了即使在复杂的PJAX环境中，数据更新和图表渲染也能独立、稳定地运行。三、文件准备请将以下4个文件，按照指定的路径和内容，放置到您的主题目录中。注意：本教程假设您已在 /usr/themes/handsome/static/js/ 目录下放置了 echarts.min.js 文件。这是本地的ECharts图表库，您可以从 ECharts官网 下载最新版本。若您没有此文件，我们的加载器会自动尝试从CDN获取，但建议使用本地版本以获得更好的加载速度和稳定性。1. 数据读取脚本路径: /usr/themes/handsome/component/get-qps.php(该脚本用于让前台图表安全地读取缓存数据)<?php // usr/themes/handsome/component/get-qps.php header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-cache, no-store, must-revalidate'); $cache_dir = __DIR__ . '/cache'; $cache_file = $cache_dir . '/safeguard_qps.json'; if (file_exists($cache_file)) { echo file_get_contents($cache_file); } else { // 如果缓存文件不存在，返回一个默认的JSON结构 echo json_encode(['qps' => 0, 'timestamp' => time(), 'simulated' => true]); }2. 数据更新脚本路径: /usr/themes/handsome/component/update-qps.php(该脚本用于从WAF获取真实数据并写入缓存)[hide]<?php // usr/themes/handsome/component/update-qps.php // 仅允许内部JS脚本通过AJAX调用 if ( !isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest' ) { header('HTTP/1.1 403 Forbidden'); exit('Access Denied'); } // --- WAF API 配置 --- $api_url = 'https://your.waf.domain:944…

分享60个有趣实用的API及其Typecho博客集成指南
type: item
date: 2025-07-17T16:26:00+08:00
description: 引言突发奇想，搜集了一些公开API，为博客添加一些动态和个性化的元素让网站更加生动有趣。本文介绍60个有趣的API，它们都可以集成到Typecho博客中，让您的博客脱颖而出。目录个人社交账号API热搜榜单API工具类API内容增强类API博客功能增强类API游戏与娱乐类API个人社交账号API1. QQ等级显示通过QQ空间API获取您的QQ等级、成长值等信息，并在博客中展示。<?php // 在sidebar.php或自定义页面中添加 function getQQLevel($qq) { $url = "https://r.qzone.qq.com/fcg-bin/cgi_get_score.fcg?mask=7&uins=".$qq; $data = file_get_contents($url); $data = mb_convert_encoding($data, "UTF-8", "GBK"); preg_match('/lvtext:"(\d+级)"/', $data, $level); return isset($level[1]) ? $level[1] : '获取失败'; } $qq = '你的QQ号'; echo '我的QQ等级: '.getQQLevel($qq); ?>2. QQ音乐状态API展示您正在听的QQ音乐歌曲信息。<?php function getQQMusicStatus($qq) { // QQ音乐状态获取 $url = "https://c.y.qq.com/rsc/fcgi-bin/fcg_get_profile_homepage.fcg?format=json&inCharset=utf-8&outCharset=utf-8&notice=0&platform=yqq&uin=".$qq; $context = stream_context_create([ 'http' => [ 'header' => 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' ] ]); $data = json_decode(file_get_contents($url, false, $context), true); // 判断是否有正在收听的歌曲 if (isset($data['data']['listen']) && !empty($data['data']['listen']['now'])) { return [ 'status' => true, 'song_name' => $data['data']['listen']['now']['song_name'], 'singer' => $data['data']['listen']['now']['singer'][0]['name'], 'cover' => $data['data']['listen']['now']['cover'] ]; } else { return [ 'status' => false ]; } } $qq = '你的QQ号'; $music = getQQMusicStatus($qq); if ($music['status']) { echo ' '; echo ' 我正在听 '; echo ' '; echo ' '; echo ' '.$music['song_name'].' '; echo ' - '.$music['singer'].' '; echo ' '; echo ' '; echo ' '; } else { echo '暂无音乐播放记录'; } ?>3. 哔哩哔哩UP主数据展示您的B站粉丝数、视频数、播放量等数据。<?php function getBiliStats($uid) { $url = "https://api.bilibili.com/x/relation/stat?vmid=".$uid."&jsonp=jsonp"; $data = json_decode(file_get_contents($url), true); return $data['data']; } $uid = '你的B站UID'; $stats = getBiliStats($uid); echo "粉丝数: ".$stats['follower']; ?>4. 网易云音乐状态展示您最近听的歌曲或者创建一个迷你播放器。<?php // 网易云音乐用户歌单获取 function get…

解决Typecho报错 - Unsupported operand types: int - string
type: item
date: 2025-07-15T20:57:44+08:00
description: 一场突如其来的网站崩溃今天做完插件，网站访客量激增。直接把我的2h2g负载拉满了，还好网站能打开，看到了点报错内容：Fatal error: Uncaught TypeError: Unsupported operand types: int - string in /www/wwwroot/blog.ybyq.wang/usr/themes/handsome/functions.php:113 Stack trace: #0 /www/wwwroot/blog.ybyq.wang/usr/themes/handsome/sidebar.php(25): online_users() #1 /www/wwwroot/blog.ybyq.wang/var/Widget/Themes/Handsome/index.php(113): require_once('/www/wwwroot/bl...') #2 /www/wwwroot/blog.ybyq.wang/var/Widget/Archive.php(1589): require('/www/wwwroot/bl...') #3 /www/wwwroot/blog.ybyq.wang/var/Widget/Archive.php(449): Widget_Archive->render() #4 /www/wwwroot/blog.ybyq.wang/index.php(15): Widget_Archive->archive() #5 {main} thrown in /www/wwwroot/blog.ybyq.wang/usr/themes/handsome/functions.php on line 113错误解读：这个错误信息告诉我们：在handsome主题的functions.php文件第113行，代码试图用一个整数(int)减去一个字符串(string)，这在PHP中是不被允许的操作。错误定位：顺藤摸瓜找到源头分析错误日志提供的线索：关键信息错误文件：handsome主题的functions.php错误位置：第113行错误类型：TypeError，类型不匹配调用链路：sidebar.php(25) → online_users()堆栈信息清晰地展示了错误的传播路径：侧边栏调用online_users()函数时触发了错误。这个函数用于统计当前在线的访客人数。问题定位：在线人数统计功能在特定情况下产生了类型不匹配，导致整个页面渲染失败。深入分析：代码脆弱性查看online_users()函数的核心逻辑：function online_users() { $filename = 'online.txt'; // 存储访客信息的文件 $onlinetime = 30; // 30秒在线有效期 $nowtime = $_SERVER['REQUEST_TIME']; // 当前时间戳(整数) $online = file($filename); // 读取文件内容为数组 $nowonline = array(); // 处理每条访客记录 foreach ($online as $line) { $row = explode('|', $line); // 分割用户ID和时间戳 $sesstime = trim($row[1]); // 获取时间戳(字符串!) // 问题代码就在这里 if (($nowtime - $sesstime) <= $onlinetime) { $nowonline[$row[0]] = $sesstime; } } return count($nowonline); }核心问题出在$nowtime - $sesstime这一运算：$nowtime是一个整数时间戳$sesstime是从文件读取的字符串警告：在线运行环境中，online.txt文件可能出现各种异常情况：存在空行某行格式错误，缺少分隔符|时间戳部分包含非数字字符当这些情况发生时，trim($row[1])可能返回空字符串或非数值内容，导致PHP在执行整数 - 字符串运算时抛出TypeError，整个程序终止运行。解决之道：防御性编程防御性编程思想：面对外部数据的不确定性，我们不能寄希望于数据永远完美，而应让代码能够优雅地处理各种异常情况。针对这个问题，我们需要两个关键改进：存在性检查：在使用数组元素前，先确认它们是否存在类型安全转换：在执行数学运算前，将可能的字符串显式转换为整数修改后的代码：function online_users() { $filename = 'online.txt'; $onlinetime = 30; $nowtime = $_SERVER['REQUE…