Feature: #108763 - Console command to analyze Fluid templates 

See forge#108763

Description 

The typo3 fluid:analyze console command is introduced, which analyzes Fluid templates in the current project for correct Fluid syntax and reports deprecations that are emitted during template parsing.

Usage:

vendor/bin/typo3 fluid:analyze
Copied!

Example output:

[DEPRECATION] packages/myext/Resources/Private/Templates/Test.fluid.html: <my:obsolete> has been deprecated in X and will be removed in Y.
[ERROR] packages/myext/Resources/Private/Templates/Test2.fluid.html: Variable identifiers cannot start with a "_": _temp
Copied!

In its initial implementation, the command automatically finds all Fluid templates within the current project based on the *.fluid.* file extension (see Feature: #108166 - Fluid file extension and template resolving) and analyzes them. By default, TYPO3 system extensions are skipped. This can be adjusted by specifying the --include-system-extensions CLI option.

The following errors and deprecations are currently supported:

If exceptions are caught during the parsing process of at least one template, the console command will have a return status of 1 (error). Otherwise, it will return 0 (success). This means that deprecations are not interpreted as errors.

This should make it possible to use the command in CI workflows of most projects, since deprecated functionality used by third-party templates will not make the pipeline fail.

Verbose output allows users to get feedback on the analyzed templates and the number of errors and deprecations, or success.

Integration with other tools 

The command also supports input of a template string via STDIN as well as machine-readable output as JSON. This enables better integration with other development-related tools.

Usage:

echo "<formvh:form.timePicker /> {_invalidVariable}" | vendor/bin/typo3 fluid:analyze --stdin --json
Copied!

Example output (formatted):

{
    "identifier": "template__5adb1a7702b9dcbf",
    "path": "php:\/\/stdin",
    "errors": [
        {
            "file": "\/var\/www\/html\/vendor\/typo3fluid\/fluid\/src\/Core\/Parser\/TemplateParser.php",
            "line": 130,
            "message": "Fluid parse error in template php:\/\/stdin, line 2 at character 27. Error: Variable identifiers cannot start with a \"_\": _invalidVariable (error code 1765900762). Template source chunk:    {_invalidVariable}\n",
            "templateLocation": {
                "identifierOrPath": "php:\/\/stdin",
                "line": 2,
                "character": 27
            }
        }
    ],
    "deprecations": [
        {
            "file": "\/var\/www\/html\/typo3\/sysext\/form\/Classes\/ViewHelpers\/Form\/TimePickerViewHelper.php",
            "line": 143,
            "message": "The TimePickerViewHelper is deprecated since TYPO3 v14 and will be removed in v15."
        }
    ]
}
Copied!

Deprecating ViewHelpers 

The fluid:analyze console command can catch deprecations of whole ViewHelpers if the deprecation is emitted during the parse time of a template. This is possible by implementing the \ViewHelperNodeInitializedEventInterface :

ObsoleteViewHelper.php
use TYPO3Fluid\Fluid\Core\Parser\ParsingState;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;

/**
 * @deprecated since X, will be removed in Y.
 */
final class ObsoleteViewHelper extends AbstractViewHelper implements ViewHelperNodeInitializedEventInterface
{
    // ...

    public static function nodeInitializedEvent(ViewHelperNode $node, array $arguments, ParsingState $parsingState): void
    {
        trigger_error(
            '<my:obsolete> has been deprecated in X and will be removed in Y.',
            E_USER_DEPRECATED,
        );
    }
}
Copied!

Deprecating ViewHelper arguments 

The \ViewHelperNodeInitializedEventInterface can be used to deprecate a ViewHelper argument. The deprecation is only triggered if the argument is actually used in a template.

SomeViewHelper.php
use TYPO3Fluid\Fluid\Core\Parser\ParsingState;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\ViewHelperNodeInitializedEventInterface;

final class SomeViewHelper extends AbstractViewHelper implements ViewHelperNodeInitializedEventInterface
{
    public function initializeArguments(): void
    {
        // @deprecated since X, will be removed in Y.
        $this->registerArgument('obsoleteArgument', 'string', 'Original description. Deprecated since X, will be removed in Y');
    }

    public static function nodeInitializedEvent(ViewHelperNode $node, array $arguments, ParsingState $parsingState): void
    {
        if (array_key_exists('obsoleteArgument', $arguments)) {
            trigger_error(
                'ViewHelper argument "obsoleteArgument" in <my:some> is deprecated since X and will be removed in Y.',
                E_USER_DEPRECATED,
            );
        }
    }
}
Copied!

Impact 

The new typo3 fluid:analyze console command can be used to check basic validity of Fluid templates in projects that use the *.fluid.* file extension and to discover deprecated functionality in template files.