typo3/fluid-schema-generator
Generates XSD schemas for TYPO3 Fluid ViewHelpers, enabling IDE autocompletion, validation, and better template editing for custom and core ViewHelpers.
This is a TYPO3/Fluid package, not a Laravel one — treat it as a case study in reflection-driven IDE metadata rather than something you composer require into an app/. If you are working in a TYPO3 extension or evaluating the generator as a pattern for Blade tooling, here is the minimal path.
Install into the TYPO3 project that owns the ViewHelpers, not the package you are writing templates for:
composer require --dev typo3/fluid-schema-generator
It is archived. On TYPO3 v10+ the same functionality ships inside typo3/cms-fluid as bin/typo3 fluid:schema:generate. Only reach for the standalone package when you are pinned to an older core or need to fork the behaviour.
Look first at the Generator class and the FluidSchemaGenerator\Command\... entry point — that is where package scanning, file writing, and the XSD output directory are wired. Everything else is type-mapping detail.
You need a bootstrapped TYPO3 instance (PackageManager, autoloader, DI container) before generating. A minimal CLI script looks like:
$generator = new \TYPO3\FluidSchemaGenerator\Generator();
$generator->generateXsdForPackage('vendor/my_extension', __DIR__ . '/Schemas');
Register the resulting .xsd in PhpStorm under Settings → Languages & Frameworks → Schemas and DTDs → External Schemas, or drop a schemas.xml catalog into the project. Templates using xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" now get attribute autocompletion and validation.
First use case: you have shipped a custom ViewHelper (<my:formatMoney amount="{total}" />), template authors keep guessing argument names, and you want the IDE to tell them currency is required. Generate the schema, commit the .xsd, and the guesswork disappears.
Generate, don't hand-write. The whole value is that the XSD is derived from @param/@return docblocks and reflection on the ViewHelper class. The moment you edit the .xsd by hand you have a schema that silently disagrees with the code.
Wire it into the build, not into muscle memory. A composer script or Makefile target that runs after composer dump-autoload:
"scripts": {
"schema:generate": "@php bin/generate-fluid-schema.php",
"post-autoload-dump": ["@schema:generate"]
}
Run it after autoload regeneration — the generator walks class files and reflects them, so a stale autoloader means missing ViewHelpers in the XSD.
One schema per package, namespaced by target namespace. The generator emits a schema whose targetNamespace matches the Fluid namespace URI of the package (http://typo3.org/ns/Vendor/Extension/ViewHelpers). Keep the output directory aligned with that (Resources/Private/Schemas/Vendor.Extension.xsd) so the mapping is obvious to whoever configures the IDE.
CI as the enforcement point. Regenerate in a pipeline step and git diff --exit-code against the committed .xsd. If a developer added a ViewHelper argument but didn't regenerate, the build fails. This is the single highest-value integration tip: without it, schemas rot within a sprint.
Custom ViewHelpers need care with the base class. The generator discovers ViewHelpers by inheritance from AbstractViewHelper / ViewHelperInterface. If yours extends an intermediate base class that declares @param annotations, ensure the annotations land where reflection can see them — inherited docblocks are not always merged the way you expect.
Use it as a reference implementation for Blade. The pattern — reflect over classes, read docblock annotations, emit an IDE-consumable artifact — is exactly what barryvdh/laravel-ide-helper does for facades. If you want Blade component autocompletion, the generator's type-mapping table (PHP type → xs:string / xs:boolean / xs:anyType) is the part worth copying.
It is archived — check whether core already does this. On TYPO3 10+ the command lives in the core. Installing the standalone package alongside a modern core gives you two generators, version drift, and confusing output. Verify which one your project actually ships before debugging anything.
XSD is a blunt type system. PHP mixed, unions, nullable types, arrays of objects, and \DateTimeInterface all collapse to something loose (xs:anyType, xs:string, or nothing). Do not expect the schema to catch a genuine type error — it catches typos in argument names and missing required arguments. That is the entire contract; calibrate expectations accordingly.
Boolean defaulting is a classic trap. Fluid's boolean handling for ViewHelper arguments (true/false vs 1/0 vs empty) does not map cleanly onto xs:boolean. Expect false positives in the IDE where a perfectly valid template gets flagged.
Namespace URI mismatch fails silently. If the package's declared Fluid namespace URI does not byte-for-byte match the xmlns:my= in templates, the schema simply never activates and the IDE gives no error — just no autocompletion. Compare the strings character by character; trailing slashes bite.
IDE caching. PhpStorm caches XSDs aggressively. After regenerating, you often need File → Invalidate Caches or a project reopen before changes appear. Budget for this the first time, and don't chase phantom bugs.
Commit or ignore — decide deliberately. Committing the .xsd gives teammates autocompletion without running the generator (good for onboarding) but produces noisy diffs. Gitignoring keeps history clean but means every new clone must run bootstrap. For a small team, commit it; for a large one, generate in CI and publish as an artifact.
Bootstrap friction is the real cost. The generator needs a working TYPO3 container. In a monorepo or a CI image without a configured TYPO3_CONTEXT and DB, it will fail on application bootstrap long before it touches a single ViewHelper. Keep a dedicated minimal bootstrap script that only registers the PackageManager.
Extension points are thin. There is no rich plugin API for custom type mapping — you fork and edit the type conversion method, or you accept the output. If you need finer control (e.g. emitting xs:enumeration for a string constant set), the fork is small but permanent. Weigh that against just documenting the argument in the ViewHelper's docblock.
Performance on large packages. Reflection over every ViewHelper class in a big extension is measurable but not painful; the bottleneck is usually filesystem walking. If you are generating for many packages, cache the output keyed on a hash of the source tree rather than regenerating unconditionally on every request.
How can I help you explore Laravel packages today?