统计对象:
D:\phpstudy_pro\WWW\release\core\flinthub1.2.14统计日期:2026-10-26
统计工具:PHP 8.2.9 官方
token_get_all()分词器(非正则剥离)+ 自研分层汇总脚本脚本位置:
.audit_tmp/count_php.php、.audit_tmp/layer_agg.php(可复现)
一、统计口径说明(先厘清,再给数字)
1.1 总行数到底是 58,315 还是 58,251?
两个数字都对,差异来自计数方式,我在上一轮口头引用的是后者:
| 计数方式 | 数值 | 含义 |
|---|---|---|
| 物理行数(本文采用) | 58,315 | 按「内容行」计:换行符数 + 1(每行文字都算一行) |
wc -l 口径 | 58,251 | 只数换行符,末尾无换行的最后一行不被计入 |
| 差值 | 64 | 恰好等于末尾缺少换行符的 PHP 文件数(实测 64 个) |
# 验证:无末尾换行的文件数
$ php -r '...' → 无末尾换行: 64
$ find . -name "*.php" | xargs wc -l | tail -1 → 58251
结论:58,315 是准确的物理行数,58,251 是我上次用
wc -l得出的偏小值。本报告统一采用 58,315。
1.2 是否包含注释行和空行?
原始数字包含,但本报告全部拆开呈现。 统计方式不是"估算注释比例",而是逐行判定:
- 用
token_get_all()拿到每个 token 的起始行号与跨行数; T_COMMENT/T_DOC_COMMENT覆盖到的行 → 注释行;- 其余非
T_WHITESPACEtoken 覆盖到的行 → 纯代码行; - 两者都不是 → 空行。
这样能正确处理三种正则容易出错的情况:
- 块注释跨行(
/** ... */占 20 行 → 20 行全部计为注释); - 字符串里含
//或#(如 URLhttps://...)→ 不会误判为注释; - 模板文件
<?php ?>之间的 HTML(app/Views/*.php)→T_INLINE_HTML计为代码行,因为它是实际输出的模板内容。
三者相加恒等于总行数,已做闭合校验:44,059 + 10,261 + 3,995 = 58,315 ✓
1.3 统计范围包含哪些后缀?
| 后缀 | 是否计入 | 说明 |
|---|---|---|
.php | ✅ | 202 个文件,58,315 行(核心统计对象) |
.css .js | ✅ | 17 + 13 个文件,含 sw.js;行数统计对压缩库无意义,另附字节数 |
.html | — | 本包内 0 个:视图全部是 .php(模板引擎自行编译 .html,但发行包未提供任何 .html 视图) |
.json .md .conf | ❌ | 文档与配置,非可执行代码(docs/ 188KB 单独标注) |
.png .svg .woff2 .ttf .ico | ❌ | 二进制/矢量资源,不适用行数度量 |
1.4 是否包含 plugins/ 插件代码?
完全不含。 本次审计对象是纯净核心包(改动说明.md 明确:"不含 plugins/ 整个目录"),实测该目录不存在:
$ ls -d plugins
ls: cannot access 'plugins': No such file or directory
因此所有数字均为核心包自身体量。线上完整站点若安装 16~31 个插件,总代码量会显著高于本报告。
1.5 第三方库如何处理?
关键处理:第三方库单独剥离,不参与架构评价。原因是对压缩/发行版文件按行统计会严重失真——assets/lib/qrcode.min.js 只有 1 行却是 19,927 字节;fhajax.js(htmx v2.0.10)只有 16 行却是 51,598 字节。
| 第三方库文件 | 行数 | 字节 | 来源 |
|---|---|---|---|
assets/lib/fhajax.js | 16 | 51,598 | htmx v2.0.10 (0BSD) |
assets/lib/fhstate.js | 41 | 47,810 | Alpine.js v3.15.12 (MIT) |
assets/lib/qrcode.min.js | 1 | 19,927 | qrcode 压缩版 |
assets/js/marked.min.js | 69 | 39,903 | marked v15.0.12 (MIT) |
assets/js/turndown.js | 804 | 26,930 | Turndown (MIT) |
assets/js/turndown-plugin-gfm.js | 165 | 4,191 | Turndown GFM 插件 |
assets/css/fontawesome.css | 154 | 7,229 | FontAwesome |
assets/css/tabler-icons.css | 62 | 3,769 | Tabler Icons |
| 小计 | 1,312 | 201,357 | — |
⚠️ 注意一个反直觉现象:这 8 个第三方库合计 1,312 行,却占 201KB;而自研 23 个 css/js 合计 12,180 行仅占 548KB。按行数会严重低估第三方依赖的体积占比(字节占比达 26.9%)。
二、按 MVC 分层统计
2.1 主表(自研代码,已剔除第三方库)
| 层 | 目录 | 文件 | 总行数 | 纯代码 | 注释 | 空行 | 字节 |
|---|---|---|---|---|---|---|---|
| Model | app/Models/ | 10 | 3,908 | 2,580 | 957 | 371 | — |
| Model | app/SplitDB/ | 14 | 4,907 | 3,217 | 1,318 | 372 | — |
| Model 小计 | 24 | 8,815 | 5,797 | 2,275 | 743 | — | |
| View | app/Views/(72 个模板) | 72 | 9,992 | 9,546 | 420 | 26 | — |
| View | assets/ css+js + sw.js | 23 | 14,430 | 10,685 | 2,510 | 1,235 | 548,244 |
| View | lang/(zh/en/zh_tw) | 3 | 5,220 | 5,040 | 114 | 66 | — |
| View 小计 | 98 | 29,642 | 25,271 | 3,044 | 1,327 | 548,244 | |
| Controller | app/Controllers/ | 35 | 10,246 | 7,346 | 1,851 | 1,049 | — |
| Controller | index.php(前端控制器) | 1 | 488 | 322 | 130 | 36 | — |
| Controller | app/Core/Router.php | 1 | 131 | 97 | 13 | 21 | — |
| Controller 小计 | 37 | 10,865 | 7,765 | 1,994 | 1,106 | — | |
| Infra | app/Helpers/ | 32 | 14,738 | 9,662 | 3,768 | 1,308 | — |
| Infra | app/Core/(除 Router) | 6 | 2,100 | 1,544 | 407 | 149 | — |
| Infra | app/init.php | 1 | 198 | 127 | 58 | 13 | — |
| Infra | cli/(24 个运维脚本) | 24 | 5,296 | 3,699 | 1,079 | 518 | — |
| Infra | install.php / cron_trigger.php / version.php | 3 | 1,091 | 879 | 146 | 66 | — |
| Infra 小计 | 66 | 23,423 | 15,911 | 5,458 | 2,054 | — | |
| 合计(自研) | 225 | 72,745 | 54,744 | 12,771 | 5,230 | 548,244 | |
| 第三方库(参照) | assets/lib、marked 等 | 8 | 1,312 | — | — | — | 201,357 |
| 总计(含三方) | 233 | 74,057 | — | — | — | 749,601 |
2.2 四层占比(按纯代码行)
| 层 | 文件 | 纯代码行 | 占比 | 判定 |
|---|---|---|---|---|
| View | 98 | 25,271 | 46.2% | 偏高(但含 5,040 行语言包,属正常) |
| Infra | 66 | 15,911 | 29.1% | 偏高,反映自研框架成本 |
| Controller | 37 | 7,765 | 14.2% | 合理 |
| Model | 24 | 5,797 | 10.6% | 偏低(详见评价) |
2.3 View 层内部构成(避免被"46.2%"误导)
| 子项 | 纯代码行 | 占 View | 性质 |
|---|---|---|---|
模板 app/Views/ | 9,546 | 37.8% | 展示逻辑 |
| 前端 css/js | 10,685 | 42.3% | 样式与交互(其中 editor.js 单文件 5,127 行) |
语言包 lang/ | 5,040 | 19.9% | 纯文案数据(3 语言 × 1,680 行),非逻辑代码 |
若剔除语言包,View 逻辑代码为 20,231 行,占剔除后总代码(49,704 行)的 40.7%——这才是公平的视图层负担。
2.4 Controller 内部构成
| 子项 | 纯代码行 | 文件 | 平均 |
|---|---|---|---|
| 前台控制器 | 3,321 | 16 | 208 行/个 |
后台控制器 Admin/ | 4,025 | 19 | 212 行/个 |
路由分发 index.php + Router.php | 419 | 2 | — |
前后台体量几乎 1:1(3,321 : 4,025),说明后台管理功能与前台业务同等复杂。
三、架构健康度评价
3.1 总体结论
这是一个"分层清晰、但职责边界被框架层挤压"的架构。 MVC 骨架是真实的(视图零 SQL、控制器无直连 PDO),但真正承担复杂度的是 Helpers/(15,911 行,占 29.1%)——它既是基础设施,也悄悄装下了大量业务逻辑。
3.2 做得好的地方
① 视图层纯净,零数据库越界(这条很少见)
我专门核查了"视图是否偷偷查库"这个 MVC 最常见的破窗:
$ grep -rn "getInstance\|SELECT" app/Views/
(无输出)
72 个模板、9,546 行,没有任何一处直接访问数据库或写库。数据全部由控制器注入,视图只做展示与转义($this->e() 用了 621 次)。这在自研 PHP 项目里属于优秀水平。
② 控制器不臃肿——平均 210 行/个
35 个控制器、7,346 行纯代码,平均值 210 行。对比常见自研项目动辄 800~2000 行的巨型控制器,这个粒度是健康的。即使最大的 Admin/UpdateController(932 行)也在可接受范围。
③ 模型层承担了真正的领域复杂度
Thread.php(955 行)完整封装了分片读写、extern 正文剥离、摘要生成、缓存联动;SplitDB/(3,217 行)把分片路由、连接池、扩容、队列全部内聚。没有把分片逻辑泄漏到控制器,这是本架构最值得肯定的设计。
④ 注释率 17.6%,且注释质量高
12,771 行注释 / 72,745 行。不是无意义的函数签名注释,而是大量"为什么这么做"的决策记录(如"为什么不用 RETURNING"、"为什么 BEGIN IMMEDIATE 不能用 PDO 事务")。SplitDB/Schema.php 注释率 23.3%(245/1,052),Models/Thread.php 达 26.2%(390/1,488),属于知识密集型代码。
3.3 需要关注的结构性问题
① Model 层占比仅 10.6% —— 偏低,业务逻辑外溢到 Helpers
健康的 MVC 中 Model 通常占 20~30%。这里只有 10.6%,缺口去哪了?看 Helpers/ 的构成:
| Helper | 纯代码行 | 实际职责 | 是否越界 |
|---|---|---|---|
Updater.php | 1,618 | 升级包下载/验签/差异/应用/回滚 | 越界(应为独立领域服务) |
Settings.php | 1,263 | 配置读写 + 在线状态 + 计数器 + 孤儿附件清理 | 严重越界 |
Plugin.php | 725 | 插件加载 + 权限判定 + 数据库 + 钩子 | 越界 |
Market.php | 605 | 跨站插件商店安装(远程拉取 + 验签 + 落盘) | 越界 |
Settings.php 一个文件里塞了「读配置」「更新在线状态(写 online_users 表)」「运行时计数器」「清理孤儿附件(遍历上传目录 + 删文件)」四类互不相关的事——它是 Helpers 层最该拆分的地方。
② 三个巨石文件
| 文件 | 总行 | 纯代码 | 问题 |
|---|---|---|---|
app/Helpers/Updater.php | 2,359 | 1,618 | 集验签/HTTP/解压/备份/应用/回滚于一身,8 类职责 |
app/Helpers/Settings.php | 1,954 | 1,263 | 配置 + 在线状态 + 计数器 + 文件清理 |
app/Controllers/Admin/UpdateController.php | 1,427 | 932 | 14 个 POST 动作 + 状态机 + 进度轮询 |
Updater.php + UpdateController.php 合计 2,550 行纯代码,占 Controller 层的 33%——升级功能一个模块吃掉了三分之一个控制器层,这是职责过度集中的应用信号。
③ 前端有一处明显的单体脚本
assets/js/editor.js 5,127 行(241,753 字节),占自研前端代码行的 48.0%、字节的 44.1%。富文本编辑器本身就是复杂组件,但单文件超 5,000 行已不利于维护(无模块拆分、无类型约束)。这是前端侧最需要重构的点。
④ 语言包占 View 层 20%,但统计方式值得商榷
lang/ 5,040 行是"数据"不是"代码"。若严格按"可执行逻辑"口径,应从代码行中剔除,则总代码量为 49,704 行,View 占比降至 40.7%。我在主表中保留它(因为它是 PHP 数组、确实占文件行),但引用占比时应说明这一点,否则会高估视图层。
3.4 分层健康度评分
| 维度 | 评分 | 依据 |
|---|---|---|
| 分层清晰度 | ★★★★☆ | 视图零 SQL、控制器无 PDO、模型封装分片——边界干净 |
| Model 内聚度 | ★★★☆☆ | 分片引擎优秀,但业务逻辑分散在 Helpers |
| Controller 粒度 | ★★★★☆ | 平均 210 行,无巨型控制器(除 Update 相关) |
| View 可维护性 | ★★★☆☆ | 模板整洁,但 editor.js 5,127 行是隐患 |
| Infra 职责单一性 | ★★☆☆☆ | Settings.php / Updater.php 职责过载 |
| 注释/可读性 | ★★★★★ | 17.6% 注释率且含大量决策记录,罕见 |
四、附:可直接复现的统计命令
cd D:/phpstudy_pro/WWW/release/core/flinthub1.2.14
# 1. PHP 逐行分类(tokenizer 精确判定)
"D:/phpstudy_pro/Extensions/php/php8.2.9nts/php.exe" .audit_tmp/count_php.php . > .audit_tmp/php_counts.json
# 2. 分层汇总(含 css/js,剥离第三方库)
"D:/phpstudy_pro/Extensions/php/php8.2.9nts/php.exe" .audit_tmp/layer_agg.php . > .audit_tmp/layers.json
# 3. 闭合校验:纯代码 + 注释 + 空行 == 总行数
# 44,059 + 10,261 + 3,995 = 58,315 ✓
统计口径一览
| 项目 | 取值 |
|---|---|
| 统计方式 | 物理行(换行符数 + 1),非 wc -l |
| 注释判定 | token_get_all() 的 T_COMMENT / T_DOC_COMMENT,支持跨行块注释 |
| 模板 HTML | T_INLINE_HTML 计为代码行(属实际输出内容) |
| 后缀范围 | .php 全额;.css/.js 计入并标注字节;.html 本包为 0 |
| 插件 | 不含(纯净包,无 plugins/ 目录) |
| 第三方库 | 8 个文件、1,312 行、201KB,单列不计入架构评价 |
报告生成:2026-10-26 | 数据源:flinthub1.2.14 纯净核心包 | 所有数字经三项闭合校验