From craft-workspace-webconsulting-skills
Simplifies TYPO3 v14 extension code by replacing custom implementations with core APIs and deprecated patterns. Reviews PHP classes, Fluid templates, TCA, Services.yaml, and config files for best practices.
npx claudepluginhub dirnbauer/webconsulting-skillsThis skill uses the workspace's default tool permissions.
> Adapted from Boris Cherny's (Anthropic) code-simplifier agent for TYPO3 contexts.
Orchestrates large-scale TYPO3 code changes across multiple files in parallel: migrations, TCA modernization, Fluid refactoring, hook-to-event conversions, namespace renames. Decomposes into independent verifiable units with tests.
Modernizes PHP apps to 8.1+ with strict types, enums, DTOs, readonly properties, property hooks; configures PHPStan, Rector, PHP-CS-Fixer for type safety and PSR compliance.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Share bugs, ideas, or general feedback.
Adapted from Boris Cherny's (Anthropic) code-simplifier agent for TYPO3 contexts. Target: TYPO3 v14.x only.
Simplify and refine recently changed TYPO3 code. Preserve all functionality. Focus on clarity over cleverness, TYPO3 API usage over custom implementations, and v14 patterns over deprecated approaches.
git diff --name-only HEAD~1 or staged changes)Find custom implementations that duplicate what TYPO3 already provides.
| Custom Pattern | TYPO3 API Replacement |
|---|---|
Manual DB queries ($connection->executeQuery(...)) | QueryBuilder with named parameters |
$GLOBALS['TYPO3_REQUEST'] | Inject ServerRequestInterface via middleware/controller |
new FlashMessage(...) + manual queue | FlashMessageService via DI |
| Manual JSON response construction | JsonResponse from PSR-7 |
GeneralUtility::makeInstance() | Constructor injection via Services.yaml |
$GLOBALS['TSFE']->id | $request->getAttribute('frontend.page.information')?->getId() (canonical on v14; routing page id also works) |
$GLOBALS['BE_USER'] | Inject Context or BackendUserAuthentication |
ObjectManager::get() | Constructor DI |
| Manual file path resolution | PathUtility, Environment::getPublicPath() |
| Custom caching with globals | CacheManager via DI with cache configuration |
BackendUtility::getRecord() for single field | QueryBuilder selecting only needed columns |
| Manual page tree traversal | RootlineUtility or PageRepository |
GeneralUtility::_GP() / _POST() / _GET() | $request->getQueryParams() / getParsedBody() |
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF'] writes | PSR-14 events or ExtensionConfiguration |
| Manual link generation | UriBuilder (backend) or ContentObjectRenderer::typoLink() |
| Deprecated | v14 Replacement |
|---|---|
ext_localconf.php hook arrays | #[AsEventListener] on PSR-14 events |
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'] hooks | PSR-14 events |
ext_tables.php module registration | Configuration/Backend/Modules.php |
XCLASS | PSR-14 events or DI decoration |
TCA eval for required, null | Dedicated TCA keys: required, nullable |
TCA eval for trim | Still valid in v14 — no dedicated TCA replacement; keep trim in eval when you need it |
renderType => 'inputDateTime' | 'type' => 'datetime' |
'type' => 'input', 'eval' => 'int' | 'type' => 'number' |
items with numeric array keys | items with label/value keys |
| Removed / legacy | When | Modern approach |
|---|---|---|
switchableControllerActions | Deprecated in v10.3 (#89463), removed in v12.0 | Separate plugin registrations |
Signal/Slot Dispatcher | Removed in v12 | PSR-14 events |
AbstractPlugin (pi_base) | Made @internal v12.0 (#98281), deprecated v12.4 (#100639), removed v13.0 | Extbase or middleware |
| Topic | Guidance |
|---|---|
$querySettings->setRespectStoragePage(false) | Valid Extbase API — prefer setting query settings in the repository factory or controller, not scattered in repositories, for clarity |
declare(strict_types=1) on every PHP filefinal on classes not designed for inheritancereadonly on immutable propertiesuse imports@)array typehints with typed arrays or DTOs// 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
{
// ...
}
}
<f:translate> instead of hardcoded strings<f:link.page> / <f:link.typolink> instead of manual <a href><f:image> instead of manual <img> tags{variable -> f:format.raw()} unless absolutely necessary (XSS risk)<f:section> blocks<f:if> to <f:switch> or ternary where cleareritems format with label/value keys'exclude' => true on fields already restricted'type' => 'email', 'type' => 'datetime', 'type' => 'number', 'type' => 'link', 'type' => 'color', 'type' => 'json''required' => true instead of 'eval' => 'required''nullable' => true instead of 'eval' => 'null'eval => trim remains supported — do not remove unless you replace trimming in another layer'default' => '' on string fields (already default)columns definitions auto-created from ctrl on TYPO3 v14: hidden, starttime, endtime, fe_group, sys_language_uid, l10n_parent, l10n_diffsourcehidden for hidden, access for starttime, endtime, fe_group (verify keys in your table’s types showitem)ext_tables.sql when ctrl enables them (hidden, starttime, endtime, fe_group, sys_language_uid, l10n_parent, l10n_source, sorting, deleted, crdate, tstamp) — only if they are auto-managed for that tableshowitem fields from types_defaults: autowire: true, autoconfigure: true, public: falsefactory: blocks when autowiring handles construction; keep factory: for non-trivial construction that autowiring cannot resolve. #[Autoconfigure] tags/configures services but does not replace factory: constructionpublic: true unless needed for GeneralUtility::makeInstance()Configuration/ files where possibleExtensionUtility::configurePlugin() in ext_localconf.php and ExtensionUtility::registerPlugin() (or TCA items) in Configuration/TCA/Overrides/tt_content.php — both are required for a complete registrationaddPageTSConfig — use Configuration/page.tsconfigaddUserTSConfig — use Configuration/user.tsconfigaddTypoScript — use Configuration/TypoScript/setup.typoscript*setMaxResults() when expecting single rowcount() queries instead of fetching all rows to countJOIN or batch loading)TYPO3\CMS\Core\Resource\ProcessedFileRepository not re-processing on every requestfindAll() calls without pagination#[\TYPO3\CMS\Extbase\Attribute\ORM\Lazy] (replace legacy @Lazy annotation)foreach + manual in_array() filtering with QueryBuilder WHERE INcache:flush calls in CLI commandsAfter analysis, report findings grouped by file:
## 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.
When the same codebase must run on TYPO3 v13 and v14 (dual-version extensions), you may temporarily keep transitional patterns:
Configuration/RequestMiddlewares.php (Core-supported pattern)Services.yaml event listener config alongside #[AsEventListener]items arrays alongside label/value formatServices.yaml _defaults (autowire / autoconfigure) plus Core registration attributes (#[AsCommand], #[AsEventListener], …) over ad-hoc factory: blocksThe following simplification opportunities are v14-specific.
| Pattern | Simplification |
|---|---|
$GLOBALS['TSFE'] access | Fatal error in v14 (Breaking #107831) — replace with $request->getAttribute('frontend.page.information') |
Extbase annotations (@validate) | Replace with #[\TYPO3\CMS\Extbase\Attribute\Validate] |
MailMessage->send() | Inject TYPO3\CMS\Core\Mail\MailerInterface and call $this->mailer->send($email) |
FlexFormService usage | Prefer FlexFormTools; FlexFormService remains as a BC alias in v14 but should not be used in new code |
| Bootstrap Modal JS | Frontend: native <dialog> where appropriate. Backend: use @typo3/backend/modal — it wraps native <dialog> in v14 (Breaking #107443); do not use raw Bootstrap modal JS |
TCA ctrl.searchFields (removed in v14) | TYPO3 v14 derives backend search fields automatically; tune inclusion per column with 'searchable' => true/false (supported field types: input, text, email, link, slug, color, datetime (without custom dbType), flex, json, uuid) where supported |
| Custom localization parsers | Deprecated in v14 (#107436); migrate to Symfony Translation Component. Removed in v15 |
GeneralUtility::createVersionNumberedFilename() | Replace with SystemResourceFactory / SystemResourcePublisherInterface (System Resource API, Feature #107537) |
This skill is based on the excellent work by Anthropic.
Original repository: https://github.com/anthropics/claude-plugins-official/tree/main/plugins/code-simplifier
Special thanks to Anthropic for their generous open-source contributions, which helped shape this skill collection. Adapted by webconsulting.at for this skill collection