Feature: #108763 - Console command to analyze Fluid templates 

See forge#108763

Description 

The fluid:analyze console command is introduced, which analyses Fluid templates in the current project for correct Fluid syntax and alerts about 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's 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 has a return status of 1 (error), otherwise it returns 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 won't make the pipeline fail.

Verbose output allows to get feedback of analyzed templates and the number of errors/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 tooling.

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 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 also be used to deprecate a ViewHelper's argument. The deprecation will only be 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 fluid:analyze console command can be used to check basic validity of Fluid templates in a project that use the *.fluid.* file extension and can discover deprecated functionality used in template files.