From craft-workspace-webconsulting-skills
Automates non-PHP migrations for TYPO3 v14 upgrades using Fractor. Handles FlexForms, TypoScript, Fluid, YAML, Htaccess, and composer.json changes for upgrade workflows.
npx claudepluginhub dirnbauer/webconsulting-skillsThis skill uses the workspace's default tool permissions.
Fractor is a generic file refactoring tool that automates non-PHP migrations for TYPO3 upgrades. It complements [Rector](https://github.com/sabbelasichon/typo3-rector) (which handles PHP) by migrating FlexForms, TypoScript, Fluid templates, YAML, Htaccess files, and composer.json.
Automates TYPO3 v14 PHP code upgrades using Rector refactoring rules, level sets, and migration workflows. Use for TYPO3 upgrades, rector runs, refactoring, deprecations.
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 Framer SDK upgrades: checks versions with npm, reviews changelog for breaking changes, executes updates via git/npm, applies TypeScript migration patterns for plugins and code components.
Share bugs, ideas, or general feedback.
Fractor is a generic file refactoring tool that automates non-PHP migrations for TYPO3 upgrades. It complements Rector (which handles PHP) by migrating FlexForms, TypoScript, Fluid templates, YAML, Htaccess files, and composer.json.
Target: TYPO3 v14.x for this skill collection. Upstream Fractor sets may still be named TYPO3_12, TYPO3_13, etc. — those are historical bundle identifiers for rules you apply while moving code toward a v14 codebase.
TYPO3 API First: Always use TYPO3's built-in APIs, core features, and established conventions before creating custom implementations. Always verify that the APIs and methods you use exist and are not deprecated in TYPO3 v14 by checking the official TYPO3 documentation.
Safety: Never run Fractor on production. Always run on a development environment with version control. Review and test changes before releasing.
| Tool | Handles | Package |
|---|---|---|
| Rector | PHP code (classes, TCA PHP, ext_localconf, etc.) | ssch/typo3-rector |
| Fractor | Non-PHP files (FlexForms, TypoScript, Fluid, YAML, Htaccess, composer.json) | a9f/typo3-fractor |
Use both together for complete TYPO3 upgrades. See the typo3-extension-upgrade skill for the full workflow.
composer require --dev a9f/typo3-fractor
# or with DDEV:
ddev composer require --dev a9f/typo3-fractor
Important: Like Rector, Fractor loads the project autoloader and requires a PHP version that can parse the installed TYPO3 packages. For TYPO3 v14 targets, run Fractor with PHP 8.2+. Use DDEV or a container if your local PHP is older.
Always run Fractor, never skip it. Non-PHP files (FlexForms, TypoScript, Fluid) contain version-specific patterns that are easy to miss in manual review. Fractor rules are maintained by the TYPO3 community and cover edge cases.
This meta-package installs all TYPO3-specific file processors:
a9f/fractor (core engine)a9f/fractor-xml (FlexForm/XML processing)a9f/fractor-fluid (Fluid template processing)a9f/fractor-typoscript (TypoScript/TSconfig processing)a9f/fractor-yaml (YAML processing)a9f/fractor-htaccess (Htaccess processing)a9f/fractor-composer-json (composer.json processing — not installed as a dependency of a9f/typo3-fractor; add composer require --dev a9f/fractor-composer-json if you need it)Generate a default config:
vendor/bin/typo3-fractor-init
Create fractor.php in your project root:
<?php
declare(strict_types=1);
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\Set\Typo3LevelSetList;
return FractorConfiguration::configure()
->withPaths([
__DIR__ . '/packages/',
])
->withSets([
Typo3LevelSetList::UP_TO_TYPO3_14,
]);
<?php
declare(strict_types=1);
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\Set\Typo3LevelSetList;
return FractorConfiguration::configure()
->withPaths([
__DIR__ . '/Configuration/',
__DIR__ . '/Resources/',
])
->withSets([
Typo3LevelSetList::UP_TO_TYPO3_14,
]);
<?php
declare(strict_types=1);
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\Set\Typo3LevelSetList;
use a9f\Typo3Fractor\TYPO3v12\FlexForm\MigrateRequiredFlagFlexFormFractor;
return FractorConfiguration::configure()
->withPaths([__DIR__ . '/packages/'])
->withSkip([
MigrateRequiredFlagFlexFormFractor::class,
__DIR__ . '/packages/legacy_ext/',
__DIR__ . '/packages/my_ext/Resources/Private/Templates/Old.html' => [
\a9f\Typo3Fractor\TYPO3v14\Fluid\ChangeLogoutHandlingInFeLoginFractor::class,
],
])
->withSets([
Typo3LevelSetList::UP_TO_TYPO3_14,
]);
<?php
declare(strict_types=1);
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\TYPO3v12\FlexForm\MigrateItemsIndexedKeysToAssociativeFractor;
return FractorConfiguration::configure()
->withPaths([__DIR__ . '/packages/'])
->withRules([
MigrateItemsIndexedKeysToAssociativeFractor::class,
]);
vendor/bin/fractor process --dry-run
vendor/bin/fractor process
vendor/bin/fractor process --only="a9f\Typo3Fractor\TYPO3v14\TypoScript\RemoveExternalOptionFromTypoScriptFractor"
ddev exec vendor/bin/fractor process --dry-run
ddev exec vendor/bin/fractor process
Apply all rules up to a target version:
| Set List | Includes |
|---|---|
Typo3LevelSetList::UP_TO_TYPO3_10 | Imports UP_TO_TYPO3_9 plus TYPO3_10 rules only (see package config/level/up-to-typo3-10.php) |
Typo3LevelSetList::UP_TO_TYPO3_11 | ... + v11 |
Typo3LevelSetList::UP_TO_TYPO3_12 | ... + v12 |
Typo3LevelSetList::UP_TO_TYPO3_13 | ... + v13 |
Typo3LevelSetList::UP_TO_TYPO3_14 | ... + v14 |
Apply rules for a single version only:
| Set List | Description |
|---|---|
Typo3SetList::TYPO3_12 | v12 rules only |
Typo3SetList::TYPO3_13 | v13 rules only |
Typo3SetList::TYPO3_14 | v14 rules only |
| Rule | Migration |
|---|---|
MigrateEmailFlagToEmailTypeFlexFormFractor | eval=email → type=email |
MigrateEvalIntAndDouble2ToTypeNumberFlexFormFractor | eval=int/double2 → type=number |
MigrateInternalTypeFolderToTypeFolderFlexFormFractor | internalType=folder → type=folder |
MigrateItemsIndexedKeysToAssociativeFractor | Indexed items → label/value keys |
MigrateNullFlagFlexFormFractor | eval=null → nullable=true |
MigratePasswordAndSaltedPasswordToPasswordTypeFlexFormFractor | Password eval → type=password |
MigrateRenderTypeColorpickerToTypeColorFlexFormFractor | renderType=colorPicker → type=color |
MigrateRequiredFlagFlexFormFractor | eval=required → required=1 |
MigrateTypeNoneColsToSizeFlexFormFractor | cols → size for type=none |
RemoveTceFormsDomElementFlexFormFractor | Remove <TCEforms> wrapper |
| Rule | Migration |
|---|---|
MigrateTypoScriptLoginUserAndUsergroupConditionsFractor | Legacy conditions → Symfony expressions |
MigrateDeprecatedTypoScriptConditionsFractor | Deprecated TypoScript [compatVersion] / old condition syntax → supported forms |
RemoveConfigDisablePageExternalUrlFractor | Remove config.disablePageExternalUrl |
RemoveConfigDoctypeSwitchFractor | Remove config.doctypeSwitch |
RemoveConfigMetaCharsetFractor | Remove config.metaCharset |
RemoveConfigSendCacheHeadersOnlyWhenLoginDeniedInBranchFractor | Remove deprecated cache header config |
RemoveConfigSpamProtectEmailAddressesAsciiOptionFractor | Remove ascii option |
RemoveNewContentElementWizardOptionsFractor | Remove legacy CE wizard TSconfig |
RemoveWorkspaceModeOptionsFractor | Remove workspace mode options |
RenameConfigXhtmlDoctypeToDoctypeFractor | xhtmlDoctype → doctype |
RenameTcemainLinkHandlerMailKeyFractor | Rename mail link handler key |
UseConfigArrayForTSFEPropertiesFractor | TSFE properties → config array |
| Rule | Migration |
|---|---|
AbstractMessageGetSeverityFluidFractor | Migrate severity constants in Fluid |
TYPO3_13)These rules are published under the historical set name Typo3SetList::TYPO3_13 (v13-targeted rules). They still run when you apply the cumulative Typo3LevelSetList::UP_TO_TYPO3_14 level set on a v14 codebase.
| Rule | Migration |
|---|---|
MigrateIncludeTypoScriptSyntaxFractor | <INCLUDE_TYPOSCRIPT: → @import |
RemovePageDoktypeRecyclerFromUserTsConfigFractor | Remove recycler doktype from TSconfig |
| Rule | Migration |
|---|---|
MigrateTypoScriptConditionGetTSFEFractor | getTSFE() conditions → new API |
MigrateTypoScriptGetDataPathFractor | getData path changes |
RemoveDuplicateDoktypeRestrictionConfigurationFractor | Remove duplicate doktype restrictions |
RemoveExposeNonexistentUserInForgotPasswordDialogSettingInFeLoginFractor | Remove deprecated fe_login setting |
RemoveExternalOptionFromTypoScriptFractor | Remove external option |
RemoveFrontendAssetConcatenationAndCompressionFractor | Remove asset concatenation/compression settings |
RemoveModWebLayoutDefLangBindingFractor | Remove mod.web_layout.defLangBinding |
RemoveUserTSConfigAuthBeRedirectToURLFractor | Remove auth.BE.redirectToURL |
| Rule | Migration |
|---|---|
ChangeLogoutHandlingInFeLoginFractor | Migrate fe_login logout handling |
| Rule | Migration |
|---|---|
MigrateLegacyFormTemplatesFractor | Migrate legacy EXT:form templates |
| Rule | Migration |
|---|---|
RemoveUploadsFromDefaultHtaccessFractor | Remove /uploads/ from .htaccess |
<?php
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\FractorTypoScript\Configuration\TypoScriptProcessorOption;
use Helmich\TypoScriptParser\Parser\Printer\PrettyPrinterConfiguration;
use Helmich\TypoScriptParser\Parser\Printer\PrettyPrinterConditionTermination;
return FractorConfiguration::configure()
->withPaths([__DIR__ . '/Configuration/'])
->withOptions([
TypoScriptProcessorOption::INDENT_SIZE => 2,
TypoScriptProcessorOption::INDENT_CHARACTER => PrettyPrinterConfiguration::INDENTATION_STYLE_SPACES,
TypoScriptProcessorOption::ADD_CLOSING_GLOBAL => false,
TypoScriptProcessorOption::INCLUDE_EMPTY_LINE_BREAKS => true,
TypoScriptProcessorOption::INDENT_CONDITIONS => true,
TypoScriptProcessorOption::CONDITION_TERMINATION => PrettyPrinterConditionTermination::Keep,
]);
Indent character options:
PrettyPrinterConfiguration::INDENTATION_STYLE_SPACES -- use spacesPrettyPrinterConfiguration::INDENTATION_STYLE_TABS -- use tabs'auto' -- detect from file and keep existingCondition termination options:
PrettyPrinterConditionTermination::Keep -- keep existingPrettyPrinterConditionTermination::EnforceGlobal -- always [global]PrettyPrinterConditionTermination::EnforceEnd -- always [end]<?php
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\FractorXml\Configuration\XmlProcessorOption;
use a9f\Fractor\ValueObject\Indent;
return FractorConfiguration::configure()
->withPaths([__DIR__ . '/Configuration/'])
->withOptions([
XmlProcessorOption::INDENT_CHARACTER => Indent::STYLE_TAB,
XmlProcessorOption::INDENT_SIZE => 1,
]);
Control which file extensions each processor handles:
<?php
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\FractorFluid\Configuration\FluidProcessorOption;
use a9f\FractorTypoScript\Configuration\TypoScriptProcessorOption;
use a9f\FractorXml\Configuration\XmlProcessorOption;
use a9f\FractorYaml\Configuration\YamlProcessorOption;
return FractorConfiguration::configure()
->withPaths([__DIR__ . '/packages/'])
->withOptions([
FluidProcessorOption::ALLOWED_FILE_EXTENSIONS => ['html'],
TypoScriptProcessorOption::ALLOWED_FILE_EXTENSIONS => ['typoscript', 'tsconfig'],
XmlProcessorOption::ALLOWED_FILE_EXTENSIONS => ['xml', 'xlf'],
YamlProcessorOption::ALLOWED_FILE_EXTENSIONS => ['yaml', 'yml'],
]);
Default extensions:
html, xml, txttyposcript, tsconfig, tsxmlyaml, ymlExtend AbstractXmlFractor (in package a9f/fractor-xml). XmlFractor in a9f\FractorXml\Contract is the interface; the abstract base provides the DOM visitor wiring.
<?php
declare(strict_types=1);
namespace Vendor\MyExtension\Fractor;
use a9f\FractorXml\AbstractXmlFractor;
use DOMNode;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class MyCustomFlexFormFractor extends AbstractXmlFractor
{
public function canHandle(\DOMNode $node): bool
{
return $node->nodeName === 'config'
&& $node->parentNode instanceof \DOMElement;
}
public function refactor(DOMNode $node): DOMNode|int|null
{
// Modify the DOM node
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Migrate custom FlexForm configuration', []);
}
}
<?php
// fractor.php
use a9f\Fractor\Configuration\FractorConfiguration;
use Vendor\MyExtension\Fractor\MyCustomFlexFormFractor;
return FractorConfiguration::configure()
->withPaths([__DIR__ . '/packages/'])
->withRules([
MyCustomFlexFormFractor::class,
]);
1. git checkout -b feature/typo3-upgrade
2. Run Rector (PHP migrations) → vendor/bin/rector process
3. Run Fractor (non-PHP migrations) → vendor/bin/fractor process
4. Run PHP-CS-Fixer (code style) → vendor/bin/php-cs-fixer fix
5. Run PHPStan (static analysis) → vendor/bin/phpstan analyse
6. Run Tests → vendor/bin/phpunit
7. Manual testing in target version
<?php
declare(strict_types=1);
use a9f\Fractor\Configuration\FractorConfiguration;
use a9f\Typo3Fractor\Set\Typo3LevelSetList;
use a9f\FractorTypoScript\Configuration\TypoScriptProcessorOption;
use Helmich\TypoScriptParser\Parser\Printer\PrettyPrinterConfiguration;
return FractorConfiguration::configure()
->withPaths([
__DIR__ . '/Configuration/',
__DIR__ . '/Resources/',
])
->withSkip([
__DIR__ . '/Resources/Public/',
])
->withSets([
Typo3LevelSetList::UP_TO_TYPO3_14,
])
->withOptions([
TypoScriptProcessorOption::INDENT_SIZE => 4,
TypoScriptProcessorOption::INDENT_CHARACTER => PrettyPrinterConfiguration::INDENTATION_STYLE_SPACES,
]);
# Check for syntax errors in TypoScript first
# Fractor uses helmich/typo3-typoscript-parser which requires valid TypoScript
# Skip problematic files
vendor/bin/fractor process --dry-run
# Then add them to withSkip() in fractor.php
# Ensure XML files are in the configured paths
# Check ALLOWED_FILE_EXTENSIONS includes 'xml'
# Verify the FlexForm has the expected structure (<T3DataStructure>)
# Configure code style explicitly in fractor.php
# Use 'auto' indent detection to preserve existing formatting
# Review withOptions() settings
# .github/workflows/fractor.yml
- name: Check Fractor
run: vendor/bin/fractor process --dry-run
# Fails if any rules would apply = outdated code
For Fractor resource links and TYPO3 v14-specific migration notes, see references/resources-and-v14.md.
Thanks to Andreas Wolf for creating and maintaining Fractor.