mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-08-30 12:45:32 +08:00
- 删除 tools:agent:publish 整套(不再兼容多套 IDE,只保留 .agents 目录) - helper.php 全部 27 个全局函数加 if (!function_exists()) 包裹,项目侧可在 app/common.php 定义同名函数覆盖 - ulthon-file-override-mechanism.md 新增全局函数覆盖章节 - AGENTS.md 删除智能体指导章节,PHP 8+ 改为 8.0+,MySQL 8+ 改为 5.7+ - AGENTS.md 项目结构树补全 tests/、extend/think/、source/ 子项 - README.md PHP 版本统一为 8.0+ - ulthon-base-app-architecture SKILL 路径补 admin/service/ 段 - ulthon-tools-http-call SKILL 补 --page-data 参数 - ulthon-naming-convention 补元数据头 - ulthon-update-workflow 统一 extend/think/ 归类
115 lines
6.8 KiB
Markdown
115 lines
6.8 KiB
Markdown
# 文件级覆盖机制(app/ 覆盖 extend/base/)
|
||
|
||
> 来源:框架内置(ulthon-)
|
||
> 作用域:所有"框架默认文件 → 业务同名文件"的覆盖场景(视图模板、include 标签、`app_file_path()` 加载的 PHP/配置/模板)
|
||
> 触发条件:需要替换框架内置视图/模板/跳转页/异常页/定时任务配置/CURD 模板/版本升级钩子时加载
|
||
|
||
## 规则内容
|
||
|
||
### 概述
|
||
|
||
Ulthon Admin 在"类继承覆盖"之外,还提供"文件级覆盖"能力:业务侧在 `app/` 下放置与 `extend/base/` 同路径、同名的文件,框架运行时会优先加载 app 版本。
|
||
|
||
该机制只对**特定类型**的文件生效,不是所有文件都支持。覆盖能力分三类:
|
||
|
||
- 机制一:视图模板覆盖(最核心、最隐蔽,控制器 `fetch()` 自动生效)
|
||
- 机制二:模板内 `{include}` 标签覆盖(与机制一同源)
|
||
- 机制三:`app_file_path()` 辅助函数显式覆盖(需调用方主动使用)
|
||
|
||
### 机制一:视图模板覆盖
|
||
|
||
- 实现位置:`extend/think/view/driver/Think.php:160-257` 的 `parseTemplate()` 方法
|
||
- 不走 `app_file_path`,由视图驱动自身完成三级回退查找:
|
||
1. `app/<module>/view/<template>.html`(最高优先,app 应用视图)
|
||
2. `extend/base/<module>/view/<template>.html`(框架兜底视图)
|
||
3. `<root>/view/<template>.html`(根目录全局视图,最低优先)
|
||
- 触发场景:所有控制器 `fetch()` 渲染视图时(经 `ViewBase::fetch()` → `think.View::fetch()` → `Think::fetch()` → `parseTemplate()`)
|
||
- 业务侧用法:在 `app/<module>/view/` 下放同名 `.html` 即可覆盖框架视图
|
||
|
||
### 机制二:模板内 `{include}` 标签覆盖
|
||
|
||
- 实现位置:`extend/think/Template.php:1192-1248` 的 `parseTemplateFile()` 方法
|
||
- 查找逻辑:与机制一相同的三级查找(`app_path` / `base_app_path` / `view_app_path`,由 `Think.php:196-198` 注入到 Template 实例)
|
||
- 前缀规则:
|
||
- `@/` 开头:从根目录定位(三级路径都设为同一个 `view_root_path`)
|
||
- `@` 开头:从控制器视图目录定位(用 `base_view_path`)
|
||
- `./` 开头:相对当前模板文件目录
|
||
- 无前缀:走完整三级查找
|
||
- 触发场景:模板编译时解析 `{include file="..."}` 标签
|
||
|
||
### 机制三:`app_file_path()` 辅助函数
|
||
|
||
- 定义位置:`extend/base/helper.php:392-400`
|
||
- 加载入口:`app/common.php:6`(唯一 include 点;这是 AGENTS.md 中明确的全局辅助函数唯一特许)
|
||
- 查找层级(两级回退):
|
||
1. `app/<相对路径>`(最高优先)
|
||
2. `extend/base/<相对路径>`(兜底)
|
||
- 函数签名约束(来自源码注释):
|
||
- 第一个参数 `$file_path` 不要以 `/` 开头
|
||
- 不需要以 `app` 开头
|
||
- 函数内部会自动定位 `app/` 或 `extend/base/`
|
||
|
||
#### 已知调用点(业务侧可在 app/ 同路径下覆盖其中任一)
|
||
|
||
| 调用点 | 相对路径参数 | 文件类型 | 业务意义 |
|
||
|---|---|---|---|
|
||
| `config/app.php:35` | `common/tpl/think_exception(_debug).tpl` | 异常模板 | 调试异常页 |
|
||
| `config/app.php:37,39` | `common/tpl/dispatch_jump.tpl` | 跳转模板 | success/error 跳转页 |
|
||
| `TimerServiceBase:19,71` | `common/command/timer/config.php` | 配置数组 | 定时任务列表(可被业务定制) |
|
||
| `AdminUpdateCodeServiceBase:23` | `admin/service/adminUpdateCodeData/{version}.php` | PHP 钩子文件 | 版本升级时执行的迁移代码 |
|
||
| `MigrateBase:207` | `common/command/curd/migrate.tpl` | 视图模板 | CURD 生成迁移代码的模板 |
|
||
|
||
### 反例:以下场景不支持文件级覆盖
|
||
|
||
下列路径与机制都不走 app/base 覆盖,业务侧在 `app/` 下放同名文件**不会**生效:
|
||
|
||
- `AdminInitServiceBase::requireData()`(`extend/base/admin/service/AdminInitServiceBase.php:139`):用 `__DIR__` 直接定位 `extend/base/admin/service/adminInitData/`,不支持 app 覆盖
|
||
- 命令注册(`config/console.php`):空 `commands` 数组,命令通过 SPI 自动扫描 `app/common/command/`,不走覆盖
|
||
- 语言包(`config/lang.php`):标准 ThinkPHP 机制,不支持 app/base 覆盖
|
||
- 路由(`route/app.php`):空文件,无覆盖机制
|
||
- 静态资源(`public/static/`):Filesystem disk 加载,无覆盖机制
|
||
- 配置目录(`config/*`):标准 ThinkPHP 配置加载,无 app/base 覆盖机制
|
||
|
||
### 与"类继承覆盖"的关系
|
||
|
||
类继承层面的 Base/App 覆盖(如 `app\<Class>` extends `base\<Class>Base`)属于**另一套机制**,详见技能:[ulthon-base-app-architecture](../skills/ulthon-base-app-architecture/SKILL.md)。本规则只讲"文件级"覆盖,不涉及类的继承重写。
|
||
|
||
### 业务侧使用范例
|
||
|
||
- 覆盖异常页样式:在 `app/common/tpl/think_exception.tpl` 放同名文件
|
||
- 覆盖定时任务配置:在 `app/common/command/timer/config.php` 放同名文件(实际项目已在用)
|
||
- 覆盖某控制器视图:在 `app/admin/view/<controller>/<action>.html` 放同名文件
|
||
- 覆盖 CURD 迁移模板:在 `app/common/command/curd/migrate.tpl` 放同名文件
|
||
|
||
## 全局函数覆盖
|
||
|
||
`extend/base/helper.php` 里的所有全局函数都有 `if (!function_exists('xxx'))` 包裹。项目侧如需覆盖某个全局函数的行为(例如修复框架 bug 或调整实现),只需在 `app/common.php` 的 `include helper.php` 语句**之前**定义同名函数即可。
|
||
|
||
加载顺序:`app/common.php` 先执行 → 项目侧函数先定义 → `include helper.php` 时 `if (!function_exists())` 检查发现已存在 → 跳过框架版本。
|
||
|
||
示例:
|
||
|
||
```php
|
||
// app/common.php
|
||
function sysconfig($group, $name = null, $default = null) {
|
||
// 项目侧自定义实现
|
||
return my_custom_logic($group, $name, $default);
|
||
}
|
||
|
||
include App::getRootPath() . '/extend/base/helper.php';
|
||
```
|
||
|
||
注意:该机制是"函数级覆盖",与本文档前述三类"文件级覆盖"是独立机制。helper.php 中已包裹的函数清单见源码(截至当前版本共 27 个:`__url`、`flow_url`、`password`、`xdebug`、`sysconfig`、`array_format_key`、`auth`、`get_session_admin`、`read_header_token`、`json_message`、`unparse_url`、`build_upload_url`、`event_handle_result`、`event_handle_string`、`event_view_content`、`event_view_replace`、`event_view_replace_js`、`event_response`、`app_file_path`、`array_to_table`、`format_bytes`、`parse_bytes`、`get_store_value`、`set_store_value`、`get_site_version_key`、`format_null_value`、`ctype_numeric`)。
|
||
|
||
## 相关文件
|
||
|
||
- `extend/base/helper.php`(`app_file_path()` 定义)
|
||
- `app/common.php`(辅助函数唯一加载入口)
|
||
- `extend/think/view/driver/Think.php`(视图驱动 `parseTemplate()`)
|
||
- `extend/think/Template.php`(模板编译 `parseTemplateFile()`)
|
||
|
||
## 相关技能
|
||
|
||
- [ulthon-base-app-architecture](../skills/ulthon-base-app-architecture/SKILL.md)
|
||
- [ulthon-timer](../skills/ulthon-timer/SKILL.md)(使用 `app_file_path` 加载定时器配置)
|