fluidstan / typo3
TYPO3 integration for FluidStan: boots TYPO3, registers its Fluid namespaces and resolves EXT: paths
Requires
- php: ^8.2
- fluidstan/fluidstan: ^0.3
- psr/container: ^1.1 || ^2.0
- typo3/cms-core: ^13.4 || ^14.2
- typo3/cms-fluid: ^13.4 || ^14.2
- typo3fluid/fluid: ^4.4 || ^5.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.47
- fluidstan-example/shop: @dev
- friendsofphp/php-cs-fixer: ^3.75
- php-parallel-lint/php-parallel-lint: ^1.4
- phpstan/phpstan: ^2.1.30
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.5
- rector/rector: ^2.0
- saschaegerer/phpstan-typo3: ^2.2 || ^3.1
- shipmonk/composer-dependency-analyser: ^1.8
- typo3/minimal: ^13.4 || ^14.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-29 22:27:44 UTC
README
TYPO3 integration for FluidStan, the static analyser for Fluid templates and components powered by PHPStan. Developed and maintained by Flowd GmbH.
FluidStan analyses Fluid templates without rendering them and reports property paths that do not exist
on the model, values that cannot be rendered, loops over non-iterables, wrong ViewHelper and component
argument types, undefined variables, unknown slots and more. The screenshot shows the analysis of
docs/example, a small shop extension with an Extbase model, a component collection
class and a collection configured in Configuration/Fluid/ComponentCollections.php (TYPO3 14): a typo
in a property path, a DateTimeImmutable rendered without formatting, a file reference passed where a
number is expected, a float passed to the arguments of f:translate, a missing getter on a related
category read through an ObjectStorage, an undefined variable, an ObjectStorage passed to the count
argument of the configured collection's component, and a property read on that int inside it. Run it
yourself with composer fluidstan:example.
This package makes FluidStan work inside a TYPO3 project:
- Boots TYPO3 like a CLI command, without opening a database connection, so the dependency injection container, extension paths and configuration are available while templates are parsed.
- Registers TYPO3's global Fluid namespaces exactly as TYPO3 assembles them:
fincludesTYPO3\CMS\Fluid\ViewHelpers, and everyConfiguration/Fluid/Namespaces.php(TYPO3 14),$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']and theModifyNamespacesEventare honoured. - Knows TYPO3 14's component collections defined in
Configuration/Fluid/ComponentCollections.php, without a PHP class, and collection classes registered through dependency injection. Their variables come from theProvideStaticVariablesToComponentEventat runtime and aremixedin the analysis. - Resolves
EXT:paths in the configuration, so templates are addressed the TYPO3 way.
Supports TYPO3 13.4 and 14.2 or newer on PHP 8.2 or newer, in Composer mode.
Installation
composer require --dev fluidstan/typo3
The core package comes along as a dependency.
Configuration
fluidstan.neon in the project root, next to composer.json:
parameters: environment: FluidStan\TYPO3\Typo3Environment bootstrapFiles: - vendor/autoload.php paths: - EXT:my_site/Resources/Private/Components - EXT:my_site/Resources/Private/Templates namespaces: my: My\Site\Components\ComponentCollection tmpDir: var/cache/fluidstan
environmentactivates this package.bootstrapFilesmust include the project's autoloader; TYPO3 is booted from it.pathstakeEXT:notation or paths relative to the configuration file.namespaceslists component collections and ViewHelper namespaces that are not registered globally in TYPO3, for example a collection a TYPO3 13 project registers only inext_localconf.php. A component collection is given by its class name. Globally registered namespaces need no entry.tmpDirholds the generated code and PHPStan's cache; keep it out of version control.
Then run the analysis:
vendor/bin/fluidstan vendor/bin/fluidstan -c Build/fluidstan.neon # configuration elsewhere vendor/bin/fluidstan --error-format=json # for CI tooling
Run it where TYPO3 runs. In a Docker or DDEV setup that is the PHP container, because the boot reads
config/system/settings.php and additional.php, loads every extension's ext_localconf.php and builds
the dependency injection container like any other TYPO3 CLI command. No database connection is opened.
Without a settings.php TYPO3 only offers its failsafe mode, which knows the system extensions alone, so
FluidStan stops with a message; a minimal settings.php with SYS/encryptionKey is enough.
What gets typed
-
Components declare their argument types with
f:argument; FluidStan checks every use against the PHP classes behind them, including your Extbase models, DTOs and value objects. -
Plain templates, partials and layouts have
mixedvariables unless you declare them with an annotation, which is also the way to type{settings},{data}or anything else a controller assigns:<f:comment> @var My\Site\Domain\Model\Product product @var array<string, mixed> data </f:comment>
-
ViewHelper arguments are validated against the ViewHelper's argument definitions with the rules Fluid 5 applies at runtime. ViewHelpers that take services through their constructor are instantiated without the constructor to read their definitions, which is sufficient for the core and most extensions.
-
Variables a collection class provides through
getAdditionalVariables()are typed from that method without running it, so a{component.class}your collection builds is astringand an unknown key is reported; see the core README for the details. Collections fromComponentCollections.phpget theirs from a PSR-14 event at runtime, which the analysis cannot see, so those aremixed. -
Conditions narrow: inside
<f:if condition="{product}">an optionalproductis no longernull. -
Variables ViewHelpers assign are detected, in your ViewHelpers and in TYPO3's: inside
<f:link.typolink parameter="{link}" partsAs="parts">,{parts}is a knownarray. Where detection cannot follow the code, declare the assignment influidstan.neonunderviewHelperAssignmentsor with a@fluidstan-assignstag; the core README has the details. -
Strict mode (
strict: true) also reports what Fluid tolerates silently, such as a string passed to anintargument or a path through a nullable Extbase relation; see the core README. -
A baseline lets an existing project adopt FluidStan:
vendor/bin/fluidstan --generate-baselinerecords the current errors influidstan-baseline.neon, and withincludes: [fluidstan-baseline.neon]influidstan.neononly new ones are reported.ignoreErrorsworks as in PHPStan.
Roadmap
- Typed TYPO3 variables out of the box:
{settings},{data},{page},{site},{language},{request} - Content Blocks field definitions as the type of
{data}and of content element component arguments - Partials and layouts resolved through TYPO3's template paths from site sets and TypoScript
- Extbase relations:
ObjectStorageand lazy-loading proxies unwrapped to their element types
Development
composer install # installs TYPO3 (typo3/minimal) and the example extension for the test suite composer fluidstan:example # analyse docs/example composer check # everything below composer lint # syntax, composer.json, php-cs-fixer, PHPStan (max, strict, phpstan-typo3), Rector, dependency analysis composer fix # apply Rector, php-cs-fixer and composer normalize composer test # PHPUnit
Booting TYPO3 from the outside goes through SystemEnvironmentBuilder, which TYPO3 marks as internal;
the same route typo3-console and the testing framework take. The package root is a minimal TYPO3 project
with config/system/settings.php, so the example can be analysed and the integration test boots TYPO3
for real. CI runs the suite on PHP 8.2 to 8.5 against TYPO3 13.4 and 14.2.