关于 typo3-simplify
用于精简近期修改的 TYPO3 v14 代码并保持功能不变的代码审查技能。它引导开发者并行检查 TYPO3 API 复用、代码质量和效率,再应用并验证修复。
主要功能
- 01三阶段代码审查
- 02TYPO3 v14 API 与弃用指引
- 03PHP、Fluid、TCA 和服务检查
- 04效率与查询审查
- 05保持行为的修复与验证
适用场景
- 01审查近期 TYPO3 扩展改动
- 02用 TYPO3 核心 API 替换自定义代码
- 03发现 v14 弃用模式
- 04减少低效数据库访问
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dirnbauer/ webconsulting-skills --skill typo3-simplify安装后在 Claude Code 中输入/typo3-simplify确认已加载。
详细介绍
TYPO3 代码简化器
根据 Boris Cherny(Anthropic)面向 TYPO3 场景的代码简化 Agent 改编。 **目标:**仅适用于 TYPO3 v14.x。
简化并改进最近更改的 TYPO3 代码。保留所有功能。优先考虑清晰易懂,而非巧妙复杂;优先使用 TYPO3 API,而非自定义实现;优先采用 v14 模式,而非已弃用的做法。
流程
- 确定最近修改的文件(使用
git diff --name-only HEAD~1或暂存的更改) - 并行执行三轮审查:复用、质量、效率
- 汇总审查结果、去重,并按影响排序
- 应用修复,验证行为未发生变化
第 1 轮:复用 TYPO3 API
查找重复实现 TYPO3 现有功能的自定义代码。
用核心 API 替换自定义代码
| 自定义模式 | TYPO3 API 替代方案 |
|---|---|
手动数据库查询($connection->executeQuery(...)) | 使用命名参数的 QueryBuilder |
$GLOBALS['TYPO3_REQUEST'] | 通过中间件/控制器注入 ServerRequestInterface |
new FlashMessage(...) + 手动入队 | 通过 DI 使用 FlashMessageService |
| 手动构造 JSON 响应 | 使用 PSR-7 中的 JsonResponse |
GeneralUtility::makeInstance() | 通过 Services.yaml 进行构造函数注入 |
$GLOBALS['TSFE']->id | $request->getAttribute('frontend.page.information')?->getId()(v14 的规范方式;routing 页面 ID 也可用) |
$GLOBALS['BE_USER'] | 注入 Context 或 BackendUserAuthentication |
ObjectManager::get() | 构造函数 DI |
| 手动解析文件路径 | 使用 PathUtility、Environment::getPublicPath() |
| 使用全局变量进行自定义缓存 | 通过 DI 使用带缓存配置的 CacheManager |
对单个字段使用 BackendUtility::getRecord() | 使用 QueryBuilder,仅选择所需列 |
| 手动遍历页面树 | 使用 RootlineUtility 或 PageRepository |
GeneralUtility::_GP() / _POST() / _GET() | $request->getQueryParams() / getParsedBody() |
写入 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF'] | 使用 PSR-14 事件或 ExtensionConfiguration |
| 手动生成链接 | 使用 UriBuilder(后端)或 ContentObjectRenderer::typoLink() |
替换已弃用模式(v14)
| 已弃用 | v14 替代方案 |
|---|---|
ext_localconf.php 钩子数组 | 在 PSR-14 事件上使用 #[AsEventListener] |
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] 钩子 | PSR-14 事件 |
ext_tables.php 模块注册 | Configuration/Backend/Modules.php |
XCLASS | PSR-14 事件或 DI 装饰 |
用于 required、null 的 TCA eval | 专用 TCA 键:required、nullable |
用于 trim 的 TCA eval | 在 v14 中仍然有效——没有专用的 TCA 替代方案;需要时保留 eval 中的 trim |
renderType => 'inputDateTime' | 'type' => 'datetime' |
'type' => 'input', 'eval' => 'int' | 'type' => 'number' |
使用数字数组键的 items | 使用 label/value 键的 items |
历史移除项(你可能仍会看到的 v14 之前代码)
| 已移除 / 旧式做法 | 时间 | 现代方案 |
|---|---|---|
switchableControllerActions | 在 v10.3 弃用(#89463),在 v12.0 移除 | 单独注册插件 |
Signal/Slot Dispatcher | 在 v12 移除 | PSR-14 事件 |
AbstractPlugin(pi_base) | v12.0 标记为 @internal(#98281),v12.4 弃用(#100639),v13.0 移除 | Extbase 或中间件 |
代码质量(非弃用项)
| 主题 | 指引 |
|---|---|
$querySettings->setRespectStoragePage(false) | 有效的 Extbase API——为保持清晰,优先在仓库工厂或控制器中设置查询设置,而不是分散在各个仓库中 |
第 2 轮:代码质量
PHP 类
- 每个文件定义一个类,使用 PSR-4 自动加载
- 每个 PHP 文件都使用
declare(strict_types=1) - 对不为继承而设计的类使用
final - 对不可变属性使用
readonly - 使用构造函数属性提升注入依赖
- 所有方法都显式声明返回类型
- 不要使用未用到的
use导入 - 不要屏蔽错误(
@) - 优先使用守卫子句,避免深层嵌套(尽早返回)
- 有具体类型可用时,不要使用 mixed 类型
- 将
array类型提示替换为带类型的数组或 DTO
// Before
class MyService
{
private ConnectionPool $connectionPool;
private Context $context;
public function __construct(ConnectionPool $connectionPool, Context $context)
{
$this->connectionPool = $connectionPool;
$this->context = $context;
}
public function getData($id)
{
// ...
}
}
// After
final class MyService
{
public function __construct(
private readonly ConnectionPool $connectionPool,
private readonly Context $context,
) {}
public function getData(int $id): array
{
// ...
}
}
Fluid 模板
- 不要使用内联 PHP 或复杂的 ViewHelper 链
- 使用
<f:translate>,不要硬编码字符串 - 使用
<f:link.page>/<f:link.typolink>,不要手动编写<a href> - 使用
<f:image>,不要手动编写<img>标签 - 对重复标记使用 Partial(DRY)
- Section 用于布局插槽,不用于复用(请使用 Partial)
- 除非绝对必要,否则不要使用
{variable -> f:format.raw()}(有 XSS 风险) - 变量使用 camelCase
- 删除空的
<f:section>块 - 在更清晰时,将嵌套的
<f:if>简化为<f:switch>或三元表达式
TCA
- 使用 v14 的
items格式,包含label/value键 - 删除已受限制字段上冗余的
'exclude' => true - 使用专用类型:
'type' => 'email'、'type' => 'datetime'、'type' => 'number'、'type' => 'link'、'type' => 'color'、'type' => 'json' - 使用
'required' => true,不要使用'eval' => 'required' - 使用
'nullable' => true,不要使用'eval' => 'null' - 仍支持
eval=>trim— 除非在其他层替代了修剪操作,否则不要删除 - 删除字符串字段上的
'default' => ''(这本来就是默认值) - 合并 palette 定义(删除仅包含一个字段的 palette)
- 删除 TYPO3 v14 中由
ctrl自动创建的样板columns定义:hidden、starttime、endtime、fe_group、sys_language_uid、l10n_parent、l10n_diffsource - 对 enablecolumns 使用 palette:Core 对
hidden使用 palette 键hidden,对starttime、endtime、fe_group使用access(请在表的typesshowitem中确认键名) - 当
ctrl启用了 标准 ctrl 列 时,从ext_tables.sql中删除它们(hidden、starttime、endtime、fe_group、sys_language_uid、l10n_parent、l10n_source、sorting、deleted、crdate、tstamp)——仅当这些列由该表自动管理时 - 从各类型中删除未使用的
showitem字段
Services.yaml
- 使用自动装配(对于可通过类型提示解析的依赖,删除显式参数定义)
- 使用
_defaults: autowire: true, autoconfigure: true, public: false - 删除自动装配可处理的类的手动服务定义
- 自动装配可处理构造时,删除
factory:块;对于自动装配无法解析的非简单构造,保留factory:。#[Autoconfigure]标签/配置服务,但不能替代factory:构造 - 除非
GeneralUtility::makeInstance()需要,否则删除public: true
ext_localconf.php / ext_tables.php
- 尽量精简代码 — 尽可能移至
Configuration/文件 - 仅注册插件(不包含业务逻辑)
- 前端插件:在
ext_localconf.php中使用ExtensionUtility::configurePlugin(),并且在Configuration/TCA/Overrides/tt_content.php中使用ExtensionUtility::registerPlugin()(或 TCAitems)— 两者都是完整注册所必需的 - 不要使用
addPageTSConfig— 请使用Configuration/page.tsconfig - 不要使用
addUserTSConfig— 请使用Configuration/user.tsconfig - 不要使用
addTypoScript— 请使用Configuration/TypoScript/setup.typoscript
第 3 轮:效率
- QueryBuilder:仅选择所需列,而不是
* - QueryBuilder:预期结果为单行时,添加
setMaxResults() - 使用
count()查询代替获取所有行后再计数 - 使用 TYPO3 Caching Framework 缓存开销较大的操作
- 避免 Extbase 仓库中的 N+1 查询(使用
JOIN或批量加载) - 使用
TYPO3\CMS\Core\Resource\ProcessedFileRepository,不要在每次请求时重新处理文件 - 移除没有分页的
findAll()调用 - 使用
#[\TYPO3\CMS\Extbase\Attribute\ORM\Lazy]延迟加载文件引用(替换旧版@Lazy注解) - 用 QueryBuilder
WHERE IN替换foreach+ 手动in_array()筛选 - 移除 CLI 命令中多余的
cache:flush调用
输出格式
分析后,按文件分组报告发现:
## Classes/Controller/MyController.php
:42 — replace GeneralUtility::makeInstance(MyService::class) → constructor injection
:18 — add return type `: ResponseInterface`
:55 — deprecated: $GLOBALS['TSFE']->id → $request routing attribute
:67 — guard clause: invert condition, return early, reduce nesting
## Resources/Private/Templates/List.html
:12 — hardcoded string "No items found" → f:translate
:34 — manual <a href> → f:link.page
## Configuration/TCA/Overrides/tt_content.php
:8 — legacy items format `['Label', 'value']` → `['label' => 'Label', 'value' => 'value']`
Applied 7 fixes. No behavior changes. Run tests to verify.
版本回退方案
当同一代码库必须同时运行于 TYPO3 v13 和 v14(双版本扩展)时,可以暂时保留过渡模式:
- 在
Configuration/RequestMiddlewares.php中注册 PSR-15 中间件(Core 支持的模式) - 保留
Services.yaml事件监听器配置,同时使用#[AsEventListener] - 保留数字型 TCA
items数组,同时使用label/value格式 - 优先使用
Services.yaml_defaults(autowire/autoconfigure)以及 Core 注册属性(#[AsCommand]、#[AsEventListener],……),而不是临时拼凑的factory:配置块
仅适用于 v14 的简化目标
以下简化机会仅适用于 v14。
v14 简化模式 [仅限 v14]
| 模式 | 简化方式 |
|---|---|
$GLOBALS['TSFE'] 访问 | 在 v14 中会触发致命错误(Breaking #107831)——替换为 $request->getAttribute('frontend.page.information') |
Extbase 注解(@validate) | 替换为 #[\TYPO3\CMS\Extbase\Attribute\Validate] |
MailMessage->send() | 注入 TYPO3\CMS\Core\Mail\MailerInterface 并调用 $this->mailer->send($email) |
使用 FlexFormService | 优先使用 FlexFormTools;FlexFormService 在 v14 中仍作为 BC 别名保留,但新代码中不应使用 |
| Bootstrap Modal JS | **前端:**在适当情况下使用原生 <dialog>。后端:使用 @typo3/backend/modal——它在 v14 中封装了原生 <dialog>(Breaking #107443);不要使用原始 Bootstrap modal JS |
TCA ctrl.searchFields(在 v14 中已移除) | TYPO3 v14 会自动推导后端搜索字段;在支持的列上通过 'searchable' => true/false 调整是否纳入搜索(支持的字段类型:input、text、email、link、slug、color、datetime(不带自定义 dbType)、flex、json、uuid) |
| 自定义本地化解析器 | 已由 Symfony Translation Component 集成取代(Feature #107436);v14 中仍维护向后兼容性——新代码优先使用新的 Symfony Translation API |
GeneralUtility::createVersionNumberedFilename() | 替换为 SystemResourceFactory / SystemResourcePublisherInterface(System Resource API,Feature #107537) |
致谢与署名
此 skill 基于 Anthropic 的优秀成果。
原始仓库:https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-simplifier
特别感谢 Anthropic 慷慨的开源贡献,这些贡献帮助塑造了这个 skill 集合。 由 webconsulting.at 为此 skill 集合改编
评论
支持 Markdown