diff --git a/composer.json b/composer.json index de721383618f89c74cfa877df2c2a9492901ff16..6c214b9a19b8b257170eab8639d975a750be8ce2 100644 --- a/composer.json +++ b/composer.json @@ -291,7 +291,8 @@ "TYPO3\\CMS\\Recycler\\Tests\\": "typo3/sysext/recycler/Tests/", "TYPO3\\CMS\\T3editor\\Tests\\": "typo3/sysext/t3editor/Tests/", "TYPO3\\CMS\\Tstemplate\\Tests\\": "typo3/sysext/tstemplate/Tests/", - "TYPO3Tests\\TestLogger\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_logger/Classes/" + "TYPO3Tests\\TestLogger\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_logger/Classes/", + "TYPO3Tests\\TestTyposcriptAstFunctionEvent\\": "typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Classes/" }, "classmap": [ "typo3/sysext/core/Tests/Unit/Core/Fixtures/test_extension/", diff --git a/typo3/sysext/core/Classes/TypoScript/AST/AstBuilder.php b/typo3/sysext/core/Classes/TypoScript/AST/AstBuilder.php index 66ca8e88fe034690e55d45f039ea458ff96e97b5..56b380d07359d2d40e387220ea7ad7467293eba1 100644 --- a/typo3/sysext/core/Classes/TypoScript/AST/AstBuilder.php +++ b/typo3/sysext/core/Classes/TypoScript/AST/AstBuilder.php @@ -17,8 +17,10 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\TypoScript\AST; +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Core\TypoScript\AST\CurrentObjectPath\CurrentObjectPath; use TYPO3\CMS\Core\TypoScript\AST\CurrentObjectPath\CurrentObjectPathStack; +use TYPO3\CMS\Core\TypoScript\AST\Event\EvaluateModifierFunctionEvent; use TYPO3\CMS\Core\TypoScript\AST\Node\ChildNode; use TYPO3\CMS\Core\TypoScript\AST\Node\ChildNodeInterface; use TYPO3\CMS\Core\TypoScript\AST\Node\NodeInterface; @@ -54,6 +56,11 @@ final class AstBuilder */ private array $flatConstants = []; + public function __construct( + private readonly EventDispatcherInterface $eventDispatcher, + ) { + } + /** * @param array<string, string> $flatConstants */ @@ -237,42 +244,43 @@ final class AstBuilder * Evaluate operator functions, example TypoScript: * "page.10.value := appendString(foo)" */ - private function evaluateValueModifier(Token $functionNameToken, ?Token $functionValueToken, ?string $currentValue): ?string + private function evaluateValueModifier(Token $functionNameToken, ?Token $functionArgumentToken, ?string $originalValue): ?string { - $functionValue = ''; - if ($functionValueToken) { - $functionValue = $functionValueToken->getValue(); + $functionName = $functionNameToken->getValue(); + $functionArgument = null; + if ($functionArgumentToken) { + $functionArgument = $functionArgumentToken->getValue(); } - switch ($functionNameToken->getValue()) { + switch ($functionName) { case 'prependString': - return $functionValue . $currentValue; + return $functionArgument . $originalValue; case 'appendString': - return $currentValue . $functionValue; + return $originalValue . $functionArgument; case 'removeString': - return str_replace($functionValue, '', $currentValue); + return str_replace((string)$functionArgument, '', $originalValue); case 'replaceString': - $functionValueArray = explode('|', $functionValue, 2); + $functionValueArray = explode('|', (string)$functionArgument, 2); $fromStr = $functionValueArray[0] ?? ''; $toStr = $functionValueArray[1] ?? ''; - return str_replace($fromStr, $toStr, $currentValue); + return str_replace($fromStr, $toStr, $originalValue); case 'addToList': - return ($currentValue !== null ? $currentValue . ',' : '') . $functionValue; + return ($originalValue !== null ? $originalValue . ',' : '') . $functionArgument; case 'removeFromList': - $existingElements = GeneralUtility::trimExplode(',', $currentValue); - $removeElements = GeneralUtility::trimExplode(',', $functionValue); + $existingElements = GeneralUtility::trimExplode(',', $originalValue); + $removeElements = GeneralUtility::trimExplode(',', (string)$functionArgument); if (!empty($removeElements)) { return implode(',', array_diff($existingElements, $removeElements)); } - return $currentValue; + return $originalValue; case 'uniqueList': - $elements = GeneralUtility::trimExplode(',', $currentValue); + $elements = GeneralUtility::trimExplode(',', $originalValue); return implode(',', array_unique($elements)); case 'reverseList': - $elements = GeneralUtility::trimExplode(',', $currentValue); + $elements = GeneralUtility::trimExplode(',', $originalValue); return implode(',', array_reverse($elements)); case 'sortList': - $elements = GeneralUtility::trimExplode(',', $currentValue); - $arguments = GeneralUtility::trimExplode(',', $functionValue); + $elements = GeneralUtility::trimExplode(',', $originalValue); + $arguments = GeneralUtility::trimExplode(',', (string)$functionArgument); $arguments = array_map('strtolower', $arguments); $sortFlags = SORT_REGULAR; if (in_array('numeric', $arguments)) { @@ -284,7 +292,7 @@ final class AstBuilder foreach ($elements as $element) { if (!is_numeric($element)) { throw new \InvalidArgumentException( - 'The list "' . $currentValue . '" should be sorted numerically but contains a non-numeric value', + 'The list "' . $originalValue . '" should be sorted numerically but contains a non-numeric value', 1650893781 ); } @@ -296,26 +304,13 @@ final class AstBuilder } return implode(',', $elements); case 'getEnv': - $environmentValue = getenv(trim($functionValue)); + $environmentValue = getenv(trim((string)$functionArgument)); if ($environmentValue !== false) { return $environmentValue; } - return $currentValue; + return $originalValue; default: - return $currentValue; - // @todo: Implement (and test) hook again or switch to event along the way - /* - if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$modifierName])) { - $hookMethod = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc'][$modifierName]; - $params = ['currentValue' => $currentValue, 'functionArgument' => $modifierArgument]; - $fakeThis = null; - $newValue = GeneralUtility::callUserFunction($hookMethod, $params, $fakeThis); - } else { - self::getLogger()->warning('Missing function definition for {modifier_name} on TypoScript', [ - 'modifier_name' => $modifierName, - ]); - } - */ + return $this->eventDispatcher->dispatch(new EvaluateModifierFunctionEvent($functionName, $functionArgument, $originalValue))->getValue() ?? $originalValue; } } } diff --git a/typo3/sysext/core/Classes/TypoScript/AST/Event/EvaluateModifierFunctionEvent.php b/typo3/sysext/core/Classes/TypoScript/AST/Event/EvaluateModifierFunctionEvent.php new file mode 100644 index 0000000000000000000000000000000000000000..7d68d7973b88c3f25522c20408aa2f8b261f4807 --- /dev/null +++ b/typo3/sysext/core/Classes/TypoScript/AST/Event/EvaluateModifierFunctionEvent.php @@ -0,0 +1,85 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\TypoScript\AST\Event; + +/** + * Listeners to this event are able to implement own ":=" TypoScript modifier functions, example: + * + * foo = myOriginalValue + * foo := myNewFunction(myFunctionArgument) + * + * Listeners should take care function names can not overlap with function names + * from other extensions and should thus namespace, example naming: "extNewsSortFunction()" + */ +final class EvaluateModifierFunctionEvent +{ + private ?string $value = null; + + public function __construct( + private readonly string $functionName, + private readonly ?string $functionArgument, + private readonly ?string $originalValue, + ) { + } + + /** + * The function name, for example "extNewsSortFunction" when using "foo := extNewsSortFunction()" + */ + public function getFunctionName(): string + { + return $this->functionName; + } + + /** + * Optional function argument, for example "myArgument" when using "foo := extNewsSortFunction(myArgument)" + */ + public function getFunctionArgument(): ?string + { + return $this->functionArgument; + } + + /** + * Original / current value, for example "fooValue" when using: + * foo = fooValue + * foo := extNewsSortFunction(myArgument) + */ + public function getOriginalValue(): ?string + { + return $this->originalValue; + } + + /** + * Set the updated value calculated by a listener. + * Note you can not set to null to "unset", since getValue() falls back to + * originalValue in this case. Set to empty string instead for this edge case. + */ + public function setValue(string $value): void + { + $this->value = $value; + } + + /** + * Used by AstBuilder to fetch the updated value, falls back to given original value. + * Can be used by Listeners to see if a previous listener changed the value already + * by comparing with getOriginalValue(). + */ + public function getValue(): ?string + { + return $this->value; + } +} diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-98016-RemovedTypoScriptFunctionHook.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-98016-RemovedTypoScriptFunctionHook.rst new file mode 100644 index 0000000000000000000000000000000000000000..b447f1fb5c3df0517222d77bbf04080c2f3301b5 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-98016-RemovedTypoScriptFunctionHook.rst @@ -0,0 +1,49 @@ +.. include:: /Includes.rst.txt + +.. _breaking-98016-1658731955: + +================================================ +Breaking: #98016 - RemovedTypoScriptFunctionHook +================================================ + +See :issue:`98016` + +Description +=========== + +With the transition to the :ref:`new TypoScript parser <feature-97816-1656350667>`, +the hook :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc']` +is no longer called. + +This hook has been used to implement own functions for the TypoScript "function" operator :typoscript:`:=`. + +Additional functions can now be implemented using the :php:`\TYPO3\CMS\Core\TypoScript\AST\Event\EvaluateModifierFunctionEvent` +as described in :ref:`this Changelog <feature-98016-1658732423>` + +Impact +====== + +With the continued implementation of the new TypoScript parser in TYPO3 v12, +registered hook implementations are not executed anymore. The extension scanner +will report possible usages. + + +Affected installations +====================== + +Extensions registering own TypoScript function implementations like this: + +.. code-block:: typoscript + + myValue := myCustomFunction(modifierArgument) + + +Migration +========= + +Implement the :ref:`new event <feature-98016-1658732423>`. Extensions that want to keep +compatibility with both TYPO3 v11 and v12 can keep the old hook implementation without +further deprecations. + + +.. index:: PHP-API, TSConfig, TypoScript, FullyScanned, ext:core diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Feature-98016-PSR-14EvaluateModifierFunctionEvent.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-98016-PSR-14EvaluateModifierFunctionEvent.rst new file mode 100644 index 0000000000000000000000000000000000000000..ef594d2d86efdfa04863253f2786d995db3aac7e --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-98016-PSR-14EvaluateModifierFunctionEvent.rst @@ -0,0 +1,65 @@ +.. include:: /Includes.rst.txt + +.. _feature-98016-1658732423: + +====================================================== +Feature: #98016 - PSR-14 EvaluateModifierFunctionEvent +====================================================== + +See :issue:`98016` + +Description +=========== + +A new PSR-14 Event :php:`\TYPO3\CMS\Core\TypoScript\AST\Event\EvaluateModifierFunctionEvent` +has been introduced which allows own TypoScript functions using the :typoscript:`:=` operator. + +This is a substitution of the old :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsparser.php']['preParseFunc']` +hook as described in :ref:`this Changelog <breaking-98016-1658731955>`. + + +Impact +====== + +The TYPO3 core tests come with test extension +:file:`EXT:core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event` to functional +test the new event. The extension implements an example listener that can be used as boilerplate. + +A simple TypoScript example looks like this: + +.. code-block:: typoscript + + someIdentifier = originalValue + someIdentifier := myModifierFunction(myFunctionArgument) + +To implement :typoscript:`myModifierFunction`, an extension needs to register an event listener +in file :file:`Configuration/Services.yaml`: + +.. code-block:: yaml + + MyVendor\MyPackage\EventListener\MyTypoScriptModifierFunction: + tags: + - name: event.listener + identifier: 'my-package/typoscript/evaluate-modifier-function' + +The corresponding event listener class could look like this: + +.. code-block:: php + + use TYPO3\CMS\Core\TypoScript\AST\Event\EvaluateModifierFunctionEvent; + + final class MyTypoScriptModifierFunction + { + public function __invoke(EvaluateModifierFunctionEvent $event): void + { + if ($event->getFunctionName() === 'myModifierFunction') { + $originalValue = $event->getOriginalValue(); + $functionArgument = $event->getFunctionArgument(); + // Manipulate values and set new value + $event->setValue($originalValue . ' example ' . $functionArgument); + } + } + } + + +.. index:: PHP-API, TSConfig, TypoScript, ext:core diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Classes/EventListener/TyposcriptTestFunction.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Classes/EventListener/TyposcriptTestFunction.php new file mode 100644 index 0000000000000000000000000000000000000000..1a909310ac8a5ceef083634c54175b58d3af027b --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Classes/EventListener/TyposcriptTestFunction.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3Tests\TestTyposcriptAstFunctionEvent\EventListener; + +use TYPO3\CMS\Core\TypoScript\AST\Event\EvaluateModifierFunctionEvent; + +final class TyposcriptTestFunction +{ + public function __invoke(EvaluateModifierFunctionEvent $event): void + { + if ($event->getFunctionName() === 'testFunction') { + $event->setValue(($event->getOriginalValue() ?? '') . ' ' . ($event->getFunctionArgument() ?? '')); + } + } +} diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Configuration/Services.yaml b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Configuration/Services.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ab2da15bda8ceac6f1e95decc7d01d7c6573676a --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Configuration/Services.yaml @@ -0,0 +1,10 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + TYPO3Tests\TestTyposcriptAstFunctionEvent\EventListener\TyposcriptTestFunction: + tags: + - name: event.listener + identifier: 'typo3tests-test-typoscript-ast-function-event/typoscript-test-function' diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Resources/Public/Icons/Extension.svg b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Resources/Public/Icons/Extension.svg new file mode 100644 index 0000000000000000000000000000000000000000..bca7a17509c5f3e42481725c025ec1b8f597fa56 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/Resources/Public/Icons/Extension.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="#FF8700" d="M0 0h64v64H0z"/><path fill="#FFF" d="M42.8 32.8c-3.6 0-8.1-10.1-8.1-15.1 0-2.3.9-2.7 3.2-2.7 5.5 0 11 .9 11 4-.1 6.2-4 13.8-6.1 13.8zM28.5 18.5c0 5 6.4 20.2 10.7 20.2.5 0 .9-.1 1.4-.2-3.8 6.1-8.4 10.6-11.2 10.6-5.9 0-14.3-17.9-14.3-25.7 0-1.2.3-2.2.7-2.8 2-2.5 8.4-4.4 13.7-5-.6.4-1 1-1 2.9z"/></svg> \ No newline at end of file diff --git a/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/ext_emconf.php b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/ext_emconf.php new file mode 100644 index 0000000000000000000000000000000000000000..8fc6f6e0b913f61ad5dfa4df2706ae2fa8059a19 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event/ext_emconf.php @@ -0,0 +1,22 @@ +<?php + +declare(strict_types=1); + +$EM_CONF[$_EXTKEY] = [ + 'title' => 'TypoScript AST function evaluation test', + 'description' => 'TypoScript AST function evaluation test', + 'category' => 'example', + 'version' => '12.0.0', + 'state' => 'beta', + 'author' => 'Christian Kuhn', + 'author_email' => 'lolli@schwarzbu.ch', + 'author_company' => '', + 'constraints' => [ + 'depends' => [ + 'typo3' => '12.0.0', + 'workspaces' => '12.0.0', + ], + 'conflicts' => [], + 'suggests' => [], + ], +]; diff --git a/typo3/sysext/core/Tests/Functional/TypoScript/AST/AstBuilderTest.php b/typo3/sysext/core/Tests/Functional/TypoScript/AST/AstBuilderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..d1507d642500db84fbf56a1878aa72280effe304 --- /dev/null +++ b/typo3/sysext/core/Tests/Functional/TypoScript/AST/AstBuilderTest.php @@ -0,0 +1,72 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Functional\TypoScript\AST; + +use TYPO3\CMS\Core\TypoScript\AST\AstBuilder; +use TYPO3\CMS\Core\TypoScript\AST\Node\RootNode; +use TYPO3\CMS\Core\TypoScript\Tokenizer\LosslessTokenizer; +use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; + +class AstBuilderTest extends FunctionalTestCase +{ + protected array $testExtensionsToLoad = [ + 'typo3/sysext/core/Tests/Functional/Fixtures/Extensions/test_typoscript_ast_function_event', + ]; + + /** + * @test + */ + public function notModifiedValueKeepsNullValue(): void + { + $tokens = (new LosslessTokenizer())->tokenize('foo := doesNotExistFunction()'); + /** @var AstBuilder $astBuilder */ + $astBuilder = $this->get(AstBuilder::class); + $ast = $astBuilder->build($tokens, new RootNode()); + self::assertNull($ast->getChildByName('foo')->getValue()); + } + + /** + * @test + */ + public function notModifiedValueKeepsOriginalValue(): void + { + $tokens = (new LosslessTokenizer())->tokenize( + "foo = originalValue\n" . + 'foo := doesNotExistFunction()' + ); + /** @var AstBuilder $astBuilder */ + $astBuilder = $this->get(AstBuilder::class); + $ast = $astBuilder->build($tokens, new RootNode()); + self::assertSame('originalValue', $ast->getChildByName('foo')->getValue()); + } + + /** + * @test + */ + public function modifiedValueUpdatesOriginalValue(): void + { + $tokens = (new LosslessTokenizer())->tokenize( + "foo = originalValue\n" . + 'foo := testFunction(modifierArgument)' + ); + /** @var AstBuilder $astBuilder */ + $astBuilder = $this->get(AstBuilder::class); + $ast = $astBuilder->build($tokens, new RootNode()); + self::assertSame('originalValue modifierArgument', $ast->getChildByName('foo')->getValue()); + } +} diff --git a/typo3/sysext/core/Tests/Unit/Fixtures/EventDispatcher/NoopEventDispatcher.php b/typo3/sysext/core/Tests/Unit/Fixtures/EventDispatcher/NoopEventDispatcher.php new file mode 100644 index 0000000000000000000000000000000000000000..7f4d2477d89f8ea6bf3fb07818ffcae3e15ad251 --- /dev/null +++ b/typo3/sysext/core/Tests/Unit/Fixtures/EventDispatcher/NoopEventDispatcher.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +namespace TYPO3\CMS\Core\Tests\Unit\Fixtures\EventDispatcher; + +use Psr\EventDispatcher\EventDispatcherInterface; + +final class NoopEventDispatcher implements EventDispatcherInterface +{ + public function dispatch(object $event) + { + return $event; + } +} diff --git a/typo3/sysext/core/Tests/Unit/TypoScript/AST/AstBuilderTest.php b/typo3/sysext/core/Tests/Unit/TypoScript/AST/AstBuilderTest.php index af35c454027d69aa13b624cec425a6d89ea1b208..972e8504fe03d79bf3bd65db64527d66c5774f00 100644 --- a/typo3/sysext/core/Tests/Unit/TypoScript/AST/AstBuilderTest.php +++ b/typo3/sysext/core/Tests/Unit/TypoScript/AST/AstBuilderTest.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Core\Tests\Unit\TypoScript\AST; +use TYPO3\CMS\Core\Tests\Unit\Fixtures\EventDispatcher\NoopEventDispatcher; use TYPO3\CMS\Core\TypoScript\AST\AstBuilder; use TYPO3\CMS\Core\TypoScript\AST\Node\ChildNode; use TYPO3\CMS\Core\TypoScript\AST\Node\ReferenceChildNode; @@ -1329,8 +1330,9 @@ class AstBuilderTest extends UnitTestCase */ public function build(string $source, RootNode $expectedAst): void { + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode()); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode()); self::assertEquals($expectedAst, $ast); } @@ -1340,8 +1342,9 @@ class AstBuilderTest extends UnitTestCase */ public function buildCompatArray(string $source, RootNode $_, array $expectedArray): void { + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode()); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode()); self::assertEquals($expectedArray, $ast->toArray()); } @@ -1490,8 +1493,9 @@ class AstBuilderTest extends UnitTestCase */ public function buildReference(string $source, RootNode $expectedAst): void { + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode()); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode()); self::assertEquals($expectedAst, $ast); } @@ -1501,8 +1505,9 @@ class AstBuilderTest extends UnitTestCase */ public function buildReferenceArray(string $source, RootNode $_, array $expectedArray): void { + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode()); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode()); self::assertEquals($expectedArray, $ast->toArray()); } @@ -1633,8 +1638,9 @@ class AstBuilderTest extends UnitTestCase */ public function buildConstant(string $source, array $constants, RootNode $expectedAst): void { + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode(), $constants); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode(), $constants); self::assertEquals($expectedAst, $ast); } @@ -1644,8 +1650,9 @@ class AstBuilderTest extends UnitTestCase */ public function buildConstantCompatArray(string $source, array $constants, RootNode $_, array $expectedArray): void { + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode(), $constants); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode(), $constants); self::assertEquals($expectedArray, $ast->toArray()); } @@ -1672,8 +1679,9 @@ class AstBuilderTest extends UnitTestCase 'bar' => 'bar1', ]; + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize('bar = bar1'); - $resultAst = (new AstBuilder())->build($tokens, $inputAst, []); + $resultAst = (new AstBuilder($noopEventDispatcher))->build($tokens, $inputAst, []); self::assertEquals($expectedAst, $resultAst); self::assertEquals($expectedArray, $resultAst->toArray()); } @@ -1699,8 +1707,9 @@ class AstBuilderTest extends UnitTestCase { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionCode(1650893781); + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - (new AstBuilder())->build($tokens, new RootNode()); + (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode()); } public function functionGetEnvDataProvider(): \Generator @@ -1784,8 +1793,9 @@ class AstBuilderTest extends UnitTestCase if ($envVarName) { putenv($envVarName . '=' . $envVarValue); } + $noopEventDispatcher = new NoopEventDispatcher(); $tokens = (new LosslessTokenizer())->tokenize($source); - $ast = (new AstBuilder())->build($tokens, new RootNode()); + $ast = (new AstBuilder($noopEventDispatcher))->build($tokens, new RootNode()); self::assertEquals($expectedAst, $ast); if ($envVarName) { putenv($envVarName); @@ -1794,6 +1804,8 @@ class AstBuilderTest extends UnitTestCase public function flattenDataProvider(): \Generator { + $noopEventDispatcher = new NoopEventDispatcher(); + yield 'empty' => [ new RootNode(), [], @@ -1807,7 +1819,7 @@ class AstBuilderTest extends UnitTestCase 'second' => 'secondValue', ]; yield 'one level' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; @@ -1817,7 +1829,7 @@ class AstBuilderTest extends UnitTestCase 'first' => '', ]; yield 'empty string as value is kept' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; @@ -1827,7 +1839,7 @@ class AstBuilderTest extends UnitTestCase 'first' => '0', ]; yield 'zero as value is kept' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; @@ -1839,7 +1851,7 @@ class AstBuilderTest extends UnitTestCase 'second.secondSub' => 'secondSubValue', ]; yield 'two levels' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; @@ -1855,7 +1867,7 @@ class AstBuilderTest extends UnitTestCase 'second.secondSub' => 'secondSubValue', ]; yield 'two levels with values on first level' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; @@ -1871,7 +1883,7 @@ class AstBuilderTest extends UnitTestCase 'second.secondSub.secondSubSub' => 'secondSubSubValue', ]; yield 'three levels, partially with values' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; @@ -1881,7 +1893,7 @@ class AstBuilderTest extends UnitTestCase 'first.firstSub\.firstSubSub' => 'firstSubSubValue', ]; yield 'two levels with quoted dote' => [ - (new AstBuilder())->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), + (new AstBuilder($noopEventDispatcher))->build((new LosslessTokenizer())->tokenize($typoscript), new RootNode()), $expected, ]; } diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php index 8cdbc7fa8698094692a7331aea7b5899b22e35e3..1c9535b3f081092e5577de4bdebc6d7eb11ebbd1 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php @@ -820,4 +820,10 @@ return [ 'Feature-97945-PSR14AfterPageTreeItemsPreparedEvent.rst', ], ], + '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'t3lib/class.t3lib_tsparser.php\'][\'preParseFunc\']' => [ + 'restFiles' => [ + 'Breaking-98016-RemovedTypoScriptFunctionHook.rst', + 'Feature-98016-PSR-14EvaluateModifierFunctionEvent.rst', + ], + ], ];