From c64f292e52260bed45b3f4905ec59ddb696eccd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anke=20H=C3=A4slich?= Date: Tue, 7 Mar 2023 15:19:30 +0100 Subject: [PATCH 01/26] TASK: replace `Neos.Ui.NodeInfo.inBackend` and `Neos.Ui.NodeInfo.isLive` with correct eel helper - Replace all usages of `Neos.Ui.NodeInfo.inBackend` in favour of `Neos.Node.inBackend` - Replace all usages of `Neos.Ui.NodeInfo.isLive` in favour of `Neos.Node.isLive` Relates: neos/neos-development-collection#4074 Relates: neos/neos-ui#3414 --- config/set/contentrepository-90.php | 4 ++-- docs/architecture_2022_09_16_FusionRefactorings.md | 2 +- .../Rules/FusionContextInBackendRector.php | 6 +++--- src/ContentRepository90/Rules/FusionContextLiveRector.php | 6 +++--- .../FusionContextInBackendRector/Fixture/some_file.fusion | 8 ++++---- .../FusionContextLiveRector/Fixture/some_file.fusion | 8 ++++---- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index e1feb9c..69c668b 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -258,10 +258,10 @@ // ContentContext::getCurrentSiteNode // TODO: PHP // TODO: Fusion - // ContentContext::isLive -> Neos.Ui.NodeInfo.isLive(...) (TODO - should this be part of Neos.Ui or Neos Namespace?) + // ContentContext::isLive -> Neos.Node.isLive(...) // TODO: PHP $rectorConfig->rule(FusionContextLiveRector::class); - // ContentContext::isInBackend -> Neos.Ui.NodeInfo.inBackend(...) (TODO - should this be part of Neos.Ui or Neos Namespace?) + // ContentContext::isInBackend -> Neos.Node.inBackend(...) // TODO: PHP $rectorConfig->rule(FusionContextInBackendRector::class); // ContentContext::getCurrentRenderingMode diff --git a/docs/architecture_2022_09_16_FusionRefactorings.md b/docs/architecture_2022_09_16_FusionRefactorings.md index 2c32fe7..48801a4 100644 --- a/docs/architecture_2022_09_16_FusionRefactorings.md +++ b/docs/architecture_2022_09_16_FusionRefactorings.md @@ -3,7 +3,7 @@ ## Problem Description We want to refactor Eel expressions like `node.context.inBackend` or `site.context.inBackend` towards -`Neos.Ui.NodeInfo.inBackend(node)` and `Neos.Ui.NodeInfo.inBackend(site)` respectively. +`Neos.Node.inBackend(node)` and `Neos.Node.inBackend(site)` respectively. This must work BOTH in Eel expressions inside Fusion, and inside AFX blocks. diff --git a/src/ContentRepository90/Rules/FusionContextInBackendRector.php b/src/ContentRepository90/Rules/FusionContextInBackendRector.php index a996c50..0e5d38f 100644 --- a/src/ContentRepository90/Rules/FusionContextInBackendRector.php +++ b/src/ContentRepository90/Rules/FusionContextInBackendRector.php @@ -12,7 +12,7 @@ class FusionContextInBackendRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.inBackend to Neos.Ui.NodeInfo.inBackend(...)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...)', __CLASS__, 'some_class.fusion'); } public function refactorFileContent(string $fileContent): string @@ -20,12 +20,12 @@ public function refactorFileContent(string $fileContent): string return EelExpressionTransformer::parse($fileContent) ->process(fn(string $eelExpression) => preg_replace( '/(node|documentNode|site)\.context\.inBackend/', - 'Neos.Ui.NodeInfo.inBackend($1)', + 'Neos.Node.inBackend($1)', $eelExpression )) ->addCommentsIfRegexMatches( '/\.context\.inBackend/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Ui.NodeInfo.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Node.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' )->getProcessedContent(); } } diff --git a/src/ContentRepository90/Rules/FusionContextLiveRector.php b/src/ContentRepository90/Rules/FusionContextLiveRector.php index 3909965..28e16a0 100644 --- a/src/ContentRepository90/Rules/FusionContextLiveRector.php +++ b/src/ContentRepository90/Rules/FusionContextLiveRector.php @@ -12,7 +12,7 @@ class FusionContextLiveRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.live to Neos.Ui.NodeInfo.isLive(...)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.live to Neos.Node.isLive(...)', __CLASS__, 'some_class.fusion'); } public function refactorFileContent(string $fileContent): string @@ -20,12 +20,12 @@ public function refactorFileContent(string $fileContent): string return EelExpressionTransformer::parse($fileContent) ->process(fn(string $eelExpression) => preg_replace( '/(node|documentNode|site)\.context\.live/', - 'Neos.Ui.NodeInfo.isLive($1)', + 'Neos.Node.isLive($1)', $eelExpression )) ->addCommentsIfRegexMatches( '/\.context\.live/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.live" to Neos.Ui.NodeInfo.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.live" to Neos.Node.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' )->getProcessedContent(); } } diff --git a/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion index 7983fec..caece89 100644 --- a/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion +++ b/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion @@ -30,7 +30,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie } } ----- -// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Ui.NodeInfo.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Node.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { renderer = Neos.Fusion:Component { @@ -38,7 +38,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie # # pass down props # - attributes = ${Neos.Ui.NodeInfo.inBackend(node) || Neos.Ui.NodeInfo.inBackend(site) || Neos.Ui.NodeInfo.inBackend(documentNode)} + attributes = ${Neos.Node.inBackend(node) || Neos.Node.inBackend(site) || Neos.Node.inBackend(documentNode)} # # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` @@ -54,10 +54,10 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie renderer = afx` ` } diff --git a/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion index caad918..27bbb94 100644 --- a/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion +++ b/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion @@ -30,7 +30,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie } } ----- -// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.live" to Neos.Ui.NodeInfo.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.live" to Neos.Node.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { renderer = Neos.Fusion:Component { @@ -38,7 +38,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie # # pass down props # - attributes = ${Neos.Ui.NodeInfo.isLive(node) || Neos.Ui.NodeInfo.isLive(site) || Neos.Ui.NodeInfo.isLive(documentNode)} + attributes = ${Neos.Node.isLive(node) || Neos.Node.isLive(site) || Neos.Node.isLive(documentNode)} # # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` @@ -54,10 +54,10 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie renderer = afx` ` } From 98401765e55804bc00f7f67894e49f42d0c1955b Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Sat, 1 Apr 2023 15:45:17 +0200 Subject: [PATCH 02/26] FEATURE: ToStringToPropertyFetchRector --- config/set/contentrepository-90.php | 37 ++++++ .../Rules/ToStringToPropertyFetchRector.php | 108 ++++++++++++++++++ .../RemoveInjectionsRectorTest.php | 2 +- .../Fixture/some_class.php.inc | 21 ++++ .../Fixture/some_class.php.inc.bkp | 17 +++ .../ToStringToPropertyFetchRectorTest.php | 31 +++++ .../config/configured_rule.php | 12 ++ 7 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 src/Generic/Rules/ToStringToPropertyFetchRector.php create mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc create mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp create mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php create mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 69c668b..1d36aef 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -29,12 +29,14 @@ use Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector; use Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector; use Neos\Rector\Generic\Rules\RemoveInjectionsRector; +use Neos\Rector\Generic\Rules\ToStringToPropertyFetchRector; use Neos\Rector\Generic\ValueObject\FusionNodePropertyPathToWarningComment; use Neos\Rector\Generic\ValueObject\MethodCallToWarningComment; use Neos\Rector\Generic\ValueObject\RemoveInjection; use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; +use Rector\Transform\Rector\String_\ToStringToMethodCallRector; use Rector\Transform\ValueObject\MethodCallToPropertyFetch; return static function (RectorConfig $rectorConfig): void { @@ -299,4 +301,39 @@ // Should run LAST - as other rules above might create $this->contentRepositoryRegistry calls. $rectorConfig->rule(InjectContentRepositoryRegistryIfNeededRector::class); // TODO: does not fully seem to work.$rectorConfig->rule(RemoveDuplicateCommentRector::class); + + $rectorConfig->ruleWithConfiguration(ToStringToPropertyFetchRector::class, [ + \Neos\ContentRepository\Core\Dimension\ContentDimensionId::class => 'value', + \Neos\ContentRepository\Core\Dimension\ContentDimensionValue::class => 'value', + \Neos\ContentRepository\Core\Dimension\ContentDimensionValueSpecializationDepth::class => 'value', + \Neos\ContentRepository\Core\Factory\ContentRepositoryId::class => 'value', + \Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName::class => 'value', + \Neos\ContentRepository\Core\Infrastructure\Property\PropertyType::class => 'value', + \Neos\ContentRepository\Core\NodeType\NodeType::class => 'name', + \Neos\ContentRepository\Core\NodeType\NodeTypeName::class => 'value', + \Neos\ContentRepository\Core\Projection\ContentGraph\NodePath::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Node\NodeName::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Node\PropertyName::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Node\ReferenceName::class => 'value', + \Neos\ContentRepository\Core\SharedModel\User\UserId::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::class => 'value', + \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle::class => 'value', + ]); + + $rectorConfig->ruleWithConfiguration(ToStringToMethodCallRector::class, [ + \Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraints::class => 'toFilterString', + \Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraintsWithSubNodeTypes::class => 'toFilterString', + \Neos\ContentRepository\Core\DimensionSpace\AbstractDimensionSpacePoint::class => 'toJson', + \Neos\ContentRepository\Core\DimensionSpace\ContentSubgraphVariationWeight::class => 'toJson', + \Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet::class => 'toJson', + \Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet::class => 'toJson', + \Neos\ContentRepository\Core\Feature\NodeMove\Dto\ParentNodeMoveDestination::class => 'toJson', + \Neos\ContentRepository\Core\Feature\NodeMove\Dto\SucceedingSiblingNodeMoveDestination::class => 'toJson', + \Neos\ContentRepository\Core\Projection\ContentGraph\CoverageByOrigin::class => 'toJson', + \Neos\ContentRepository\Core\Projection\ContentGraph\OriginByCoverage::class => 'toJson', + \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds::class => 'toJson', + ]); }; diff --git a/src/Generic/Rules/ToStringToPropertyFetchRector.php b/src/Generic/Rules/ToStringToPropertyFetchRector.php new file mode 100644 index 0000000..db4c16b --- /dev/null +++ b/src/Generic/Rules/ToStringToPropertyFetchRector.php @@ -0,0 +1,108 @@ + + */ + private array $propertyNamesByType = []; + + public function getRuleDefinition() : RuleDefinition + { + return new RuleDefinition('Turns defined code uses of "__toString()" method to specific property fetches.', [new ConfiguredCodeSample(<<<'CODE_SAMPLE' +$someValue = new SomeObject; +$result = (string) $someValue; +$result = $someValue->__toString(); +CODE_SAMPLE + , <<<'CODE_SAMPLE' +$someValue = new SomeObject; +$result = $someValue->someProperty; +$result = $someValue->someProperty; +CODE_SAMPLE + , ['SomeObject' => 'someProperty'])]); + } + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [String_::class, MethodCall::class, Concat::class]; + } + /** + * @param String_|MethodCall|Concat $node + */ + public function refactor(Node $node) : ?Node + { + if ($node instanceof String_) { + return $this->processStringNode($node); + } + if ($node instanceof Concat) { + return $this->processConcatNode($node); + } + return $this->processMethodCall($node); + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration) : void + { + \RectorPrefix202211\Webmozart\Assert\Assert::allString(\array_keys($configuration)); + Assert::allString($configuration); + /** @var array $configuration */ + $this->propertyNamesByType = $configuration; + } + + private function processStringNode(String_ $string) : ?Node + { + foreach ($this->propertyNamesByType as $type => $propertyName) { + if (!$this->isObjectType($string->expr, new ObjectType($type))) { + continue; + } + return $this->nodeFactory->createPropertyFetch($string->expr, $propertyName); + } + return null; + } + + private function processConcatNode(Concat $concat) : ?Node + { + foreach ($this->propertyNamesByType as $type => $propertyName) { + if ($this->isObjectType($concat->right, new ObjectType($type))) { + $concat->right = $this->nodeFactory->createPropertyFetch($concat->right, $propertyName); + } + if ($this->isObjectType($concat->left, new ObjectType($type))) { + $concat->left = $this->nodeFactory->createPropertyFetch($concat->left, $propertyName); + } + } + return $concat; + } + + private function processMethodCall(MethodCall $methodCall) : ?Node + { + foreach ($this->propertyNamesByType as $type => $propertyName) { + if (!$this->isObjectType($methodCall->var, new ObjectType($type))) { + continue; + } + if (!$this->isName($methodCall->name, '__toString')) { + continue; + } + return $this->nodeFactory->createPropertyFetch($methodCall->var, $propertyName); + } + return null; + } +} diff --git a/tests/Generic/Rules/RemoveInjectionsRector/RemoveInjectionsRectorTest.php b/tests/Generic/Rules/RemoveInjectionsRector/RemoveInjectionsRectorTest.php index f867940..a684ca3 100644 --- a/tests/Generic/Rules/RemoveInjectionsRector/RemoveInjectionsRectorTest.php +++ b/tests/Generic/Rules/RemoveInjectionsRector/RemoveInjectionsRectorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Neos\Rector\Tests\Rules\RemoveInjectionsRector; +namespace Neos\Rector\Tests\Generic\Rules\RemoveInjectionsRector; use Rector\Testing\PHPUnit\AbstractRectorTestCase; diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc b/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc new file mode 100644 index 0000000..0f99a8a --- /dev/null +++ b/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc @@ -0,0 +1,21 @@ +__toString(); +$result = 'foo' . $someValue . $someOtherValue; +$result = $someValue . 'bar' . $someOtherValue; + +?> +----- +propertyName; +$result = $someValue->propertyName; +$result = 'foo' . $someValue->propertyName . $someOtherValue; +$result = $someValue->propertyName . 'bar' . $someOtherValue; + +?> diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp b/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp new file mode 100644 index 0000000..c2d4331 --- /dev/null +++ b/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp @@ -0,0 +1,17 @@ +__toString(); +$result = 'foo' . $someValue; + +?> +----- +propertyName; +$result = $someValue->propertyName; +$result = 'foo' . $someValue->propertyName; + +?> diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php b/tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php new file mode 100644 index 0000000..29268e9 --- /dev/null +++ b/tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php b/tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php new file mode 100644 index 0000000..6d268b6 --- /dev/null +++ b/tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(ToStringToPropertyFetchRector::class, [ + 'SomeObject' => 'propertyName', + ]); +}; From abf71309e8688373ce20e448ad64fd9c301794cb Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Sun, 2 Apr 2023 14:56:55 +0200 Subject: [PATCH 03/26] Extend rector and rename to `ToStringToMethodCallOrPropertyFetchRector` --- config/set/contentrepository-90.php | 31 ++-- ...tringToMethodCallOrPropertyFetchRector.php | 146 ++++++++++++++++++ .../Rules/ToStringToPropertyFetchRector.php | 108 ------------- ...torGetAllAllowedCombinationsRectorTest.php | 31 ---- .../Fixture/some_class.php.inc | 35 +++++ ...ToMethodCallOrPropertyFetchRectorTest.php} | 2 +- .../config/configured_rule.php | 13 ++ .../Fixture/some_class.php.inc | 21 --- .../Fixture/some_class.php.inc.bkp | 17 -- .../config/configured_rule.php | 12 -- 10 files changed, 208 insertions(+), 208 deletions(-) create mode 100644 src/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector.php delete mode 100644 src/Generic/Rules/ToStringToPropertyFetchRector.php delete mode 100644 tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/ContentDimensionCombinatorGetAllAllowedCombinationsRectorTest.php create mode 100644 tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/Fixture/some_class.php.inc rename tests/Generic/Rules/{ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php => ToStringToMethodCallOrPropertyFetchRector/ToStringToMethodCallOrPropertyFetchRectorTest.php} (87%) create mode 100644 tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/config/configured_rule.php delete mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc delete mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp delete mode 100644 tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 1d36aef..fcc3793 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -27,16 +27,14 @@ use Neos\Rector\ContentRepository90\Rules\NodeIsHiddenRector; use Neos\Rector\Generic\Rules\FusionNodePropertyPathToWarningCommentRector; use Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector; -use Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector; use Neos\Rector\Generic\Rules\RemoveInjectionsRector; -use Neos\Rector\Generic\Rules\ToStringToPropertyFetchRector; +use Neos\Rector\Generic\Rules\ToStringToMethodCallOrPropertyFetchRector; use Neos\Rector\Generic\ValueObject\FusionNodePropertyPathToWarningComment; use Neos\Rector\Generic\ValueObject\MethodCallToWarningComment; use Neos\Rector\Generic\ValueObject\RemoveInjection; use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; -use Rector\Transform\Rector\String_\ToStringToMethodCallRector; use Rector\Transform\ValueObject\MethodCallToPropertyFetch; return static function (RectorConfig $rectorConfig): void { @@ -302,7 +300,7 @@ $rectorConfig->rule(InjectContentRepositoryRegistryIfNeededRector::class); // TODO: does not fully seem to work.$rectorConfig->rule(RemoveDuplicateCommentRector::class); - $rectorConfig->ruleWithConfiguration(ToStringToPropertyFetchRector::class, [ + $rectorConfig->ruleWithConfiguration(ToStringToMethodCallOrPropertyFetchRector::class, [ \Neos\ContentRepository\Core\Dimension\ContentDimensionId::class => 'value', \Neos\ContentRepository\Core\Dimension\ContentDimensionValue::class => 'value', \Neos\ContentRepository\Core\Dimension\ContentDimensionValueSpecializationDepth::class => 'value', @@ -321,19 +319,16 @@ \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription::class => 'value', \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::class => 'value', \Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle::class => 'value', - ]); - - $rectorConfig->ruleWithConfiguration(ToStringToMethodCallRector::class, [ - \Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraints::class => 'toFilterString', - \Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraintsWithSubNodeTypes::class => 'toFilterString', - \Neos\ContentRepository\Core\DimensionSpace\AbstractDimensionSpacePoint::class => 'toJson', - \Neos\ContentRepository\Core\DimensionSpace\ContentSubgraphVariationWeight::class => 'toJson', - \Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet::class => 'toJson', - \Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet::class => 'toJson', - \Neos\ContentRepository\Core\Feature\NodeMove\Dto\ParentNodeMoveDestination::class => 'toJson', - \Neos\ContentRepository\Core\Feature\NodeMove\Dto\SucceedingSiblingNodeMoveDestination::class => 'toJson', - \Neos\ContentRepository\Core\Projection\ContentGraph\CoverageByOrigin::class => 'toJson', - \Neos\ContentRepository\Core\Projection\ContentGraph\OriginByCoverage::class => 'toJson', - \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds::class => 'toJson', + \Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraints::class => 'toFilterString()', + \Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraintsWithSubNodeTypes::class => 'toFilterString()', + \Neos\ContentRepository\Core\DimensionSpace\AbstractDimensionSpacePoint::class => 'toJson()', + \Neos\ContentRepository\Core\DimensionSpace\ContentSubgraphVariationWeight::class => 'toJson()', + \Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet::class => 'toJson()', + \Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet::class => 'toJson()', + \Neos\ContentRepository\Core\Feature\NodeMove\Dto\ParentNodeMoveDestination::class => 'toJson()', + \Neos\ContentRepository\Core\Feature\NodeMove\Dto\SucceedingSiblingNodeMoveDestination::class => 'toJson()', + \Neos\ContentRepository\Core\Projection\ContentGraph\CoverageByOrigin::class => 'toJson()', + \Neos\ContentRepository\Core\Projection\ContentGraph\OriginByCoverage::class => 'toJson()', + \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds::class => 'toJson()', ]); }; diff --git a/src/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector.php b/src/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector.php new file mode 100644 index 0000000..07c4988 --- /dev/null +++ b/src/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector.php @@ -0,0 +1,146 @@ + + */ + private array $methodAndPropertyNamesByType = []; + + public function getRuleDefinition() : RuleDefinition + { + return new RuleDefinition('Turns defined code uses of "__toString()" method to specific method calls or property fetches.', [new ConfiguredCodeSample(<<<'CODE_SAMPLE' +$someValue = new SomeObject; +$result = (string) $someValue; +$result = $someValue->__toString(); +CODE_SAMPLE + , <<<'CODE_SAMPLE' +$someValue = new SomeObject; +$result = $someValue->getPath(); +$result = $someValue->getPath(); +CODE_SAMPLE + , ['SomeObject' => 'getPath()'])]); + } + + /** + * @return array> + */ + public function getNodeTypes() : array + { + return [String_::class, MethodCall::class, Concat::class, FuncCall::class]; + } + + /** + * @param String_|MethodCall|Concat $node + */ + public function refactor(Node $node) : ?Node + { + if ($node instanceof String_) { + return $this->processStringNode($node); + } + if ($node instanceof Concat) { + return $this->processConcatNode($node); + } + if ($node instanceof FuncCall) { + return $this->processFuncCallNode($node); + } + return $this->processMethodCall($node); + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration) : void + { + Assert::allString(\array_keys($configuration)); + Assert::allString($configuration); + /** @var array $configuration */ + $this->methodAndPropertyNamesByType = $configuration; + } + + private function processStringNode(String_ $string) : ?Node + { + foreach ($this->methodAndPropertyNamesByType as $type => $methodOrPropertyName) { + if (!$this->isObjectType($string->expr, new ObjectType($type))) { + continue; + } + return $this->replaceByMethodCallOrPropertyFetch($string->expr, $methodOrPropertyName); + } + return null; + } + + private function processConcatNode(Concat $concat) : ?Node + { + foreach ($this->methodAndPropertyNamesByType as $type => $methodOrPropertyName) { + if ($this->isObjectType($concat->left, new ObjectType($type))) { + $concat->left = $this->replaceByMethodCallOrPropertyFetch($concat->left, $methodOrPropertyName); + } + if ($this->isObjectType($concat->right, new ObjectType($type))) { + $concat->right = $this->replaceByMethodCallOrPropertyFetch($concat->right, $methodOrPropertyName); + } + } + return $concat; + } + + private function processFuncCallNode(FuncCall $funcCall) : ?Node + { + if (!$this->isName($funcCall, 'sprintf')) { + return null; + } + foreach ($funcCall->args as $index => $arg) { + if ($index === 0) { + continue; + } + foreach ($this->methodAndPropertyNamesByType as $type => $methodOrPropertyName) { + if ($this->isObjectType($arg->value, new ObjectType($type))) { + $arg->value = $this->replaceByMethodCallOrPropertyFetch($arg->value, $methodOrPropertyName); + } + } + } + return $funcCall; + } + + private function processMethodCall(MethodCall $methodCall) : ?Node + { + foreach ($this->methodAndPropertyNamesByType as $type => $methodOrPropertyName) { + if (!$this->isObjectType($methodCall->var, new ObjectType($type))) { + continue; + } + if (!$this->isName($methodCall->name, '__toString')) { + continue; + } + if (str_ends_with($methodOrPropertyName, '()')) { + $methodCall->name = new Identifier(substr($methodOrPropertyName, 0, -2)); + return $methodCall; + } + return $this->nodeFactory->createPropertyFetch($methodCall->var, $methodOrPropertyName); + } + return null; + } + + private function replaceByMethodCallOrPropertyFetch(Expr $expr, string $methodOrPropertyName): Expr + { + if (str_ends_with($methodOrPropertyName, '()')) { + return $this->nodeFactory->createMethodCall($expr, substr($methodOrPropertyName, 0, -2)); + } + return $this->nodeFactory->createPropertyFetch($expr, $methodOrPropertyName); + } +} diff --git a/src/Generic/Rules/ToStringToPropertyFetchRector.php b/src/Generic/Rules/ToStringToPropertyFetchRector.php deleted file mode 100644 index db4c16b..0000000 --- a/src/Generic/Rules/ToStringToPropertyFetchRector.php +++ /dev/null @@ -1,108 +0,0 @@ - - */ - private array $propertyNamesByType = []; - - public function getRuleDefinition() : RuleDefinition - { - return new RuleDefinition('Turns defined code uses of "__toString()" method to specific property fetches.', [new ConfiguredCodeSample(<<<'CODE_SAMPLE' -$someValue = new SomeObject; -$result = (string) $someValue; -$result = $someValue->__toString(); -CODE_SAMPLE - , <<<'CODE_SAMPLE' -$someValue = new SomeObject; -$result = $someValue->someProperty; -$result = $someValue->someProperty; -CODE_SAMPLE - , ['SomeObject' => 'someProperty'])]); - } - /** - * @return array> - */ - public function getNodeTypes() : array - { - return [String_::class, MethodCall::class, Concat::class]; - } - /** - * @param String_|MethodCall|Concat $node - */ - public function refactor(Node $node) : ?Node - { - if ($node instanceof String_) { - return $this->processStringNode($node); - } - if ($node instanceof Concat) { - return $this->processConcatNode($node); - } - return $this->processMethodCall($node); - } - - /** - * @param mixed[] $configuration - */ - public function configure(array $configuration) : void - { - \RectorPrefix202211\Webmozart\Assert\Assert::allString(\array_keys($configuration)); - Assert::allString($configuration); - /** @var array $configuration */ - $this->propertyNamesByType = $configuration; - } - - private function processStringNode(String_ $string) : ?Node - { - foreach ($this->propertyNamesByType as $type => $propertyName) { - if (!$this->isObjectType($string->expr, new ObjectType($type))) { - continue; - } - return $this->nodeFactory->createPropertyFetch($string->expr, $propertyName); - } - return null; - } - - private function processConcatNode(Concat $concat) : ?Node - { - foreach ($this->propertyNamesByType as $type => $propertyName) { - if ($this->isObjectType($concat->right, new ObjectType($type))) { - $concat->right = $this->nodeFactory->createPropertyFetch($concat->right, $propertyName); - } - if ($this->isObjectType($concat->left, new ObjectType($type))) { - $concat->left = $this->nodeFactory->createPropertyFetch($concat->left, $propertyName); - } - } - return $concat; - } - - private function processMethodCall(MethodCall $methodCall) : ?Node - { - foreach ($this->propertyNamesByType as $type => $propertyName) { - if (!$this->isObjectType($methodCall->var, new ObjectType($type))) { - continue; - } - if (!$this->isName($methodCall->name, '__toString')) { - continue; - } - return $this->nodeFactory->createPropertyFetch($methodCall->var, $propertyName); - } - return null; - } -} diff --git a/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/ContentDimensionCombinatorGetAllAllowedCombinationsRectorTest.php b/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/ContentDimensionCombinatorGetAllAllowedCombinationsRectorTest.php deleted file mode 100644 index 214a634..0000000 --- a/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/ContentDimensionCombinatorGetAllAllowedCombinationsRectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -doTestFile($fileInfo); - } - - /** - * @return \Iterator - */ - public function provideData(): \Iterator - { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); - } - - public function provideConfigFilePath(): string - { - return __DIR__ . '/config/configured_rule.php'; - } -} diff --git a/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/Fixture/some_class.php.inc b/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/Fixture/some_class.php.inc new file mode 100644 index 0000000..33aef85 --- /dev/null +++ b/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/Fixture/some_class.php.inc @@ -0,0 +1,35 @@ +__toString(); +$result = 'foo' . $someValue; +$result = $someValue . 'bar'; +$result = sprintf('some %s format', $someValue); +$result = sprintf('some %s other %s format', 'replaced', $someValue); +$result = (string) $someOtherValue; +$result = $someOtherValue->__toString(); +$result = 'foo' . $someOtherValue; +$result = $someOtherValue . 'bar'; +$result = sprintf('some %s format', $someOtherValue); +$result = sprintf('some %s other %s format', 'replaced', $someOtherValue); +?> +----- +methodName(); +$result = $someValue->methodName(); +$result = 'foo' . $someValue->methodName(); +$result = $someValue->methodName() . 'bar'; +$result = sprintf('some %s format', $someValue->methodName()); +$result = sprintf('some %s other %s format', 'replaced', $someValue->methodName()); +$result = $someOtherValue->propertyName; +$result = $someOtherValue->propertyName; +$result = 'foo' . $someOtherValue->propertyName; +$result = $someOtherValue->propertyName . 'bar'; +$result = sprintf('some %s format', $someOtherValue->propertyName); +$result = sprintf('some %s other %s format', 'replaced', $someOtherValue->propertyName); +?> diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php b/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/ToStringToMethodCallOrPropertyFetchRectorTest.php similarity index 87% rename from tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php rename to tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/ToStringToMethodCallOrPropertyFetchRectorTest.php index 29268e9..a6ec114 100644 --- a/tests/Generic/Rules/ToStringToPropertyFetchRector/ToStringToPropertyFetchRectorTest.php +++ b/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/ToStringToMethodCallOrPropertyFetchRectorTest.php @@ -6,7 +6,7 @@ use Rector\Testing\PHPUnit\AbstractRectorTestCase; -final class ToStringToPropertyFetchRectorTest extends AbstractRectorTestCase +final class ToStringToMethodCallOrPropertyFetchRectorTest extends AbstractRectorTestCase { /** * @dataProvider provideData() diff --git a/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/config/configured_rule.php b/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/config/configured_rule.php new file mode 100644 index 0000000..74d577d --- /dev/null +++ b/tests/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector/config/configured_rule.php @@ -0,0 +1,13 @@ +ruleWithConfiguration(ToStringToMethodCallOrPropertyFetchRector::class, [ + 'SomeObject' => 'methodName()', + 'SomeOtherObject' => 'propertyName', + ]); +}; diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc b/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc deleted file mode 100644 index 0f99a8a..0000000 --- a/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc +++ /dev/null @@ -1,21 +0,0 @@ -__toString(); -$result = 'foo' . $someValue . $someOtherValue; -$result = $someValue . 'bar' . $someOtherValue; - -?> ------ -propertyName; -$result = $someValue->propertyName; -$result = 'foo' . $someValue->propertyName . $someOtherValue; -$result = $someValue->propertyName . 'bar' . $someOtherValue; - -?> diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp b/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp deleted file mode 100644 index c2d4331..0000000 --- a/tests/Generic/Rules/ToStringToPropertyFetchRector/Fixture/some_class.php.inc.bkp +++ /dev/null @@ -1,17 +0,0 @@ -__toString(); -$result = 'foo' . $someValue; - -?> ------ -propertyName; -$result = $someValue->propertyName; -$result = 'foo' . $someValue->propertyName; - -?> diff --git a/tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php b/tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php deleted file mode 100644 index 6d268b6..0000000 --- a/tests/Generic/Rules/ToStringToPropertyFetchRector/config/configured_rule.php +++ /dev/null @@ -1,12 +0,0 @@ -ruleWithConfiguration(ToStringToPropertyFetchRector::class, [ - 'SomeObject' => 'propertyName', - ]); -}; From 4ed7554ab180a63f110835147c2ab3e3cf49817a Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Tue, 11 Apr 2023 21:37:49 +0200 Subject: [PATCH 04/26] TASK: raise Rector version to latest; fix testcases. This is a preparation for new Rectors. --- composer.json | 6 +++--- .../Rules/ContextGetRootNodeRector.php | 6 ++++-- .../Rules/NodeTypeManagerAccessRector.php | 7 ++++--- src/Generic/Rules/RemoveInjectionsRector.php | 3 +++ src/Generic/Rules/Traits/FunctionsTrait.php | 21 +++++++++++++++---- ...{some_file.fusion => some_file.fusion.inc} | 0 .../FusionContextInBackendRectorTest.php | 2 +- ...{some_file.fusion => some_file.fusion.inc} | 0 .../FusionContextLiveRectorTest.php | 2 +- ...{some_file.fusion => some_file.fusion.inc} | 0 .../FusionNodeDepthRectorTest.php | 2 +- ...{some_file.fusion => some_file.fusion.inc} | 0 .../FusionNodeHiddenInIndexRectorTest.php | 2 +- ...{some_file.fusion => some_file.fusion.inc} | 0 .../FusionNodeParentRectorTest.php | 2 +- ...{some_file.fusion => some_file.fusion.inc} | 0 .../FusionNodePathRectorTest.php | 2 +- .../Fixture/some_class.php.inc | 1 - ...{some_file.fusion => some_file.fusion.inc} | 0 ...PropertyPathToWarningCommentRectorTest.php | 2 +- .../Fixture/some_class.php.inc | 1 - .../config/configured_rule.php | 1 + 22 files changed, 39 insertions(+), 21 deletions(-) rename tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) rename tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) rename tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) rename tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) rename tests/ContentRepository90/Rules/FusionNodeParentRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) rename tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) rename tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/Fixture/{some_file.fusion => some_file.fusion.inc} (100%) diff --git a/composer.json b/composer.json index fa38670..33ba0c0 100644 --- a/composer.json +++ b/composer.json @@ -15,10 +15,10 @@ "tests": "phpunit tests" }, "require": { - "rector/rector": "^0.14.2" + "rector/rector": "0.15.24" }, "require-dev": { - "symplify/rule-doc-generator": "^11.1", - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^9.5", + "webmozart/assert": "^1.11" } } diff --git a/src/ContentRepository90/Rules/ContextGetRootNodeRector.php b/src/ContentRepository90/Rules/ContextGetRootNodeRector.php index 4424d8d..a2bbebf 100644 --- a/src/ContentRepository90/Rules/ContextGetRootNodeRector.php +++ b/src/ContentRepository90/Rules/ContextGetRootNodeRector.php @@ -51,8 +51,10 @@ public function refactor(Node $node) : ?Node $this->nodesToAddCollector->addNodesBeforeNode( [ - self::todoComment('!! MEGA DIRTY CODE! Ensure to rewrite this; by getting rid of LegacyContextStub.'), - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + self::withTodoComment( + '!! MEGA DIRTY CODE! Ensure to rewrite this; by getting rid of LegacyContextStub.', + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))) + ), self::assign('workspace', $this->contentRepository_getWorkspaceFinder_findOneByName($this->workspaceName_fromString($this->context_workspaceName_fallbackToLive($node->var)))), self::assign('rootNodeAggregate', $this->contentRepository_getContentGraph_findRootNodeAggregateByType($this->workspace_currentContentStreamId(), $this->nodeTypeName_fromString('Neos.Neos:Sites'))), self::assign('subgraph', $this->contentRepository_getContentGraph_getSubgraph($this->workspace_currentContentStreamId(), $this->dimensionSpacePoint_fromLegacyDimensionArray($this->context_dimensions_fallbackToEmpty($node->var)), $this->visibilityConstraints($node->var))), diff --git a/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php b/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php index 67d2d6c..1be72f7 100644 --- a/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php +++ b/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php @@ -49,9 +49,10 @@ public function refactor(Node $node) : ?Node $this->nodesToAddCollector->addNodesBeforeNode( [ - self::todoComment('Make this code aware of multiple Content Repositories.'), - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - + self::withTodoComment( + 'Make this code aware of multiple Content Repositories.', + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + ) ], $node ); diff --git a/src/Generic/Rules/RemoveInjectionsRector.php b/src/Generic/Rules/RemoveInjectionsRector.php index d3e8ab1..ab1800b 100644 --- a/src/Generic/Rules/RemoveInjectionsRector.php +++ b/src/Generic/Rules/RemoveInjectionsRector.php @@ -77,6 +77,9 @@ private static function hasFlowInjectAttribute(array $attrGroups): bool private function hasFlowInjectDocComment(Node\Stmt\Property $node): bool { $phpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if ($phpDocInfo === null) { + return false; + } return $phpDocInfo->findOneByAnnotationClass('Neos\Flow\Annotations\Inject') !== null; } diff --git a/src/Generic/Rules/Traits/FunctionsTrait.php b/src/Generic/Rules/Traits/FunctionsTrait.php index dff68ae..8ca0e0c 100644 --- a/src/Generic/Rules/Traits/FunctionsTrait.php +++ b/src/Generic/Rules/Traits/FunctionsTrait.php @@ -7,6 +7,7 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Nop; trait FunctionsTrait @@ -26,11 +27,15 @@ private function castToString(Expr $inner): Expr return new Expr\Cast\String_($inner); } - private static function assign(string $variableName, Expr $value): Assign + private static function assign(string $variableName, Expr $value): Expression { - return new Assign( - new Variable($variableName), - $value + // NOTE: it is crucial to wrap thw assign in an expression; so that this works with self::withTodoComment. + // (otherwise the comment is silently swallowed because it is added to the wrong element, and thus not printed) + return new Expression( + new Assign( + new Variable($variableName), + $value + ) ); } @@ -43,6 +48,14 @@ private static function todoComment(string $commentText): Nop ]); } + private static function withTodoComment(string $commentText, \PhpParser\NodeAbstract $attachmentNode): \PhpParser\Node + { + $attachmentNode->setAttribute('comments', [ + new Comment('// TODO 9.0 migration: ' . $commentText) + ]); + return $attachmentNode; + } + private static function todoCommentAttribute(string $commentText): Comment { return new Comment('// TODO 9.0 migration: ' . $commentText); diff --git a/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion rename to tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion.inc diff --git a/tests/ContentRepository90/Rules/FusionContextInBackendRector/FusionContextInBackendRectorTest.php b/tests/ContentRepository90/Rules/FusionContextInBackendRector/FusionContextInBackendRectorTest.php index f424117..918d63e 100644 --- a/tests/ContentRepository90/Rules/FusionContextInBackendRector/FusionContextInBackendRectorTest.php +++ b/tests/ContentRepository90/Rules/FusionContextInBackendRector/FusionContextInBackendRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion rename to tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion.inc diff --git a/tests/ContentRepository90/Rules/FusionContextLiveRector/FusionContextLiveRectorTest.php b/tests/ContentRepository90/Rules/FusionContextLiveRector/FusionContextLiveRectorTest.php index f27627e..c325c16 100644 --- a/tests/ContentRepository90/Rules/FusionContextLiveRector/FusionContextLiveRectorTest.php +++ b/tests/ContentRepository90/Rules/FusionContextLiveRector/FusionContextLiveRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion rename to tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion.inc diff --git a/tests/ContentRepository90/Rules/FusionNodeDepthRector/FusionNodeDepthRectorTest.php b/tests/ContentRepository90/Rules/FusionNodeDepthRector/FusionNodeDepthRectorTest.php index ee16c63..49b4d74 100644 --- a/tests/ContentRepository90/Rules/FusionNodeDepthRector/FusionNodeDepthRectorTest.php +++ b/tests/ContentRepository90/Rules/FusionNodeDepthRector/FusionNodeDepthRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/Fixture/some_file.fusion rename to tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/Fixture/some_file.fusion.inc diff --git a/tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/FusionNodeHiddenInIndexRectorTest.php b/tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/FusionNodeHiddenInIndexRectorTest.php index bc09524..14c9175 100644 --- a/tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/FusionNodeHiddenInIndexRectorTest.php +++ b/tests/ContentRepository90/Rules/FusionNodeHiddenInIndexRector/FusionNodeHiddenInIndexRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/ContentRepository90/Rules/FusionNodeParentRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionNodeParentRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/ContentRepository90/Rules/FusionNodeParentRector/Fixture/some_file.fusion rename to tests/ContentRepository90/Rules/FusionNodeParentRector/Fixture/some_file.fusion.inc diff --git a/tests/ContentRepository90/Rules/FusionNodeParentRector/FusionNodeParentRectorTest.php b/tests/ContentRepository90/Rules/FusionNodeParentRector/FusionNodeParentRectorTest.php index 688c8de..43f050e 100644 --- a/tests/ContentRepository90/Rules/FusionNodeParentRector/FusionNodeParentRectorTest.php +++ b/tests/ContentRepository90/Rules/FusionNodeParentRector/FusionNodeParentRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion b/tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion rename to tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion.inc diff --git a/tests/ContentRepository90/Rules/FusionNodePathRector/FusionNodePathRectorTest.php b/tests/ContentRepository90/Rules/FusionNodePathRector/FusionNodePathRectorTest.php index 6ca315a..70d7bc9 100644 --- a/tests/ContentRepository90/Rules/FusionNodePathRector/FusionNodePathRectorTest.php +++ b/tests/ContentRepository90/Rules/FusionNodePathRector/FusionNodePathRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/ContentRepository90/Rules/NodeTypeManagerAccessRector/Fixture/some_class.php.inc b/tests/ContentRepository90/Rules/NodeTypeManagerAccessRector/Fixture/some_class.php.inc index 907c343..fc9f436 100644 --- a/tests/ContentRepository90/Rules/NodeTypeManagerAccessRector/Fixture/some_class.php.inc +++ b/tests/ContentRepository90/Rules/NodeTypeManagerAccessRector/Fixture/some_class.php.inc @@ -27,7 +27,6 @@ class SomeClass public function run() { // TODO 9.0 migration: Make this code aware of multiple Content Repositories. - $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); $nt = $contentRepository->getNodeTypeManager()->getNodeTypes(false); } diff --git a/tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/Fixture/some_file.fusion b/tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/Fixture/some_file.fusion.inc similarity index 100% rename from tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/Fixture/some_file.fusion rename to tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/Fixture/some_file.fusion.inc diff --git a/tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/FusionNodePropertyPathToWarningCommentRectorTest.php b/tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/FusionNodePropertyPathToWarningCommentRectorTest.php index 854c86e..1229c23 100644 --- a/tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/FusionNodePropertyPathToWarningCommentRectorTest.php +++ b/tests/Generic/Rules/FusionNodePropertyPathToWarningCommentRector/FusionNodePropertyPathToWarningCommentRectorTest.php @@ -21,7 +21,7 @@ public function test(string $fileInfo): void */ public function provideData(): \Iterator { - return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion'); + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); } public function provideConfigFilePath(): string diff --git a/tests/Rules/ContextGetRootNodeRector/Fixture/some_class.php.inc b/tests/Rules/ContextGetRootNodeRector/Fixture/some_class.php.inc index bf991c2..097d713 100644 --- a/tests/Rules/ContextGetRootNodeRector/Fixture/some_class.php.inc +++ b/tests/Rules/ContextGetRootNodeRector/Fixture/some_class.php.inc @@ -21,7 +21,6 @@ class SomeClass public function run(\Neos\Rector\ContentRepository90\Legacy\LegacyContextStub $context) { // TODO 9.0 migration: !! MEGA DIRTY CODE! Ensure to rewrite this; by getting rid of LegacyContextStub. - $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); $workspace = $contentRepository->getWorkspaceFinder()->findOneByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($context->workspaceName ?? 'live')); $rootNodeAggregate = $contentRepository->getContentGraph()->findRootNodeAggregateByType($workspace->currentContentStreamId, \Neos\ContentRepository\Core\NodeType\NodeTypeName::fromString('Neos.Neos:Sites')); diff --git a/tests/Rules/ContextGetRootNodeRector/config/configured_rule.php b/tests/Rules/ContextGetRootNodeRector/config/configured_rule.php index 0ba739b..57b001f 100644 --- a/tests/Rules/ContextGetRootNodeRector/config/configured_rule.php +++ b/tests/Rules/ContextGetRootNodeRector/config/configured_rule.php @@ -8,4 +8,5 @@ return static function (RectorConfig $rectorConfig) : void { $rectorConfig->rule(ContextGetRootNodeRector::class); + $rectorConfig->rule(\Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector::class); }; From b45ac7fe7ea09301326f917704329071d43828b8 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Tue, 11 Apr 2023 22:16:19 +0200 Subject: [PATCH 05/26] FEATURE: add WorkspaceRepository::countByName rector --- config/set/contentrepository-90.php | 9 ++- .../WorkspaceRepositoryCountByNameRector.php | 70 +++++++++++++++++++ .../Fixture/some_class.php.inc | 39 +++++++++++ ...rkspaceRepositoryCountByNameRectorTest.php | 31 ++++++++ .../config/configured_rule.php | 16 +++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php create mode 100644 tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/Fixture/some_class.php.inc create mode 100644 tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/WorkspaceRepositoryCountByNameRectorTest.php create mode 100644 tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 69c668b..06c9c03 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -25,6 +25,7 @@ use Neos\Rector\ContentRepository90\Rules\NodeGetPathRector; use Neos\Rector\ContentRepository90\Rules\NodeIsHiddenInIndexRector; use Neos\Rector\ContentRepository90\Rules\NodeIsHiddenRector; +use Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryCountByNameRector; use Neos\Rector\Generic\Rules\FusionNodePropertyPathToWarningCommentRector; use Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector; use Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector; @@ -282,6 +283,11 @@ // NodeFactory::reset $rectorConfig->rule(NodeFactoryResetRector::class); + /** + * Neos\ContentRepository\Domain\Repository\WorkspaceRepository + */ + $rectorConfig->rule(WorkspaceRepositoryCountByNameRector::class); + /** * CLEAN UP / END GLOBAL RULES */ @@ -293,7 +299,8 @@ $rectorConfig->ruleWithConfiguration(RemoveInjectionsRector::class, [ new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContextFactoryInterface::class), new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContentDimensionCombinator::class), - new RemoveInjection(\Neos\ContentRepository\Domain\Factory\NodeFactory::class) + new RemoveInjection(\Neos\ContentRepository\Domain\Factory\NodeFactory::class), + new RemoveInjection(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class) ]); // Should run LAST - as other rules above might create $this->contentRepositoryRegistry calls. diff --git a/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php b/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php new file mode 100644 index 0000000..4a18a0f --- /dev/null +++ b/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php @@ -0,0 +1,70 @@ +> + */ + public function getNodeTypes(): array + { + return [\PhpParser\Node\Expr\MethodCall::class]; + } + + /** + * @param \PhpParser\Node\Expr\MethodCall $node + */ + public function refactor(Node $node): ?Node + { + assert($node instanceof Node\Expr\MethodCall); + + if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + return null; + } + if (!$this->isName($node->name, 'countByName')) { + return null; + } + + + $this->nodesToAddCollector->addNodesBeforeNode( + [ + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + self::todoComment('remove ternary operator (...? 1 : 0 ) - unnecessary complexity',) + ], + $node + ); + + return new Node\Expr\Ternary( + new Node\Expr\BinaryOp\NotIdentical( + $this->contentRepository_getWorkspaceFinder_findOneByName($this->workspaceName_fromString($node->args[0]->value)), + new Expr\ConstFetch(new Node\Name('null')) + ), + new Node\Scalar\LNumber(1), + new Node\Scalar\LNumber(0) + ); + } +} diff --git a/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/Fixture/some_class.php.inc b/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/Fixture/some_class.php.inc new file mode 100644 index 0000000..41b5f9f --- /dev/null +++ b/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/Fixture/some_class.php.inc @@ -0,0 +1,39 @@ +workspaceRepository->countByName($workspace); + } +} + +?> +----- +contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); + // TODO 9.0 migration: remove ternary operator (...? 1 : 0 ) - unnecessary complexity + + return $contentRepository->getWorkspaceFinder()->findOneByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($workspace)) !== null ? 1 : 0; + } +} + +?> diff --git a/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/WorkspaceRepositoryCountByNameRectorTest.php b/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/WorkspaceRepositoryCountByNameRectorTest.php new file mode 100644 index 0000000..c4658e6 --- /dev/null +++ b/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/WorkspaceRepositoryCountByNameRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/config/configured_rule.php b/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/config/configured_rule.php new file mode 100644 index 0000000..dcacfe3 --- /dev/null +++ b/tests/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector/config/configured_rule.php @@ -0,0 +1,16 @@ +rule(\Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryCountByNameRector::class); + + $rectorConfig->rule(\Neos\Rector\ContentRepository90\Rules\InjectContentRepositoryRegistryIfNeededRector::class); + $rectorConfig->ruleWithConfiguration(RemoveInjectionsRector::class, [ + new RemoveInjection(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class) + ]); +}; From 6bc0427016ff72c7a3188f17489a4a1e4266fa26 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Fri, 14 Apr 2023 22:09:38 +0200 Subject: [PATCH 06/26] FEATURE: add fusion migration "node.identifier" => "node.nodeAggregateId.value" --- .../Rules/FusionNodeIdentifierRector.php | 33 +++++++++++++++++ .../Fixture/some_file.fusion.inc | 36 +++++++++++++++++++ .../FusionNodeIdentifierRectorTest.php | 31 ++++++++++++++++ .../config/configured_rule.php | 19 ++++++++++ 4 files changed, 119 insertions(+) create mode 100644 src/ContentRepository90/Rules/FusionNodeIdentifierRector.php create mode 100644 tests/ContentRepository90/Rules/FusionNodeIdentifierRector/Fixture/some_file.fusion.inc create mode 100644 tests/ContentRepository90/Rules/FusionNodeIdentifierRector/FusionNodeIdentifierRectorTest.php create mode 100644 tests/ContentRepository90/Rules/FusionNodeIdentifierRector/config/configured_rule.php diff --git a/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php b/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php new file mode 100644 index 0000000..d8008eb --- /dev/null +++ b/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php @@ -0,0 +1,33 @@ +process(fn(string $eelExpression) => preg_replace( + '/(node|documentNode|site)\.identifier/', + '$1.nodeAggregateId.value', + $eelExpression + )) + ->addCommentsIfRegexMatches( + '/\.identifier/', + '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.identifier" to VARIABLE.nodeAggregateId.value. We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + )->getProcessedContent(); + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/Fixture/some_file.fusion.inc new file mode 100644 index 0000000..eeb7ca9 --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/Fixture/some_file.fusion.inc @@ -0,0 +1,36 @@ +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${node.identifier || documentNode.identifier} + renderer = afx` + + ` + } +} +----- +// TODO 9.0 migration: Line 13: You may need to rewrite "VARIABLE.identifier" to VARIABLE.nodeAggregateId.value. We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${node.nodeAggregateId.value || documentNode.nodeAggregateId.value} + renderer = afx` + + ` + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/FusionNodeIdentifierRectorTest.php b/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/FusionNodeIdentifierRectorTest.php new file mode 100644 index 0000000..39e608f --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/FusionNodeIdentifierRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/config/configured_rule.php b/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/config/configured_rule.php new file mode 100644 index 0000000..8d7b0f8 --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeIdentifierRector/config/configured_rule.php @@ -0,0 +1,19 @@ +services(); + $services->defaults() + ->public() + ->autowire() + ->autoconfigure(); + $services->set(FusionFileProcessor::class); + $rectorConfig->disableParallel(); // does not work for fusion files - see https://github.com/rectorphp/rector-src/pull/2597#issuecomment-1190120688 + + $rectorConfig->rule(FusionNodeIdentifierRector::class); +}; From e8e39394e1db133b65735a92f4541ddddfc0fca3 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Fri, 14 Apr 2023 22:14:21 +0200 Subject: [PATCH 07/26] TASK: activate fusion migration "node.identifier" => "node.nodeAggregateId.value" --- config/set/contentrepository-90.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 06c9c03..e272d9d 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -10,6 +10,7 @@ use Neos\Rector\ContentRepository90\Rules\FusionContextLiveRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeDepthRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenInIndexRector; +use Neos\Rector\ContentRepository90\Rules\FusionNodeIdentifierRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeParentRector; use Neos\Rector\ContentRepository90\Rules\FusionNodePathRector; use Neos\Rector\ContentRepository90\Rules\InjectContentRepositoryRegistryIfNeededRector; @@ -135,6 +136,7 @@ $methodCallToWarningComments[] = new MethodCallToWarningComment(Node::class, 'getWorkspace', '!! Node::getWorkspace() does not make sense anymore concept-wise. In Neos < 9, it pointed to the workspace where the node was *at home at*. Now, the closest we have here is the node identity.'); // getIdentifier $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(Node::class, 'getIdentifier', 'nodeAggregateId'); + $rectorConfig->rule(FusionNodeIdentifierRector::class); // setIndex -> internal $methodCallToWarningComments[] = new MethodCallToWarningComment(Node::class, 'setIndex', '!! Node::setIndex() was always internal. To reorder nodes, use the "MoveNodeAggregate" command'); // getIndex From 56a625730844effc03f26b095b287d0d1116c6d8 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Fri, 14 Apr 2023 22:19:05 +0200 Subject: [PATCH 08/26] FEATURE: write fusion migration "node.nodeAggregateIdentifier" => "node.nodeAggregateId.value" --- config/set/contentrepository-90.php | 3 +- .../FusionNodeAggregateIdentifierRector.php | 33 +++++++++++++++++ .../Fixture/some_file.fusion.inc | 36 +++++++++++++++++++ ...usionNodeAggregateIdentifierRectorTest.php | 31 ++++++++++++++++ .../config/configured_rule.php | 19 ++++++++++ 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php create mode 100644 tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/Fixture/some_file.fusion.inc create mode 100644 tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/FusionNodeAggregateIdentifierRectorTest.php create mode 100644 tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index e272d9d..d65f95b 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -8,6 +8,7 @@ use Neos\Rector\ContentRepository90\Rules\ContextGetRootNodeRector; use Neos\Rector\ContentRepository90\Rules\FusionContextInBackendRector; use Neos\Rector\ContentRepository90\Rules\FusionContextLiveRector; +use Neos\Rector\ContentRepository90\Rules\FusionNodeAggregateIdentifierRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeDepthRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenInIndexRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeIdentifierRector; @@ -193,7 +194,7 @@ // getContentStreamIdentifier() -> threw exception in <= Neos 8.0 - so nobody could have used this // getNodeAggregateIdentifier() $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(Node::class, 'getNodeAggregateIdentifier', 'nodeAggregateId'); - // TODO: Fusion + $rectorConfig->rule(rectorClass: FusionNodeAggregateIdentifierRector::class); // getNodeTypeName() $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(Node::class, 'getNodeTypeName', 'nodeTypeName'); // getNodeType() ** (included/compatible in old NodeInterface) diff --git a/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php b/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php new file mode 100644 index 0000000..65f8986 --- /dev/null +++ b/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php @@ -0,0 +1,33 @@ +process(fn(string $eelExpression) => preg_replace( + '/(node|documentNode|site)\.nodeAggregateIdentifier/', + '$1.nodeAggregateId.value', + $eelExpression + )) + ->addCommentsIfRegexMatches( + '/\.nodeAggregateIdentifier/', + '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.nodeAggregateIdentifier" to VARIABLE.nodeAggregateId.value. We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + )->getProcessedContent(); + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/Fixture/some_file.fusion.inc new file mode 100644 index 0000000..b53f319 --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/Fixture/some_file.fusion.inc @@ -0,0 +1,36 @@ +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${node.nodeAggregateIdentifier || documentNode.nodeAggregateIdentifier} + renderer = afx` + + ` + } +} +----- +// TODO 9.0 migration: Line 13: You may need to rewrite "VARIABLE.nodeAggregateIdentifier" to VARIABLE.nodeAggregateId.value. We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${node.nodeAggregateId.value || documentNode.nodeAggregateId.value} + renderer = afx` + + ` + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/FusionNodeAggregateIdentifierRectorTest.php b/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/FusionNodeAggregateIdentifierRectorTest.php new file mode 100644 index 0000000..389e4cf --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/FusionNodeAggregateIdentifierRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/config/configured_rule.php b/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/config/configured_rule.php new file mode 100644 index 0000000..afbf8eb --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector/config/configured_rule.php @@ -0,0 +1,19 @@ +services(); + $services->defaults() + ->public() + ->autowire() + ->autoconfigure(); + $services->set(FusionFileProcessor::class); + $rectorConfig->disableParallel(); // does not work for fusion files - see https://github.com/rectorphp/rector-src/pull/2597#issuecomment-1190120688 + + $rectorConfig->rule(FusionNodeAggregateIdentifierRector::class); +}; From cc38b9a1abcd3a5789ecf8696c49e73e4a96da76 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Sat, 15 Apr 2023 07:31:53 +0200 Subject: [PATCH 09/26] FEATURE: rewrite nodes to Neos.Caching.entryIdentifierForNode in @cache.entryIdentifier --- config/set/contentrepository-90.php | 5 +++ ...sionCachingNodeInEntryIdentifierRector.php | 33 +++++++++++++++ .../EelExpressionTransformer.php | 8 ++-- .../Helper/CustomObjectTreeParser.php | 14 +++++-- .../EelExpressionPathBuilderVisitor.php | 36 ++++++++++++++++ .../Helper/EelExpressionPosition.php | 17 ++++++-- .../Helper/EelExpressionPositions.php | 11 +++++ .../FusionProcessing/Helper/FusionPath.php | 42 +++++++++++++++++++ .../Fixture/some_file.fusion.inc | 31 ++++++++++++++ ...CachingNodeInEntryIdentifierRectorTest.php | 31 ++++++++++++++ .../config/configured_rule.php | 19 +++++++++ 11 files changed, 238 insertions(+), 9 deletions(-) create mode 100644 src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php create mode 100644 src/Core/FusionProcessing/Helper/EelExpressionPathBuilderVisitor.php create mode 100644 src/Core/FusionProcessing/Helper/FusionPath.php create mode 100644 tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/Fixture/some_file.fusion.inc create mode 100644 tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/FusionCachingNodeInEntryIdentifierRectorTest.php create mode 100644 tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index d65f95b..8ca6eb6 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -6,6 +6,7 @@ use Neos\Rector\ContentRepository90\Rules\ContextFactoryToLegacyContextStubRector; use Neos\Rector\ContentRepository90\Rules\ContextGetFirstLevelNodeCacheRector; use Neos\Rector\ContentRepository90\Rules\ContextGetRootNodeRector; +use Neos\Rector\ContentRepository90\Rules\FusionCachingNodeInEntryIdentifierRector; use Neos\Rector\ContentRepository90\Rules\FusionContextInBackendRector; use Neos\Rector\ContentRepository90\Rules\FusionContextLiveRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeAggregateIdentifierRector; @@ -291,6 +292,10 @@ */ $rectorConfig->rule(WorkspaceRepositoryCountByNameRector::class); + /** + * SPECIAL rules + */ + $rectorConfig->rule(FusionCachingNodeInEntryIdentifierRector::class); /** * CLEAN UP / END GLOBAL RULES */ diff --git a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php new file mode 100644 index 0000000..22d6960 --- /dev/null +++ b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php @@ -0,0 +1,33 @@ +process(function (string $eelExpression, FusionPath $path) { + if (!$path->containsSegments('__meta', 'cache', 'entryIdentifier')) { + return $eelExpression; + } + return preg_replace( + '/(node|documentNode|site)/', + 'Neos.Caching.entryIdentifierForNode($1)', + $eelExpression + ); + })->getProcessedContent(); + } +} diff --git a/src/Core/FusionProcessing/EelExpressionTransformer.php b/src/Core/FusionProcessing/EelExpressionTransformer.php index c6ab270..d16fc19 100644 --- a/src/Core/FusionProcessing/EelExpressionTransformer.php +++ b/src/Core/FusionProcessing/EelExpressionTransformer.php @@ -36,7 +36,7 @@ public function process(Closure $processingFunction): self // apply processing function on Eel expressions $eelExpressions = $eelExpressions->map( fn(EelExpressionPosition $expressionPosition) => $expressionPosition->withEelExpression( - $processingFunction($expressionPosition->eelExpression) + $processingFunction($expressionPosition->eelExpression, $expressionPosition->fusionPath) ) ); @@ -183,7 +183,8 @@ private static function findEelExpressionsInAfxAstElement(array $afxElement, arr $result[] = new EelExpressionPosition( $afxElement['payload']['payload']['contents'], $afxElement['payload']['payload']['from'] + 1, - $afxElement['payload']['payload']['to'] + 1 + $afxElement['payload']['payload']['to'] + 1, + null ); return; } @@ -193,7 +194,8 @@ private static function findEelExpressionsInAfxAstElement(array $afxElement, arr $result[] = new EelExpressionPosition( $afxElement['payload']['contents'], $afxElement['payload']['from'] + 1, - $afxElement['payload']['to'] + 1 + $afxElement['payload']['to'] + 1, + null ); } } diff --git a/src/Core/FusionProcessing/Helper/CustomObjectTreeParser.php b/src/Core/FusionProcessing/Helper/CustomObjectTreeParser.php index 413ea04..2674ebf 100644 --- a/src/Core/FusionProcessing/Helper/CustomObjectTreeParser.php +++ b/src/Core/FusionProcessing/Helper/CustomObjectTreeParser.php @@ -28,8 +28,16 @@ public static function findEelExpressions(string $sourceCode, ?string $contextPa { $lexer = new Lexer($sourceCode); $parser = new self($lexer, $contextPathAndFilename); - $parser->parseFusionFile(); - return EelExpressionPositions::fromArray($parser->foundEelExpressions); + $fusionFile = $parser->parseFusionFile(); + $eelExpressionPositions = EelExpressionPositions::fromArray($parser->foundEelExpressions); + + // enrich $eelExpressionPositions by filling fusionPath -> needed for some context sensitive transformations + $eelExpressionPathBuilder = new EelExpressionPathBuilderVisitor( + $eelExpressionPositions + ); + $fusionFile->visit($eelExpressionPathBuilder); + + return $eelExpressionPositions; } /** @@ -53,7 +61,7 @@ protected function parsePathValue(): AbstractPathValue $result = parent::parsePathValue(); if ($result instanceof EelExpressionValue) { $toOffset = $this->lexer->getCursor(); - $this->foundEelExpressions[] = new EelExpressionPosition($result->value, $fromOffset + 2, $toOffset - 1); + $this->foundEelExpressions[] = new EelExpressionPosition($result->value, $fromOffset + 2, $toOffset - 1, $result); } return $result; } diff --git a/src/Core/FusionProcessing/Helper/EelExpressionPathBuilderVisitor.php b/src/Core/FusionProcessing/Helper/EelExpressionPathBuilderVisitor.php new file mode 100644 index 0000000..61908ea --- /dev/null +++ b/src/Core/FusionProcessing/Helper/EelExpressionPathBuilderVisitor.php @@ -0,0 +1,36 @@ + false, + fn() => [], + ); + } + + public function visitValueAssignment(ValueAssignment $valueAssignment, array $currentPath = null) + { + $currentPath ?? throw new \BadMethodCallException('$currentPath is required.'); + + // send currentPath to eel expression value + $valueAssignment->pathValue->visit($this, $currentPath); + } + + public function visitEelExpressionValue(EelExpressionValue $eelExpressionValue, array $currentPath = null) + { + $eelExpressionPosition = $this->eelExpressionPositions->byEelExpressionValue($eelExpressionValue); + if ($eelExpressionPosition) { + $eelExpressionPosition->fusionPath = FusionPath::create($currentPath); + } + } +} diff --git a/src/Core/FusionProcessing/Helper/EelExpressionPosition.php b/src/Core/FusionProcessing/Helper/EelExpressionPosition.php index c01e439..a2fdfdb 100644 --- a/src/Core/FusionProcessing/Helper/EelExpressionPosition.php +++ b/src/Core/FusionProcessing/Helper/EelExpressionPosition.php @@ -2,13 +2,22 @@ namespace Neos\Rector\Core\FusionProcessing\Helper; +use Neos\Rector\Core\FusionProcessing\FusionParser\Ast\EelExpressionValue; + final class EelExpressionPosition { + /** + * the fusion path leading to this eel expression. Not always filled (e.g. not in AFX). + */ + public FusionPath $fusionPath; + public function __construct( public readonly string $eelExpression, public readonly int $fromOffset, - public readonly int $toOffset + public readonly int $toOffset, + public readonly ?EelExpressionValue $eelExpressionValue = null ) { + $this->fusionPath = FusionPath::createEmpty(); } public function withOffset(int $offset): self @@ -16,7 +25,8 @@ public function withOffset(int $offset): self return new self( $this->eelExpression, $this->fromOffset + $offset, - $this->toOffset + $offset + $this->toOffset + $offset, + $this->eelExpressionValue ); } @@ -25,7 +35,8 @@ public function withEelExpression(string $eelExpression) return new self( $eelExpression, $this->fromOffset, - $this->toOffset + $this->toOffset, + $this->eelExpressionValue ); } } diff --git a/src/Core/FusionProcessing/Helper/EelExpressionPositions.php b/src/Core/FusionProcessing/Helper/EelExpressionPositions.php index ec765f1..35f0693 100644 --- a/src/Core/FusionProcessing/Helper/EelExpressionPositions.php +++ b/src/Core/FusionProcessing/Helper/EelExpressionPositions.php @@ -2,6 +2,7 @@ namespace Neos\Rector\Core\FusionProcessing\Helper; +use Neos\Rector\Core\FusionProcessing\FusionParser\Ast\EelExpressionValue; use Traversable; final class EelExpressionPositions implements \IteratorAggregate @@ -64,4 +65,14 @@ public function withoutFirst(): self array_shift($elements); return new self(...$elements); } + + public function byEelExpressionValue(EelExpressionValue $eelExpressionValue): ?EelExpressionPosition + { + foreach ($this->elements as $element) { + if ($element->eelExpressionValue === $eelExpressionValue) { + return $element; + } + } + return null; + } } diff --git a/src/Core/FusionProcessing/Helper/FusionPath.php b/src/Core/FusionProcessing/Helper/FusionPath.php new file mode 100644 index 0000000..bc76d4b --- /dev/null +++ b/src/Core/FusionProcessing/Helper/FusionPath.php @@ -0,0 +1,42 @@ +fusionPath) === 6 + // f.e. count($expectedSegments) === 3 + // $this->fusionPath 0 | 1 | 2 | 3 | 4 | 5 + // $expectedSegments ---------- + // $expectedSegments ----------- + // $expectedSegments ----------- + // $expectedSegments ---------- + for ($startOffset = 0; $startOffset <= count($this->fusionPath) - count($expectedSegments); $startOffset++) { + if (array_slice($this->fusionPath, $startOffset, count($expectedSegments)) === $expectedSegments) { + return true; + } + } + + return false; + } +} diff --git a/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/Fixture/some_file.fusion.inc new file mode 100644 index 0000000..65cb70d --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/Fixture/some_file.fusion.inc @@ -0,0 +1,31 @@ +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + @cache { + entryIdentifier { + foo = ${node} + } + } + @cache.entryIdentifier.foo2 = ${documentNode} + @cache { + entryIdentifier.foo3 = ${site} + entryIdentifier.foo4 = ${someOtherObject} + } + } +} +----- +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + @cache { + entryIdentifier { + foo = ${Neos.Caching.entryIdentifierForNode(node)} + } + } + @cache.entryIdentifier.foo2 = ${Neos.Caching.entryIdentifierForNode(documentNode)} + @cache { + entryIdentifier.foo3 = ${Neos.Caching.entryIdentifierForNode(site)} + entryIdentifier.foo4 = ${someOtherObject} + } + } +} diff --git a/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/FusionCachingNodeInEntryIdentifierRectorTest.php b/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/FusionCachingNodeInEntryIdentifierRectorTest.php new file mode 100644 index 0000000..a6d462a --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/FusionCachingNodeInEntryIdentifierRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/config/configured_rule.php b/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/config/configured_rule.php new file mode 100644 index 0000000..cb077c7 --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/config/configured_rule.php @@ -0,0 +1,19 @@ +services(); + $services->defaults() + ->public() + ->autowire() + ->autoconfigure(); + $services->set(FusionFileProcessor::class); + $rectorConfig->disableParallel(); // does not work for fusion files - see https://github.com/rectorphp/rector-src/pull/2597#issuecomment-1190120688 + + $rectorConfig->rule(FusionCachingNodeInEntryIdentifierRector::class); +}; From dc07f7a0c43ddfd3fb746a04652a828cb893bc10 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Sat, 15 Apr 2023 07:58:11 +0200 Subject: [PATCH 10/26] FEATURE: rewrite node.context.currentSite to Neos.Site.findBySiteNode(site) --- config/set/contentrepository-90.php | 2 ++ .../Rules/FusionContextCurrentSiteRector.php | 27 ++++++++++++++++ .../Fixture/some_file.fusion.inc | 25 +++++++++++++++ .../FusionContextCurrentSiteRectorTest.php | 31 +++++++++++++++++++ .../config/configured_rule.php | 17 ++++++++++ 5 files changed, 102 insertions(+) create mode 100644 src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php create mode 100644 tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/Fixture/some_file.fusion.inc create mode 100644 tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/FusionContextCurrentSiteRectorTest.php create mode 100644 tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 8ca6eb6..83fe0fb 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -7,6 +7,7 @@ use Neos\Rector\ContentRepository90\Rules\ContextGetFirstLevelNodeCacheRector; use Neos\Rector\ContentRepository90\Rules\ContextGetRootNodeRector; use Neos\Rector\ContentRepository90\Rules\FusionCachingNodeInEntryIdentifierRector; +use Neos\Rector\ContentRepository90\Rules\FusionContextCurrentSiteRector; use Neos\Rector\ContentRepository90\Rules\FusionContextInBackendRector; use Neos\Rector\ContentRepository90\Rules\FusionContextLiveRector; use Neos\Rector\ContentRepository90\Rules\FusionNodeAggregateIdentifierRector; @@ -256,6 +257,7 @@ */ // ContentContext::getCurrentSite // TODO: PHP + $rectorConfig->rule(FusionContextCurrentSiteRector::class); // TODO: Fusion // ContentContext::getCurrentDomain // TODO: PHP diff --git a/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php b/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php new file mode 100644 index 0000000..3ae14d3 --- /dev/null +++ b/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php @@ -0,0 +1,27 @@ +process(fn(string $eelExpression) => preg_replace( + '/(node|documentNode|site|[a-zA-Z]+)\.context\.currentSite/', + 'Neos.Site.findBySiteNode(site)', + $eelExpression + ))->getProcessedContent(); + } +} diff --git a/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/Fixture/some_file.fusion.inc new file mode 100644 index 0000000..51f905b --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/Fixture/some_file.fusion.inc @@ -0,0 +1,25 @@ +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + renderer = Neos.Fusion:Component { + attributes = ${node.context.currentSite.siteResourcesPackageKey || site.context.currentSite.siteResourcesPackageKey || documentNode.context.currentSite.siteResourcesPackageKey} + renderer = afx` + + ` + } +} +----- +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + renderer = Neos.Fusion:Component { + attributes = ${Neos.Site.findBySiteNode(site).siteResourcesPackageKey || Neos.Site.findBySiteNode(site).siteResourcesPackageKey || Neos.Site.findBySiteNode(site).siteResourcesPackageKey} + renderer = afx` + + ` + } +} diff --git a/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/FusionContextCurrentSiteRectorTest.php b/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/FusionContextCurrentSiteRectorTest.php new file mode 100644 index 0000000..3d59d75 --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/FusionContextCurrentSiteRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.fusion.inc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/config/configured_rule.php b/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/config/configured_rule.php new file mode 100644 index 0000000..2e2243d --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionContextCurrentSiteRector/config/configured_rule.php @@ -0,0 +1,17 @@ +services(); + $services->defaults() + ->public() + ->autowire() + ->autoconfigure(); + $services->set(\Neos\Rector\Core\FusionProcessing\FusionFileProcessor::class); + $rectorConfig->disableParallel(); // does not work for fusion files - see https://github.com/rectorphp/rector-src/pull/2597#issuecomment-1190120688 + + $rectorConfig->rule(FusionContextCurrentSiteRector::class); +}; From b52daff365de77341780f1253d1adfdf76108d4a Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Wed, 19 Apr 2023 15:24:08 +0200 Subject: [PATCH 11/26] FEATURE: auto-remove references to Neos\ContentRepository\Migration\Transformations\AbstractTransformation --- config/set/contentrepository-90.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 8ca6eb6..b687189 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -296,6 +296,10 @@ * SPECIAL rules */ $rectorConfig->rule(FusionCachingNodeInEntryIdentifierRector::class); + $rectorConfig->ruleWithConfiguration(RemoveParentRector::class, [ + // TODO: add comment about CR transformations -> what to use instead + 'Neos\ContentRepository\Migration\Transformations\AbstractTransformation', + ]); /** * CLEAN UP / END GLOBAL RULES */ From e5178e5b0e63c31f8a2d39942002d5559dabbf8e Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Wed, 19 Apr 2023 20:05:33 +0200 Subject: [PATCH 12/26] FEATURE: remove parent class rector with support for non-existing classes --- config/set/contentrepository-90.php | 11 ++- src/Generic/Rules/RemoveParentClassRector.php | 84 +++++++++++++++++++ src/Generic/ValueObject/RemoveParentClass.php | 14 ++++ .../Fixture/some_class.php.inc | 16 ++++ .../Fixture/some_class_with_alias.php.inc | 16 ++++ .../RemoveParentClassRectorTest.php | 31 +++++++ .../config/configured_rule.php | 12 +++ 7 files changed, 180 insertions(+), 4 deletions(-) create mode 100644 src/Generic/Rules/RemoveParentClassRector.php create mode 100644 src/Generic/ValueObject/RemoveParentClass.php create mode 100644 tests/Generic/Rules/RemoveParentClassRector/Fixture/some_class.php.inc create mode 100644 tests/Generic/Rules/RemoveParentClassRector/Fixture/some_class_with_alias.php.inc create mode 100644 tests/Generic/Rules/RemoveParentClassRector/RemoveParentClassRectorTest.php create mode 100644 tests/Generic/Rules/RemoveParentClassRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 5623a34..678d148 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -32,11 +32,11 @@ use Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryCountByNameRector; use Neos\Rector\Generic\Rules\FusionNodePropertyPathToWarningCommentRector; use Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector; -use Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector; use Neos\Rector\Generic\Rules\RemoveInjectionsRector; use Neos\Rector\Generic\ValueObject\FusionNodePropertyPathToWarningComment; use Neos\Rector\Generic\ValueObject\MethodCallToWarningComment; use Neos\Rector\Generic\ValueObject\RemoveInjection; +use Neos\Rector\Generic\ValueObject\RemoveParentClass; use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; @@ -298,10 +298,13 @@ * SPECIAL rules */ $rectorConfig->rule(FusionCachingNodeInEntryIdentifierRector::class); - $rectorConfig->ruleWithConfiguration(RemoveParentRector::class, [ - // TODO: add comment about CR transformations -> what to use instead - 'Neos\ContentRepository\Migration\Transformations\AbstractTransformation', + $rectorConfig->ruleWithConfiguration(\Neos\Rector\Generic\Rules\RemoveParentClassRector::class, [ + new RemoveParentClass( + parentClassName: Neos\ContentRepository\Migration\Transformations\AbstractTransformation::class, + comment: '// TODO 9.0 migration: You need to convert your AbstractTransformation to an implementation of Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface' + ) ]); + /** * CLEAN UP / END GLOBAL RULES */ diff --git a/src/Generic/Rules/RemoveParentClassRector.php b/src/Generic/Rules/RemoveParentClassRector.php new file mode 100644 index 0000000..0e95e36 --- /dev/null +++ b/src/Generic/Rules/RemoveParentClassRector.php @@ -0,0 +1,84 @@ +> + */ + public function getNodeTypes(): array + { + return [Node\Stmt\Class_::class]; + } + + /** + * @param \PhpParser\Node\Expr\MethodCall $node + */ + public function refactor(Node $node): ?Node + { + assert($node instanceof Node\Stmt\Class_); + foreach ($this->parentClassesToRemove as $parentClassToRemove) { + if ($node->extends === null) { + continue; + } + $objectType = $this->nodeTypeResolver->getType($node->extends); + assert($objectType instanceof ObjectType); + if ($objectType->getClassName() !== $parentClassToRemove->parentClassName) { + continue; + } + + // remove parent class + $node->extends = null; + $node->setAttribute('comments', [ + new Comment($parentClassToRemove->comment) + ]); + return $node; + } + return null; + } + + /** + * @param mixed[] $configuration + */ + public function configure(array $configuration): void + { + Assert::allIsAOf($configuration, RemoveParentClass::class); + $this->parentClassesToRemove = $configuration; + } +} diff --git a/src/Generic/ValueObject/RemoveParentClass.php b/src/Generic/ValueObject/RemoveParentClass.php new file mode 100644 index 0000000..1383a61 --- /dev/null +++ b/src/Generic/ValueObject/RemoveParentClass.php @@ -0,0 +1,14 @@ + +----- + diff --git a/tests/Generic/Rules/RemoveParentClassRector/Fixture/some_class_with_alias.php.inc b/tests/Generic/Rules/RemoveParentClassRector/Fixture/some_class_with_alias.php.inc new file mode 100644 index 0000000..a2723ca --- /dev/null +++ b/tests/Generic/Rules/RemoveParentClassRector/Fixture/some_class_with_alias.php.inc @@ -0,0 +1,16 @@ + +----- + diff --git a/tests/Generic/Rules/RemoveParentClassRector/RemoveParentClassRectorTest.php b/tests/Generic/Rules/RemoveParentClassRector/RemoveParentClassRectorTest.php new file mode 100644 index 0000000..32e3430 --- /dev/null +++ b/tests/Generic/Rules/RemoveParentClassRector/RemoveParentClassRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Generic/Rules/RemoveParentClassRector/config/configured_rule.php b/tests/Generic/Rules/RemoveParentClassRector/config/configured_rule.php new file mode 100644 index 0000000..20736b1 --- /dev/null +++ b/tests/Generic/Rules/RemoveParentClassRector/config/configured_rule.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(RemoveParentClassRector::class, [ + new RemoveParentClass(\Foo\Bar\Baz::class, '// TODO: Neos 9.0 Migration: Stuff') + ]); +}; From cddfaac37078d1aa93f9e5bc972fd07dc7762524 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Thu, 20 Apr 2023 09:01:43 +0200 Subject: [PATCH 13/26] FEATURE: rewrite dimension config with Rector --- composer.json | 4 +- config/set/contentrepository-90.php | 4 + .../Rules/YamlDimensionConfigRector.php | 115 ++++++++++++++++++ src/Core/YamlProcessing/YamlFileProcessor.php | 54 ++++++++ .../YamlProcessing/YamlRectorInterface.php | 12 ++ src/Core/YamlProcessing/YamlWithComments.php | 77 ++++++++++++ .../Fixture/NoDimensions.yaml.inc | 9 ++ .../Fixture/SingleDimension1.yaml.inc | 64 ++++++++++ .../Fixture/SingleDimension2.yaml.inc | 44 +++++++ .../Fixture/SingleDimension3.yaml.inc | 40 ++++++ .../YamlDimensionConfigRectorTest.php | 31 +++++ .../config/configured_rule.php | 16 +++ .../YamlProcessing/YamlWithCommentsTest.php | 67 ++++++++++ 13 files changed, 536 insertions(+), 1 deletion(-) create mode 100644 src/ContentRepository90/Rules/YamlDimensionConfigRector.php create mode 100644 src/Core/YamlProcessing/YamlFileProcessor.php create mode 100644 src/Core/YamlProcessing/YamlRectorInterface.php create mode 100644 src/Core/YamlProcessing/YamlWithComments.php create mode 100644 tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/NoDimensions.yaml.inc create mode 100644 tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension1.yaml.inc create mode 100644 tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension2.yaml.inc create mode 100644 tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension3.yaml.inc create mode 100644 tests/ContentRepository90/Rules/YamlDimensionConfigRector/YamlDimensionConfigRectorTest.php create mode 100644 tests/ContentRepository90/Rules/YamlDimensionConfigRector/config/configured_rule.php create mode 100644 tests/Core/YamlProcessing/YamlWithCommentsTest.php diff --git a/composer.json b/composer.json index 33ba0c0..acbdfdb 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,9 @@ "tests": "phpunit tests" }, "require": { - "rector/rector": "0.15.24" + "rector/rector": "0.15.24", + "symfony/yaml": "*", + "neos/utility-arrays": "*" }, "require-dev": { "phpunit/phpunit": "^9.5", diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 678d148..6e1bc7d 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -30,6 +30,7 @@ use Neos\Rector\ContentRepository90\Rules\NodeIsHiddenInIndexRector; use Neos\Rector\ContentRepository90\Rules\NodeIsHiddenRector; use Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryCountByNameRector; +use Neos\Rector\ContentRepository90\Rules\YamlDimensionConfigRector; use Neos\Rector\Generic\Rules\FusionNodePropertyPathToWarningCommentRector; use Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector; use Neos\Rector\Generic\Rules\RemoveInjectionsRector; @@ -50,6 +51,7 @@ ->autowire() ->autoconfigure(); $services->set(\Neos\Rector\Core\FusionProcessing\FusionFileProcessor::class); + $services->set(\Neos\Rector\Core\YamlProcessing\YamlFileProcessor::class); $rectorConfig->disableParallel(); // parallel does not work for non-PHP-Files, so we need to disable it - see https://github.com/rectorphp/rector-src/pull/2597#issuecomment-1190120688 @@ -305,6 +307,8 @@ ) ]); + $rectorConfig->rule(YamlDimensionConfigRector::class); + /** * CLEAN UP / END GLOBAL RULES */ diff --git a/src/ContentRepository90/Rules/YamlDimensionConfigRector.php b/src/ContentRepository90/Rules/YamlDimensionConfigRector.php new file mode 100644 index 0000000..5fe1db0 --- /dev/null +++ b/src/ContentRepository90/Rules/YamlDimensionConfigRector.php @@ -0,0 +1,115 @@ + we assume the file has already been processed. + return $fileContent; + } + + if (!isset($parsed['Neos']['ContentRepository']['contentDimensions'])) { + // we do not have a Neos.ContentRepository.contentDimensions key; so we do not need + // to process this file + return $fileContent; + } + + $defaultDimensionSpacePoint = []; + $uriPathSegments = []; + foreach ($parsed['Neos']['ContentRepository']['contentDimensions'] as $dimensionName => $oldDimensionConfig) { + $errors = []; + $uriPathSegmentsForDimension = [ + 'dimensionIdentifier' => $dimensionName, + 'dimensionValueMapping##' => YamlWithComments::comment('dimensionValue => uriPathSegment (empty uriPathSegment allowed)'), + 'dimensionValueMapping' => [] + ]; + $newContentDimensionConfig = []; + + if (isset($oldDimensionConfig['label'])) { + $newContentDimensionConfig['label'] = $oldDimensionConfig['label']; + } + if (isset($oldDimensionConfig['icon'])) { + $newContentDimensionConfig['icon'] = $oldDimensionConfig['icon']; + } + + if (isset($oldDimensionConfig['default'])) { + $defaultDimensionSpacePoint[$dimensionName] = $oldDimensionConfig['default']; + } else { + $errors[] = sprintf('TODO: FIXME: For preset "%s", did not find any default dimension value underneath "default". The defaultDimensionSpacePoint might be incomplete.', $presetName); + } + foreach ($oldDimensionConfig['presets'] as $presetName => $presetConfig) { + // we need to use the last dimension value as the the new dimension value name; because that is + // what the dimension migrator expects. + // + // The PresetName is discarded + // TODO: PresetName as comment + $dimensionValueConfig = []; + if (isset($presetConfig['label'])) { + $dimensionValueConfig['label'] = $presetConfig['label']; + } + if (isset($presetConfig['icon'])) { + $dimensionValueConfig['icon'] = $presetConfig['icon']; + } + + + if (!isset($presetConfig['values'])) { + $errors[] = sprintf('TODO: FIXME: For preset "%s", did not find any dimension values underneath "values"', $presetName); + } else { + $valuesExceptLast = $presetConfig['values']; + $valuesExceptLast = array_reverse($valuesExceptLast); + $lastValue = array_pop($valuesExceptLast); + $currentValuePath = &$newContentDimensionConfig['values']; + foreach ($valuesExceptLast as $value) { + $currentValuePath = &$currentValuePath[$value]['specializations']; + } + $currentValuePath[$lastValue] = $dimensionValueConfig; + + if (isset($presetConfig['uriSegment'])) { + $uriPathSegmentsForDimension['dimensionValueMapping'][$lastValue] = $presetConfig['uriSegment']; + } else { + $errors[] = sprintf('TODO: FIXME: For preset "%s", did not find any uriSegment.', $presetName); + } + } + } + + if ($errors) { + $parsed['Neos']['ContentRepositoryRegistry']['contentRepositories']['default']['contentDimensions'][$dimensionName . '##'] = YamlWithComments::comment(implode("\n", $errors)); + } + $parsed['Neos']['ContentRepositoryRegistry']['contentRepositories']['default']['contentDimensions'][$dimensionName] = $newContentDimensionConfig; + $uriPathSegments[] = $uriPathSegmentsForDimension; + } + $parsed['Neos']['ContentRepository']['contentDimensions'] = []; + $parsed = Arrays::removeEmptyElementsRecursively($parsed); + + $parsed['Neos']['Neos']['sites']['*']['contentDimensions'] = [ + 'defaultDimensionSpacePoint##' => YamlWithComments::comment('defaultDimensionSpacePoint is used for the homepage (URL /)'), + 'defaultDimensionSpacePoint' => $defaultDimensionSpacePoint, + 'resolver' => [ + 'factoryClassName' => 'Neos\Neos\FrontendRouting\DimensionResolution\Resolver\UriPathResolverFactory', + 'options' => [ + 'segments' => $uriPathSegments + ] + ] + ]; + + return YamlWithComments::dump($parsed); + } +} diff --git a/src/Core/YamlProcessing/YamlFileProcessor.php b/src/Core/YamlProcessing/YamlFileProcessor.php new file mode 100644 index 0000000..bdb9b73 --- /dev/null +++ b/src/Core/YamlProcessing/YamlFileProcessor.php @@ -0,0 +1,54 @@ +getFilePath(), '.yaml'); + } + + public function process(File $file, Configuration $configuration): array + { + $systemErrorsAndFileDiffs = [Bridge::SYSTEM_ERRORS => [], Bridge::FILE_DIFFS => []]; + if ($this->yamlRectors === []) { + return $systemErrorsAndFileDiffs; + } + $oldFileContent = $file->getFileContent(); + $newFileContent = $file->getFileContent(); + foreach ($this->yamlRectors as $yamlRector) { + $newFileContent = $yamlRector->refactorFileContent($file->getFileContent()); + if ($oldFileContent === $newFileContent) { + continue; + } + $file->changeFileContent($newFileContent); + } + if ($oldFileContent !== $newFileContent) { + $fileDiff = $this->fileDiffFactory->createFileDiff($file, $oldFileContent, $newFileContent); + $systemErrorsAndFileDiffs[Bridge::FILE_DIFFS][] = $fileDiff; + } + return $systemErrorsAndFileDiffs; + } + + public function getSupportedFileExtensions(): array + { + return ['yaml']; + } +} diff --git a/src/Core/YamlProcessing/YamlRectorInterface.php b/src/Core/YamlProcessing/YamlRectorInterface.php new file mode 100644 index 0000000..e29a662 --- /dev/null +++ b/src/Core/YamlProcessing/YamlRectorInterface.php @@ -0,0 +1,12 @@ + $value) { + if (is_string($key) && str_ends_with($key, '##')) { + $keysToSort[] = $key; + } + } + + foreach ($keysToSort as $key) { + // https://stackoverflow.com/a/38655962 + $new_element = [$key => $in[$key]]; + + // if needed, find the insertion index by key + $index = array_search(substr($key, 0, -2), array_keys($in)); + if ($index !== false) { + // add element at index (note the last array_slice argument) + $in = array_slice($in, 0, $index, true) + $new_element + array_slice($in, $index, null, true); + } + } + + foreach ($in as $key => $value) { + if (is_array($value)) { + $in[$key] = self::sort($value); + } + } + + return $in; + } +} diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/NoDimensions.yaml.inc b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/NoDimensions.yaml.inc new file mode 100644 index 0000000..c307762 --- /dev/null +++ b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/NoDimensions.yaml.inc @@ -0,0 +1,9 @@ +# some YAML with comments +Foo: + bar: true +baz: bla +----- +# some YAML with comments +Foo: + bar: true +baz: bla diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension1.yaml.inc b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension1.yaml.inc new file mode 100644 index 0000000..d7c24f4 --- /dev/null +++ b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension1.yaml.inc @@ -0,0 +1,64 @@ +# some YAML with comments +Neos: + ContentRepository: + contentDimensions: + language: + label: 'Neos.Demo:Main:contentDimensions.language' + icon: icon-language + default: en_US + defaultPreset: en_US + presets: + en_US: + label: 'English (US)' + values: + - en_US + # The default preset can also have an empty uriSegment value. + # https://docs.neos.io/cms/manual/content-repository/content-dimensions#behind-the-scenes-routing + uriSegment: en + en_UK: + label: 'English (UK)' + values: + - en_UK + - en_US + uriSegment: uk + de: + label: Deutsch + values: + - de + uriSegment: de + +----- +Neos: + ContentRepositoryRegistry: + contentRepositories: + default: + contentDimensions: + language: + label: 'Neos.Demo:Main:contentDimensions.language' + icon: icon-language + values: + en_US: + label: 'English (US)' + specializations: + en_UK: + label: 'English (UK)' + de: + label: Deutsch + Neos: + sites: + '*': + contentDimensions: + # defaultDimensionSpacePoint is used for the homepage (URL /) + defaultDimensionSpacePoint: + language: en_US + resolver: + factoryClassName: Neos\Neos\FrontendRouting\DimensionResolution\Resolver\UriPathResolverFactory + options: + segments: + - + dimensionIdentifier: language + # dimensionValue => uriPathSegment (empty uriPathSegment allowed) + dimensionValueMapping: + en_US: en + en_UK: uk + de: de diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension2.yaml.inc b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension2.yaml.inc new file mode 100644 index 0000000..86e8570 --- /dev/null +++ b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension2.yaml.inc @@ -0,0 +1,44 @@ +# some YAML with comments +Neos: + ContentRepository: + contentDimensions: + language: + label: 'Neos.Demo:Main:contentDimensions.language' + icon: icon-language + default: en_US + defaultPreset: en_US + presets: + en_US: + label: 'English (US)' + uriSegment: en + values: + - en_US + +----- +Neos: + ContentRepositoryRegistry: + contentRepositories: + default: + contentDimensions: + language: + label: 'Neos.Demo:Main:contentDimensions.language' + icon: icon-language + values: + en_US: + label: 'English (US)' + Neos: + sites: + '*': + contentDimensions: + # defaultDimensionSpacePoint is used for the homepage (URL /) + defaultDimensionSpacePoint: + language: en_US + resolver: + factoryClassName: Neos\Neos\FrontendRouting\DimensionResolution\Resolver\UriPathResolverFactory + options: + segments: + - + dimensionIdentifier: language + # dimensionValue => uriPathSegment (empty uriPathSegment allowed) + dimensionValueMapping: + en_US: en diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension3.yaml.inc b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension3.yaml.inc new file mode 100644 index 0000000..ec18575 --- /dev/null +++ b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/SingleDimension3.yaml.inc @@ -0,0 +1,40 @@ +# some YAML with comments +Neos: + ContentRepository: + contentDimensions: + language: + label: 'Neos.Demo:Main:contentDimensions.language' + icon: icon-language + default: en_US + defaultPreset: en_US + presets: + en_US: + label: 'English (US)' + uriSegment: en + # no values here, should produce error + +----- +Neos: + ContentRepositoryRegistry: + contentRepositories: + default: + contentDimensions: + # TODO: FIXME: For preset "en_US", did not find any dimension values underneath "values" + language: + label: 'Neos.Demo:Main:contentDimensions.language' + icon: icon-language + Neos: + sites: + '*': + contentDimensions: + # defaultDimensionSpacePoint is used for the homepage (URL /) + defaultDimensionSpacePoint: + language: en_US + resolver: + factoryClassName: Neos\Neos\FrontendRouting\DimensionResolution\Resolver\UriPathResolverFactory + options: + segments: + - + dimensionIdentifier: language + # dimensionValue => uriPathSegment (empty uriPathSegment allowed) + dimensionValueMapping: { } diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/YamlDimensionConfigRectorTest.php b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/YamlDimensionConfigRectorTest.php new file mode 100644 index 0000000..d4321ed --- /dev/null +++ b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/YamlDimensionConfigRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture', '*.yaml.inc'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/config/configured_rule.php b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/config/configured_rule.php new file mode 100644 index 0000000..ad8b9ed --- /dev/null +++ b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/config/configured_rule.php @@ -0,0 +1,16 @@ +services(); + $services->defaults() + ->public() + ->autowire() + ->autoconfigure(); + $services->set(\Neos\Rector\Core\YamlProcessing\YamlFileProcessor::class); + $rectorConfig->disableParallel(); // does not work for yaml files - see https://github.com/rectorphp/rector-src/pull/2597#issuecomment-1190120688 + + $rectorConfig->rule(Neos\Rector\ContentRepository90\Rules\YamlDimensionConfigRector::class); +}; diff --git a/tests/Core/YamlProcessing/YamlWithCommentsTest.php b/tests/Core/YamlProcessing/YamlWithCommentsTest.php new file mode 100644 index 0000000..de53274 --- /dev/null +++ b/tests/Core/YamlProcessing/YamlWithCommentsTest.php @@ -0,0 +1,67 @@ + [ + 'myValue' => 'foo', + 'isAbstract' => true, + 'isAbstract##' => YamlWithComments::comment( 'foo'), + 'myValue2' => 'bla', + ], + 'Neos.Neos:Foo##' => YamlWithComments::comment("My commentMy commentMy\ncommentMy commentMy commentMy commentMy commentMy commentMy commentMy\ncommentMy commentMy commentMy commentMy commentMy commentMy commentMy commentMy commentMy comment"), + ]; + + $expected = <<assertEquals(trim($expected), trim(YamlWithComments::dump($x))); + } + + /** + * @test + */ + public function yamlGenerationWithCommentsAndNumberKeys() + { + $x = [ + 'Neos.Neos:Foo' => [ + 'volumes' => [ + 'a', + 'b', + YamlWithComments::comment('Mein Kommentar'), + 'c' + ], + ], + ]; + + $expected = <<assertEquals(trim($expected), trim(YamlWithComments::dump($x))); + } +} From 158937878f91a01dde963b61921556359f22e009 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Thu, 20 Apr 2023 09:08:53 +0200 Subject: [PATCH 14/26] BUGFIX: make FusionCachingNodeInEntryIdentifierRector idempotent --- ...sionCachingNodeInEntryIdentifierRector.php | 2 +- .../Fixture/some_file2.fusion.inc | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector/Fixture/some_file2.fusion.inc diff --git a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php index 22d6960..57b0b94 100644 --- a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php +++ b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php @@ -24,7 +24,7 @@ public function refactorFileContent(string $fileContent): string return $eelExpression; } return preg_replace( - '/(node|documentNode|site)/', + '/(? Date: Thu, 20 Apr 2023 09:51:09 +0200 Subject: [PATCH 15/26] BUGFIX: fix FusionNodeDepthRector and FusionNodePathRector to use updated Eel expressions --- config/set/contentrepository-90.php | 2 +- .../Rules/FusionNodeDepthRector.php | 8 +-- .../Rules/FusionNodePathRector.php | 6 +- .../Fixture/some_file.fusion.inc | 8 +-- .../Fixture/some_file2.fusion.inc | 64 +++++++++++++++++++ .../Fixture/some_file.fusion.inc | 8 +-- 6 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file2.fusion.inc diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 6e1bc7d..f4dbb05 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -133,7 +133,7 @@ // - NodeAddress + LOG (WARNING) // getDepth $rectorConfig->rule(NodeGetDepthRector::class); - // Fusion: .depth -> Neos.NodeInfo.depth(node) + // Fusion: .depth -> Neos.Node.depth(node) $rectorConfig->rule(FusionNodeDepthRector::class); // setWorkspace -> internal $methodCallToWarningComments[] = new MethodCallToWarningComment(Node::class, 'setWorkspace', '!! Node::setWorkspace() was always internal, and the workspace system has been fundamentally changed with the new CR. Try to rewrite your code around Content Streams.'); diff --git a/src/ContentRepository90/Rules/FusionNodeDepthRector.php b/src/ContentRepository90/Rules/FusionNodeDepthRector.php index 42e031f..a10c968 100644 --- a/src/ContentRepository90/Rules/FusionNodeDepthRector.php +++ b/src/ContentRepository90/Rules/FusionNodeDepthRector.php @@ -14,20 +14,20 @@ class FusionNodeDepthRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.depth to Neos.NodeInfo.depth(node)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.depth to Neos.Node.depth(node)', __CLASS__, 'some_class.fusion'); } public function refactorFileContent(string $fileContent): string { return EelExpressionTransformer::parse($fileContent) ->process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode)\.depth/', - 'Neos.NodeInfo.depth($1)', + '/([a-zA-Z.]+)?(node|documentNode)\.depth/', + 'Neos.Node.depth($1$2)', $eelExpression )) ->addCommentsIfRegexMatches( '/\.depth$/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.depth" to Neos.NodeInfo.depth(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.depth" to Neos.Node.depth(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' )->getProcessedContent(); } } diff --git a/src/ContentRepository90/Rules/FusionNodePathRector.php b/src/ContentRepository90/Rules/FusionNodePathRector.php index a23ffb3..854d114 100644 --- a/src/ContentRepository90/Rules/FusionNodePathRector.php +++ b/src/ContentRepository90/Rules/FusionNodePathRector.php @@ -14,7 +14,7 @@ class FusionNodePathRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.path to Neos.NodeInfo.path(node)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.path to Neos.Node.path(node)', __CLASS__, 'some_class.fusion'); } public function refactorFileContent(string $fileContent): string @@ -22,12 +22,12 @@ public function refactorFileContent(string $fileContent): string return EelExpressionTransformer::parse($fileContent) ->process(fn(string $eelExpression) => preg_replace( '/(node|documentNode|site)\.path/', - 'Neos.NodeInfo.path($1)', + 'Neos.Node.path($1)', $eelExpression )) ->addCommentsIfRegexMatches( '/\.path$/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.path" to Neos.NodeInfo.path(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.path" to Neos.Node.path(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' )->getProcessedContent(); } } diff --git a/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion.inc index cc4aa9a..a1347fd 100644 --- a/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion.inc +++ b/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file.fusion.inc @@ -30,7 +30,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie } } ----- -// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.depth" to Neos.NodeInfo.depth(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.depth" to Neos.Node.depth(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { renderer = Neos.Fusion:Component { @@ -38,7 +38,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie # # pass down props # - attributes = ${Neos.NodeInfo.depth(node) || Neos.NodeInfo.depth(documentNode)} + attributes = ${Neos.Node.depth(node) || Neos.Node.depth(documentNode)} # # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` @@ -54,10 +54,10 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie renderer = afx` ` } diff --git a/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file2.fusion.inc b/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file2.fusion.inc new file mode 100644 index 0000000..fd72ce2 --- /dev/null +++ b/tests/ContentRepository90/Rules/FusionNodeDepthRector/Fixture/some_file2.fusion.inc @@ -0,0 +1,64 @@ +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${this.node.depth || this.documentNode.depth} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } +} +----- +// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.depth" to Neos.Node.depth(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${Neos.Node.depth(this.node) || Neos.Node.depth(this.documentNode)} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } +} diff --git a/tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion.inc index f959e5e..d6db00e 100644 --- a/tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion.inc +++ b/tests/ContentRepository90/Rules/FusionNodePathRector/Fixture/some_file.fusion.inc @@ -30,7 +30,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie } } ----- -// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.path" to Neos.NodeInfo.path(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.path" to Neos.Node.path(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { renderer = Neos.Fusion:Component { @@ -38,7 +38,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie # # pass down props # - attributes = ${Neos.NodeInfo.path(node) || Neos.NodeInfo.path(documentNode)} + attributes = ${Neos.Node.path(node) || Neos.Node.path(documentNode)} # # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` @@ -54,10 +54,10 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie renderer = afx` ` } From adf919216c569d4b29df27703e1f8f530e243b32 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Sun, 23 Apr 2023 09:00:42 +0200 Subject: [PATCH 16/26] FEATURE: generate docs --- README.md | 5 + composer.json | 5 +- docs/rules_overview.md | 1166 ++++++++++++++++- ...sionCachingNodeInEntryIdentifierRector.php | 2 +- .../Rules/FusionContextCurrentSiteRector.php | 2 +- .../Rules/FusionContextInBackendRector.php | 2 +- .../Rules/FusionContextLiveRector.php | 2 +- .../FusionNodeAggregateIdentifierRector.php | 2 +- .../Rules/FusionNodeDepthRector.php | 2 +- .../Rules/FusionNodeHiddenInIndexRector.php | 2 +- .../Rules/FusionNodeIdentifierRector.php | 2 +- .../Rules/FusionNodeParentRector.php | 2 +- .../Rules/FusionNodePathRector.php | 2 +- .../Rules/YamlDimensionConfigRector.php | 2 +- ...NodePropertyPathToWarningCommentRector.php | 7 +- .../MethodCallToWarningCommentRector.php | 4 +- src/Generic/Rules/RemoveInjectionsRector.php | 4 +- src/Generic/Rules/RemoveParentClassRector.php | 4 +- src/Utility/CodeSampleLoader.php | 27 +- ...ions.yaml.inc => zz_NoDimensions.yaml.inc} | 0 .../Fixture/some_class.php.inc | 18 + .../RemoveDuplicateCommentRector.php | 31 + .../config/configured_rule.php | 8 + 23 files changed, 1264 insertions(+), 37 deletions(-) rename tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/{NoDimensions.yaml.inc => zz_NoDimensions.yaml.inc} (100%) create mode 100644 tests/Generic/Rules/RemoveDuplicateCommentRector/Fixture/some_class.php.inc create mode 100644 tests/Generic/Rules/RemoveDuplicateCommentRector/RemoveDuplicateCommentRector.php create mode 100644 tests/Generic/Rules/RemoveDuplicateCommentRector/config/configured_rule.php diff --git a/README.md b/README.md index 9c83a46..958e8af 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,8 @@ git diff -- Neos.Fusion.Afx/ > ../Libraries/neos/rector/scripts/afx-eel-position git restore -- Neos.Fusion.Afx/ ``` +## Generating docs + +```bash +composer run generate-docs +``` diff --git a/composer.json b/composer.json index acbdfdb..486e9ff 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,7 @@ } }, "scripts": { - "generate-docs": "rule-doc-generator generate src/Rules --output-file docs/rules_overview.md", + "generate-docs": "rule-doc-generator generate src/ContentRepository90/Rules src/Generic/Rules", "tests": "phpunit tests" }, "require": { @@ -21,6 +21,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.5", - "webmozart/assert": "^1.11" + "webmozart/assert": "^1.11", + "symplify/rule-doc-generator": "^11.1" } } diff --git a/docs/rules_overview.md b/docs/rules_overview.md index 649716b..0a8a731 100644 --- a/docs/rules_overview.md +++ b/docs/rules_overview.md @@ -1,19 +1,632 @@ -# 4 Rules Overview +# 34 Rules Overview + +## ContentDimensionCombinatorGetAllAllowedCombinationsRector + +`"ContentDimensionCombinator::getAllAllowedCombinations()"` will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\ContentDimensionCombinatorGetAllAllowedCombinationsRector`](../src/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector.php) + +```diff + contentDimensionCombinator->getAllAllowedCombinations(); ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); ++ $dimensionSpacePoints = $contentRepository->getInterDimensionalVariationGraph()->getDimensionSpacePoints(); ++ // TODO 9.0 migration: try to directly work with $dimensionSpacePoints, instead of converting them to the legacy dimension format ++ ++ $combinations = array_map(fn(\Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint $dimensionSpacePoint) => $dimensionSpacePoint->toLegacyDimensionArray(), iterator_to_array($dimensionSpacePoints)); + foreach ($combinations as $combination) { + } + } + } + + ?> +``` + +
+ +## ContextFactoryToLegacyContextStubRector + +`"ContextFactory::create()"` will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\ContextFactoryToLegacyContextStubRector`](../src/ContentRepository90/Rules/ContextFactoryToLegacyContextStubRector.php) + +```diff + contextFactory->create([ ++ return new \Neos\Rector\ContentRepository90\Legacy\LegacyContextStub([ + 'workspace' => $workspace, + 'dimensions' => [ + 'language' => ['de_DE'] + ] + ]); + } + +- public function run2(): Neos\Neos\Domain\Service\ContentContext { ++ public function run2(): \Neos\Rector\ContentRepository90\Legacy\LegacyContextStub { + } + } + + ?> +``` + +
+ +## ContextGetFirstLevelNodeCacheRector + +`"Context::getFirstLevelNodeCache()"` will be removed. + +- class: [`Neos\Rector\ContentRepository90\Rules\ContextGetFirstLevelNodeCacheRector`](../src/ContentRepository90/Rules/ContextGetFirstLevelNodeCacheRector.php) + +```diff + getFirstLevelNodeCache()->reset(); +- $context->getFirstLevelNodeCache()->someOtherMethod(); + } + } + + ?> +``` + +
+ +## ContextGetRootNodeRector + +`"Context::getRootNode()"` will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\ContextGetRootNodeRector`](../src/ContentRepository90/Rules/ContextGetRootNodeRector.php) + +```diff + getRootNode(); ++ // TODO 9.0 migration: !! MEGA DIRTY CODE! Ensure to rewrite this; by getting rid of LegacyContextStub. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); ++ $workspace = $contentRepository->getWorkspaceFinder()->findOneByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($context->workspaceName ?? 'live')); ++ $rootNodeAggregate = $contentRepository->getContentGraph()->findRootNodeAggregateByType($workspace->currentContentStreamId, \Neos\ContentRepository\Core\NodeType\NodeTypeName::fromString('Neos.Neos:Sites')); ++ $subgraph = $contentRepository->getContentGraph()->getSubgraph($workspace->currentContentStreamId, \Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint::fromLegacyDimensionArray($context->dimensions ?? []), $context->invisibleContentShown ? \Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints::withoutRestrictions() : \Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints::frontend()); ++ return $subgraph->findNodeById($rootNodeAggregate->nodeAggregateId); + } + } + + ?> +``` + +
+ +## FusionCachingNodeInEntryIdentifierRector + +Fusion: Rewrite node to Neos.Caching.entryIdentifierForNode(...) in @cache.entryIdentifier segments + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionCachingNodeInEntryIdentifierRector`](../src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php) + +```diff + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + @cache { + entryIdentifier { +- foo = ${node} ++ foo = ${Neos.Caching.entryIdentifierForNode(node)} + } + } +- @cache.entryIdentifier.foo2 = ${documentNode} ++ @cache.entryIdentifier.foo2 = ${Neos.Caching.entryIdentifierForNode(documentNode)} + @cache { +- entryIdentifier.foo3 = ${site} ++ entryIdentifier.foo3 = ${Neos.Caching.entryIdentifierForNode(site)} + entryIdentifier.foo4 = ${someOtherObject} + } + } + } +``` + +
+ +## FusionContextCurrentSiteRector + +Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...) + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionContextCurrentSiteRector`](../src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php) + +```diff + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + renderer = Neos.Fusion:Component { +- attributes = ${node.context.currentSite.siteResourcesPackageKey || site.context.currentSite.siteResourcesPackageKey || documentNode.context.currentSite.siteResourcesPackageKey} ++ attributes = ${Neos.Site.findBySiteNode(site).siteResourcesPackageKey || Neos.Site.findBySiteNode(site).siteResourcesPackageKey || Neos.Site.findBySiteNode(site).siteResourcesPackageKey} + renderer = afx` + + ` + } + } +``` + +
+ +## FusionContextInBackendRector + +Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...) + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionContextInBackendRector`](../src/ContentRepository90/Rules/FusionContextInBackendRector.php) + +```diff ++// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Node.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.context.inBackend || site.context.inBackend || documentNode.context.inBackend} ++ attributes = ${Neos.Node.inBackend(node) || Neos.Node.inBackend(site) || Neos.Node.inBackend(documentNode)} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } + } +``` + +
+ +## FusionContextLiveRector + +Fusion: Rewrite node.context.live to Neos.Node.isLive(...) + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionContextLiveRector`](../src/ContentRepository90/Rules/FusionContextLiveRector.php) + +```diff ++// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.live" to Neos.Node.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.context.live || site.context.live || documentNode.context.live} ++ attributes = ${Neos.Node.isLive(node) || Neos.Node.isLive(site) || Neos.Node.isLive(documentNode)} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodeAggregateIdentifierRector + +Fusion: Rewrite node.nodeAggregateIdentifier to node.nodeAggregateId.value + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeAggregateIdentifierRector`](../src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php) + +```diff ++// TODO 9.0 migration: Line 13: You may need to rewrite "VARIABLE.nodeAggregateIdentifier" to VARIABLE.nodeAggregateId.value. We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.nodeAggregateIdentifier || documentNode.nodeAggregateIdentifier} ++ attributes = ${node.nodeAggregateId.value || documentNode.nodeAggregateId.value} + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodeDepthRector + +Fusion: Rewrite node.depth to Neos.Node.depth(node) + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeDepthRector`](../src/ContentRepository90/Rules/FusionNodeDepthRector.php) + +```diff ++// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.depth" to Neos.Node.depth(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.depth || documentNode.depth} ++ attributes = ${Neos.Node.depth(node) || Neos.Node.depth(documentNode)} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodeHiddenInIndexRector + +Fusion: Rewrite node.hiddenInIndex to node.properties._hiddenInIndex + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenInIndexRector`](../src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php) + +```diff ++// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.hiddenInIndex" to VARIABLE.properties._hiddenInIndex. We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.hiddenInIndex || documentNode.hiddenInIndex || site.hiddenInIndex} ++ attributes = ${node.properties._hiddenInIndex || documentNode.properties._hiddenInIndex || site.properties._hiddenInIndex} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodeIdentifierRector + +Fusion: Rewrite node.identifier to node.nodeAggregateId.value + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeIdentifierRector`](../src/ContentRepository90/Rules/FusionNodeIdentifierRector.php) + +```diff ++// TODO 9.0 migration: Line 13: You may need to rewrite "VARIABLE.identifier" to VARIABLE.nodeAggregateId.value. We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.identifier || documentNode.identifier} ++ attributes = ${node.nodeAggregateId.value || documentNode.nodeAggregateId.value} + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodeParentRector + +Fusion: Rewrite node.parent to Neos.NodeAccess.findParent(node) + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeParentRector`](../src/ContentRepository90/Rules/FusionNodeParentRector.php) + +```diff ++// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.parent" to Neos.NodeAccess.findParent(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.parent || documentNode.parent} ++ attributes = ${Neos.NodeAccess.findParent(node) || Neos.NodeAccess.findParent(documentNode)} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodePathRector + +Fusion: Rewrite node.path to Neos.Node.path(node) + +- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodePathRector`](../src/ContentRepository90/Rules/FusionNodePathRector.php) + +```diff ++// TODO 9.0 migration: Line 26: You may need to rewrite "VARIABLE.path" to Neos.Node.path(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # +- attributes = ${node.path || documentNode.path} ++ attributes = ${Neos.Node.path(node) || Neos.Node.path(documentNode)} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + ` + } + } +``` + +
+ +## FusionNodePropertyPathToWarningCommentRector + +Fusion: Adds a warning comment when the defined path is used within an Eel expression. + +:wrench: **configure it!** + +- class: [`Neos\Rector\Generic\Rules\FusionNodePropertyPathToWarningCommentRector`](../src/Generic/Rules/FusionNodePropertyPathToWarningCommentRector.php) + +```php +ruleWithConfiguration(FusionNodePropertyPathToWarningCommentRector::class, [ + new FusionNodePropertyPathToWarningComment('removed', 'Line %LINE: !! node.removed - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios.'), + new FusionNodePropertyPathToWarningComment('hiddenBeforeDateTime', 'Line %LINE: !! node.hiddenBeforeDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time.'), + new FusionNodePropertyPathToWarningComment('hiddenAfterDateTime', 'Line %LINE: !! node.hiddenAfterDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time.'), + new FusionNodePropertyPathToWarningComment('foo.bar', 'Line %LINE: !! node.foo.bar is not supported anymore.'), + ]); +}; +``` + +↓ + +```diff ++// TODO 9.0 migration: Line 20: !! node.removed - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios. ++// TODO 9.0 migration: Line 21: !! node.removed - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios. ++// TODO 9.0 migration: Line 42: !! node.removed - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios. ++// TODO 9.0 migration: Line 54: !! node.removed - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios. ++// TODO 9.0 migration: Line 20: !! node.hiddenBeforeDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 21: !! node.hiddenBeforeDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 46: !! node.hiddenBeforeDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 48: !! node.hiddenBeforeDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 22: !! node.hiddenAfterDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 40: !! node.hiddenAfterDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 52: !! node.hiddenAfterDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time. ++// TODO 9.0 migration: Line 23: !! node.foo.bar is not supported anymore. + prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { + + renderer = Neos.Fusion:Component { + + # + # pass down props + # + attributes = ${node.removed || site.removed || documentNode.hiddenBeforeDateTime} + attributes2 = ${node.hiddenBeforeDateTime || site.hiddenBeforeDateTime || documentNode.removed} + attributes3 = ${node.hiddenAfterDateTime || site.hiddenAfterDateTime || documentNode.hiddenAfterDateTime} + attributes4 = ${node.foo.bar} + attributes5 = ${node.fooXbar} + + # + # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` + # + checked = false + checked.@process.checkMultiValue = ${Array.indexOf(field.getCurrentMultivalueStringified(), field.getTargetValueStringified()) > -1} + checked.@process.checkMultiValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkMultiValue.@if.isMultiple = ${field.isMultiple()} + checked.@process.checkSingleValue = ${field.getCurrentValueStringified() == field.getTargetValueStringified()} + checked.@process.checkSingleValue.@if.hasValue = ${field.hasCurrentValue()} + checked.@process.checkSingleValue.@if.isSingle = ${!field.isMultiple()} + + renderer = afx` + + + + ` + } + } +``` + +
## InjectContentRepositoryRegistryIfNeededRector add injection for `$contentRepositoryRegistry` if in use. -- class: [`Neos\Rector\Rules\InjectContentRepositoryRegistryIfNeededRector`](../src/Rules/InjectContentRepositoryRegistryIfNeededRector.php) +- class: [`Neos\Rector\ContentRepository90\Rules\InjectContentRepositoryRegistryIfNeededRector`](../src/ContentRepository90/Rules/InjectContentRepositoryRegistryIfNeededRector.php) ```diff +## MethodCallToWarningCommentRector + +"Warning comments for various non-supported use cases + +:wrench: **configure it!** + +- class: [`Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector`](../src/Generic/Rules/MethodCallToWarningCommentRector.php) + +```php +ruleWithConfiguration(MethodCallToWarningCommentRector::class, [ + new MethodCallToWarningComment('PhpParser\Node', 'getWorkspace', '!! Node::getWorkspace() does not make sense anymore concept-wise. In Neos < 9, it pointed to the workspace where the node was *at home at*. Now, the closest we have here is the node identity.'), + ]); +}; +``` + +↓ + +```diff + getWorkspace(); + } + } + + ?> +``` + +
+ +## NodeFactoryResetRector + +`"NodeFactory::reset()"` will be removed. + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeFactoryResetRector`](../src/ContentRepository90/Rules/NodeFactoryResetRector.php) + +```diff + nodeFactory->reset(); + } + } + + ?> +``` + +
+ +## NodeFindParentNodeRector + +`"NodeInterface::findParentNode()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeFindParentNodeRector`](../src/ContentRepository90/Rules/NodeFindParentNodeRector.php) + +```diff + findParentNode(); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); ++ $parentNode = $subgraph->findParentNode($node->nodeAggregateId); + } + } + + ?> +``` + +
+ ## NodeGetChildNodesRector `"NodeInterface::getChildNodes()"` will be rewritten -- class: [`Neos\Rector\Rules\NodeGetChildNodesRector`](../src/Rules/NodeGetChildNodesRector.php) +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetChildNodesRector`](../src/ContentRepository90/Rules/NodeGetChildNodesRector.php) ```diff contentRepositoryRegistry->subgraphForNode($node); + // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. + -+ foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateIdentifier)) as $node) { ++ foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::all())) as $node) { } } } @@ -55,24 +769,182 @@ add injection for `$contentRepositoryRegistry` if in use.
+## NodeGetContextGetWorkspaceNameRector + +`"NodeInterface::getContext()::getWorkspace()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetContextGetWorkspaceNameRector`](../src/ContentRepository90/Rules/NodeGetContextGetWorkspaceNameRector.php) + +```diff + getContext()->getWorkspaceName(); ++ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); ++ return $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($node->subgraphIdentity->contentStreamId)->workspaceName; + } + } + + ?> +``` + +
+ ## NodeGetContextGetWorkspaceRector `"NodeInterface::getContext()::getWorkspace()"` will be rewritten -- class: [`Neos\Rector\Rules\NodeGetContextGetWorkspaceRector`](../src/Rules/NodeGetContextGetWorkspaceRector.php) +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetContextGetWorkspaceRector`](../src/ContentRepository90/Rules/NodeGetContextGetWorkspaceRector.php) ```diff getContext()->getWorkspace(); -+ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryIdentifier); -+ return $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamIdentifier($node->subgraphIdentity->contentStreamIdentifier); ++ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); ++ return $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($node->subgraphIdentity->contentStreamId); + } + } + + ?> +``` + +
+ +## NodeGetDepthRector + +`"NodeInterface::getDepth()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetDepthRector`](../src/ContentRepository90/Rules/NodeGetDepthRector.php) + +```diff + getDepth(); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); ++ return $subgraph->findNodePath($node->nodeAggregateId)->getDepth(); + } + } + + ?> +``` + +
+ +## NodeGetDimensionsRector + +`"NodeInterface::getChildNodes()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetDimensionsRector`](../src/ContentRepository90/Rules/NodeGetDimensionsRector.php) + +```diff + getDimensions(); ++ // TODO 9.0 migration: Try to remove the toLegacyDimensionArray() call and make your codebase more typesafe. ++ ++ return $node->originDimensionSpacePoint->toLegacyDimensionArray(); + } + } + + ?> +``` + +
+ +## NodeGetParentRector + +`"NodeInterface::getParent()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetParentRector`](../src/ContentRepository90/Rules/NodeGetParentRector.php) + +```diff + getParent(); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); ++ return $subgraph->findParentNode($node->nodeAggregateId); + } + } + + ?> +``` + +
+ +## NodeGetPathRector + +`"NodeInterface::getPath()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeGetPathRector`](../src/ContentRepository90/Rules/NodeGetPathRector.php) + +```diff + getPath(); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); ++ // TODO 9.0 migration: Try to remove the (string) cast and make your code more type-safe. ++ ++ return (string) $subgraph->findNodePath($node->nodeAggregateId); + } + } + + ?> +``` + +
+ +## NodeIsHiddenInIndexRector + +`"NodeInterface::isHiddenInIndex()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeIsHiddenInIndexRector`](../src/ContentRepository90/Rules/NodeIsHiddenInIndexRector.php) + +```diff + isHiddenInIndex(); ++ return $node->getProperty('_hiddenInIndex'); } } @@ -85,21 +957,21 @@ add injection for `$contentRepositoryRegistry` if in use. `"NodeInterface::isHidden()"` will be rewritten -- class: [`Neos\Rector\Rules\NodeIsHiddenRector`](../src/Rules/NodeIsHiddenRector.php) +- class: [`Neos\Rector\ContentRepository90\Rules\NodeIsHiddenRector`](../src/ContentRepository90/Rules/NodeIsHiddenRector.php) ```diff isHidden(); -+ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryIdentifier); -+ $nodeHiddenStateFinder = $contentRepository->getProjection(\Neos\ContentRepository\Projection\NodeHiddenState\NodeHiddenStateProjection::class); -+ $hiddenState = $nodeHiddenStateFinder->findHiddenState($node->subgraphIdentity->contentStreamIdentifier, $node->subgraphIdentity->dimensionSpacePoint, $node->nodeAggregateIdentifier); ++ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); ++ $nodeHiddenStateFinder = $contentRepository->projectionState(\Neos\ContentRepository\Core\Projection\NodeHiddenState\NodeHiddenStateFinder::class); ++ $hiddenState = $nodeHiddenStateFinder->findHiddenState($node->subgraphIdentity->contentStreamId, $node->subgraphIdentity->dimensionSpacePoint, $node->nodeAggregateId); + return $hiddenState->isHidden(); } } @@ -108,3 +980,265 @@ add injection for `$contentRepositoryRegistry` if in use. ```
+ +## NodeTypeManagerAccessRector + +"$this->nodeTypeManager" will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\NodeTypeManagerAccessRector`](../src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php) + +```diff + nodeTypeManager->getNodeTypes(false); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); ++ $nt = $contentRepository->getNodeTypeManager()->getNodeTypes(false); + } + } + + ?> +``` + +
+ +## RemoveDuplicateCommentRector + +"Warning comments for various non-supported use cases + +- class: [`Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector`](../src/Generic/Rules/RemoveDuplicateCommentRector.php) + +```diff + +``` + +
+ +## RemoveInjectionsRector + +Remove properties marked with a @Flow\Inject annotation and a certain type + +:wrench: **configure it!** + +- class: [`Neos\Rector\Generic\Rules\RemoveInjectionsRector`](../src/Generic/Rules/RemoveInjectionsRector.php) + +```php +ruleWithConfiguration(RemoveInjectionsRector::class, [ + new RemoveInjection('Foo\Bar\Baz'), + ]); +}; +``` + +↓ + +```diff + +``` + +
+ +## RemoveParentClassRector + +Remove "extends BLABLA" from classes + +:wrench: **configure it!** + +- class: [`Neos\Rector\Generic\Rules\RemoveParentClassRector`](../src/Generic/Rules/RemoveParentClassRector.php) + +```php +ruleWithConfiguration(RemoveParentClassRector::class, [ + new RemoveParentClass('Foo\Bar\Baz', '// TODO: Neos 9.0 Migration: Stuff'), + ]); +}; +``` + +↓ + +```diff + +``` + +
+ +## WorkspaceRepositoryCountByNameRector + +`"WorkspaceRepository::countByName()"` will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryCountByNameRector`](../src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php) + +```diff + workspaceRepository->countByName($workspace); ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); ++ // TODO 9.0 migration: remove ternary operator (...? 1 : 0 ) - unnecessary complexity ++ ++ return $contentRepository->getWorkspaceFinder()->findOneByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($workspace)) !== null ? 1 : 0; + } + } + + ?> +``` + +
+ +## YamlDimensionConfigRector + +Fusion: Rewrite Settings.yaml config to new language + +- class: [`Neos\Rector\ContentRepository90\Rules\YamlDimensionConfigRector`](../src/ContentRepository90/Rules/YamlDimensionConfigRector.php) + +```diff +-# some YAML with comments + Neos: +- ContentRepository: +- contentDimensions: +- language: +- label: 'Neos.Demo:Main:contentDimensions.language' +- icon: icon-language +- default: en_US +- defaultPreset: en_US +- presets: +- en_US: +- label: 'English (US)' ++ ContentRepositoryRegistry: ++ contentRepositories: ++ default: ++ contentDimensions: ++ language: ++ label: 'Neos.Demo:Main:contentDimensions.language' ++ icon: icon-language + values: +- - en_US +- # The default preset can also have an empty uriSegment value. +- # https://docs.neos.io/cms/manual/content-repository/content-dimensions#behind-the-scenes-routing +- uriSegment: en +- en_UK: +- label: 'English (UK)' +- values: +- - en_UK +- - en_US +- uriSegment: uk +- de: +- label: Deutsch +- values: +- - de +- uriSegment: de ++ en_US: ++ label: 'English (US)' ++ specializations: ++ en_UK: ++ label: 'English (UK)' ++ de: ++ label: Deutsch ++ Neos: ++ sites: ++ '*': ++ contentDimensions: ++ # defaultDimensionSpacePoint is used for the homepage (URL /) ++ defaultDimensionSpacePoint: ++ language: en_US ++ resolver: ++ factoryClassName: Neos\Neos\FrontendRouting\DimensionResolution\Resolver\UriPathResolverFactory ++ options: ++ segments: ++ - ++ dimensionIdentifier: language ++ # dimensionValue => uriPathSegment (empty uriPathSegment allowed) ++ dimensionValueMapping: ++ en_US: en ++ en_UK: uk ++ de: de +``` + +
diff --git a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php index 57b0b94..9ff1008 100644 --- a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php +++ b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php @@ -13,7 +13,7 @@ class FusionCachingNodeInEntryIdentifierRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node to Neos.Caching.entryIdentifierForNode(...) in @cache.entryIdentifier segments', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node to Neos.Caching.entryIdentifierForNode(...) in @cache.entryIdentifier segments', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php b/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php index 3ae14d3..c57bbe4 100644 --- a/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php +++ b/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php @@ -12,7 +12,7 @@ class FusionContextCurrentSiteRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...)', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionContextInBackendRector.php b/src/ContentRepository90/Rules/FusionContextInBackendRector.php index 0e5d38f..ebcc196 100644 --- a/src/ContentRepository90/Rules/FusionContextInBackendRector.php +++ b/src/ContentRepository90/Rules/FusionContextInBackendRector.php @@ -12,7 +12,7 @@ class FusionContextInBackendRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.inBackend to Neos.Node.inBackend(...)', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionContextLiveRector.php b/src/ContentRepository90/Rules/FusionContextLiveRector.php index 28e16a0..3d2fed9 100644 --- a/src/ContentRepository90/Rules/FusionContextLiveRector.php +++ b/src/ContentRepository90/Rules/FusionContextLiveRector.php @@ -12,7 +12,7 @@ class FusionContextLiveRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.live to Neos.Node.isLive(...)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.context.live to Neos.Node.isLive(...)', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php b/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php index 65f8986..183e9a4 100644 --- a/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php +++ b/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php @@ -14,7 +14,7 @@ class FusionNodeAggregateIdentifierRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.nodeAggregateIdentifier to node.nodeAggregateId.value', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.nodeAggregateIdentifier to node.nodeAggregateId.value', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionNodeDepthRector.php b/src/ContentRepository90/Rules/FusionNodeDepthRector.php index a10c968..e8098c3 100644 --- a/src/ContentRepository90/Rules/FusionNodeDepthRector.php +++ b/src/ContentRepository90/Rules/FusionNodeDepthRector.php @@ -14,7 +14,7 @@ class FusionNodeDepthRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.depth to Neos.Node.depth(node)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.depth to Neos.Node.depth(node)', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php b/src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php index aead33a..9e97d73 100644 --- a/src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php +++ b/src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php @@ -14,7 +14,7 @@ class FusionNodeHiddenInIndexRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.hiddenInIndex to node.properties._hiddenInIndex', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.hiddenInIndex to node.properties._hiddenInIndex', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php b/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php index d8008eb..90e20ae 100644 --- a/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php +++ b/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php @@ -14,7 +14,7 @@ class FusionNodeIdentifierRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.identifier to node.nodeAggregateId.value', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.identifier to node.nodeAggregateId.value', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionNodeParentRector.php b/src/ContentRepository90/Rules/FusionNodeParentRector.php index ef37056..8b8bdba 100644 --- a/src/ContentRepository90/Rules/FusionNodeParentRector.php +++ b/src/ContentRepository90/Rules/FusionNodeParentRector.php @@ -14,7 +14,7 @@ class FusionNodeParentRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.parent to Neos.NodeAccess.findParent(node)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.parent to Neos.NodeAccess.findParent(node)', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/FusionNodePathRector.php b/src/ContentRepository90/Rules/FusionNodePathRector.php index 854d114..6e56af6 100644 --- a/src/ContentRepository90/Rules/FusionNodePathRector.php +++ b/src/ContentRepository90/Rules/FusionNodePathRector.php @@ -14,7 +14,7 @@ class FusionNodePathRector implements FusionRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite node.path to Neos.Node.path(node)', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Rewrite node.path to Neos.Node.path(node)', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/ContentRepository90/Rules/YamlDimensionConfigRector.php b/src/ContentRepository90/Rules/YamlDimensionConfigRector.php index 5fe1db0..0998c75 100644 --- a/src/ContentRepository90/Rules/YamlDimensionConfigRector.php +++ b/src/ContentRepository90/Rules/YamlDimensionConfigRector.php @@ -14,7 +14,7 @@ class YamlDimensionConfigRector implements YamlRectorInterface public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Rewrite Settings.yaml config to new language', __CLASS__, 'Settings.yaml'); + return CodeSampleLoader::fromFile('Fusion: Rewrite Settings.yaml config to new language', __CLASS__); } public function refactorFileContent(string $fileContent): string diff --git a/src/Generic/Rules/FusionNodePropertyPathToWarningCommentRector.php b/src/Generic/Rules/FusionNodePropertyPathToWarningCommentRector.php index 4d968b3..5c40906 100644 --- a/src/Generic/Rules/FusionNodePropertyPathToWarningCommentRector.php +++ b/src/Generic/Rules/FusionNodePropertyPathToWarningCommentRector.php @@ -25,7 +25,12 @@ class FusionNodePropertyPathToWarningCommentRector implements FusionRectorInterf public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Fusion: Adds a warning comment when the defined path is used within an Eel expression.', __CLASS__, 'some_class.fusion'); + return CodeSampleLoader::fromFile('Fusion: Adds a warning comment when the defined path is used within an Eel expression.', __CLASS__, [ + new FusionNodePropertyPathToWarningComment('removed', 'Line %LINE: !! node.removed - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios.'), + new FusionNodePropertyPathToWarningComment('hiddenBeforeDateTime', 'Line %LINE: !! node.hiddenBeforeDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time.'), + new FusionNodePropertyPathToWarningComment('hiddenAfterDateTime', 'Line %LINE: !! node.hiddenAfterDateTime is not supported by the new CR. Timed publishing will be implemented not on the read model, but by dispatching commands at a given time.'), + new FusionNodePropertyPathToWarningComment('foo.bar', 'Line %LINE: !! node.foo.bar is not supported anymore.'), + ]); } /** diff --git a/src/Generic/Rules/MethodCallToWarningCommentRector.php b/src/Generic/Rules/MethodCallToWarningCommentRector.php index bfd71c5..222d2f3 100644 --- a/src/Generic/Rules/MethodCallToWarningCommentRector.php +++ b/src/Generic/Rules/MethodCallToWarningCommentRector.php @@ -31,7 +31,9 @@ public function __construct( public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('"Warning comments for various non-supported use cases', __CLASS__); + return CodeSampleLoader::fromFile('"Warning comments for various non-supported use cases', __CLASS__, [ + new MethodCallToWarningComment(Node::class, 'getWorkspace', '!! Node::getWorkspace() does not make sense anymore concept-wise. In Neos < 9, it pointed to the workspace where the node was *at home at*. Now, the closest we have here is the node identity.') + ]); } /** diff --git a/src/Generic/Rules/RemoveInjectionsRector.php b/src/Generic/Rules/RemoveInjectionsRector.php index ab1800b..5aacd0c 100644 --- a/src/Generic/Rules/RemoveInjectionsRector.php +++ b/src/Generic/Rules/RemoveInjectionsRector.php @@ -30,7 +30,9 @@ public function __construct( public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Remove properties marked with a @Flow\Inject annotation and a certain type', __CLASS__); + return CodeSampleLoader::fromFile('Remove properties marked with a @Flow\Inject annotation and a certain type', __CLASS__, [ + new RemoveInjection(\Foo\Bar\Baz::class) + ]); } /** diff --git a/src/Generic/Rules/RemoveParentClassRector.php b/src/Generic/Rules/RemoveParentClassRector.php index 0e95e36..6d5cf1c 100644 --- a/src/Generic/Rules/RemoveParentClassRector.php +++ b/src/Generic/Rules/RemoveParentClassRector.php @@ -36,7 +36,9 @@ public function __construct( public function getRuleDefinition(): RuleDefinition { - return CodeSampleLoader::fromFile('Remove "extends BLABLA" from classes', __CLASS__); + return CodeSampleLoader::fromFile('Remove "extends BLABLA" from classes', __CLASS__, [ + new RemoveParentClass(\Foo\Bar\Baz::class, '// TODO: Neos 9.0 Migration: Stuff') + ]); } /** diff --git a/src/Utility/CodeSampleLoader.php b/src/Utility/CodeSampleLoader.php index 0c99d45..4e4cc94 100644 --- a/src/Utility/CodeSampleLoader.php +++ b/src/Utility/CodeSampleLoader.php @@ -5,15 +5,34 @@ namespace Neos\Rector\Utility; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; class CodeSampleLoader { - static function fromFile(string $description, string $rectorClassName, string $fixtureFileName = 'some_class.php.inc'): RuleDefinition + static function fromFile(string $description, string $rectorClassName, array $codeSampleConfiguration = []): RuleDefinition { $shortName = (new \ReflectionClass($rectorClassName))->getShortName(); - $fileName = __DIR__ . '/../../tests/Rules/' . $shortName . '/Fixture/some_class.php.inc'; - list($beforeCode, $afterCode) = explode('-----', file_get_contents($fileName)); - return new RuleDefinition($description, [new CodeSample(trim($beforeCode), trim($afterCode))]); + if (str_contains($rectorClassName, 'ContentRepository90')) { + $folderName = __DIR__ . '/../../tests/ContentRepository90/Rules/' . $shortName . '/Fixture/'; + } elseif (str_contains($rectorClassName, 'Generic')) { + $folderName = __DIR__ . '/../../tests/Generic/Rules/' . $shortName . '/Fixture/'; + } else { + $folderName = __DIR__ . '/../../tests/Rules/' . $shortName . '/Fixture/'; + } + + if (!file_exists($folderName)) { + // we did not move all tests to the new location yet + $folderName = __DIR__ . '/../../tests/Rules/' . $shortName . '/Fixture/'; + } + $files = glob($folderName . '*.inc'); + $file = reset($files); + list($beforeCode, $afterCode) = explode('-----', file_get_contents($file)); + if (!empty($codeSampleConfiguration)) { + $codeSample = new ConfiguredCodeSample(trim($beforeCode), trim($afterCode), $codeSampleConfiguration); + } else { + $codeSample = new CodeSample(trim($beforeCode), trim($afterCode)); + } + return new RuleDefinition($description, [$codeSample]); } } diff --git a/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/NoDimensions.yaml.inc b/tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/zz_NoDimensions.yaml.inc similarity index 100% rename from tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/NoDimensions.yaml.inc rename to tests/ContentRepository90/Rules/YamlDimensionConfigRector/Fixture/zz_NoDimensions.yaml.inc diff --git a/tests/Generic/Rules/RemoveDuplicateCommentRector/Fixture/some_class.php.inc b/tests/Generic/Rules/RemoveDuplicateCommentRector/Fixture/some_class.php.inc new file mode 100644 index 0000000..125684d --- /dev/null +++ b/tests/Generic/Rules/RemoveDuplicateCommentRector/Fixture/some_class.php.inc @@ -0,0 +1,18 @@ + +----- + diff --git a/tests/Generic/Rules/RemoveDuplicateCommentRector/RemoveDuplicateCommentRector.php b/tests/Generic/Rules/RemoveDuplicateCommentRector/RemoveDuplicateCommentRector.php new file mode 100644 index 0000000..c57735e --- /dev/null +++ b/tests/Generic/Rules/RemoveDuplicateCommentRector/RemoveDuplicateCommentRector.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Generic/Rules/RemoveDuplicateCommentRector/config/configured_rule.php b/tests/Generic/Rules/RemoveDuplicateCommentRector/config/configured_rule.php new file mode 100644 index 0000000..945aa5c --- /dev/null +++ b/tests/Generic/Rules/RemoveDuplicateCommentRector/config/configured_rule.php @@ -0,0 +1,8 @@ +rule(\Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector::class); +}; From 67660c74901be54d24711931dcd8b06adbeb3573 Mon Sep 17 00:00:00 2001 From: Sebastian Kurfuerst Date: Mon, 1 May 2023 16:05:58 +0200 Subject: [PATCH 17/26] TASK: pin phpstan version --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 486e9ff..4bfe751 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ }, "require": { "rector/rector": "0.15.24", + "phpstan/phpstan": "1.10.11", "symfony/yaml": "*", "neos/utility-arrays": "*" }, From efbff0d060f9cf115c7c99fdcefeb07c488a3273 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 17:10:58 +0200 Subject: [PATCH 18/26] BUGFIX: Relax regex expression to reduce backtracking to fix flaky behavior based on underlying system --- src/Core/YamlProcessing/YamlWithComments.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Core/YamlProcessing/YamlWithComments.php b/src/Core/YamlProcessing/YamlWithComments.php index 5fa579c..92b8f39 100644 --- a/src/Core/YamlProcessing/YamlWithComments.php +++ b/src/Core/YamlProcessing/YamlWithComments.php @@ -18,8 +18,15 @@ public static function dump(array $input): string $yamlAsString = Yaml::dump($sortedInput, 100, 2); + // WARNING: we had sneaky bugs in the regex below, only on some systems. + // the old regex was "|(\s*)'[^']+##': '##([a-zA-Z0-9+=]+)'|m" + // this lead to problems that only some occurrences of comments were replaced and not all of them; + // and this only on specific systems. + // => our assumption is that we hit some backtracking limit of the regex engine. By using .+ + // instead, we seem to reduce the amount of backtracking, fixing the problem (hopefully everywhere :) ) + // first, replace the comment keys of the form 'bla##': ... - $yamlAsString = preg_replace_callback("|^(\s*)'[^']+##': '##([a-zA-Z0-9+=/]+)'$|m", function ($a) { + $yamlAsString = preg_replace_callback("|(\s*)'.+##': '##([a-zA-Z0-9+=]+)'|m", function ($a) { $indentation = $a[1]; $comment = base64_decode($a[2]); $commentLines = explode("\n", $comment); @@ -28,7 +35,7 @@ public static function dump(array $input): string }, $yamlAsString); // second, replace the comment keys of the form - '##...' - $yamlAsString = preg_replace_callback("|^(\s*)- '##([a-zA-Z0-9+=/]+)'$|m", function ($a) { + $yamlAsString = preg_replace_callback("|^(\s*)- '##([a-zA-Z0-9+=]+)'$|m", function ($a) { $indentation = $a[1]; $comment = base64_decode($a[2]); $commentLines = explode("\n", $comment); From f353876a204a49c33d79aa8ea467244cc0a6d55d Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 17:30:58 +0200 Subject: [PATCH 19/26] FEATURE: Replace NodeInterface::getIdentifier with NodeInterface::NodeAggregateId::value --- config/set/contentrepository-90.php | 3 +- .../Rules/NodeGetIdentifierRector.php | 60 +++++++++++++++++++ .../Fixture/some_class.php.inc | 29 +++++++++ .../NodeGetIdentifierRectorTest.php | 31 ++++++++++ .../config/configured_rule.php | 10 ++++ 5 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/ContentRepository90/Rules/NodeGetIdentifierRector.php create mode 100644 tests/ContentRepository90/Rules/NodeGetIdentifierRector/Fixture/some_class.php.inc create mode 100644 tests/ContentRepository90/Rules/NodeGetIdentifierRector/NodeGetIdentifierRectorTest.php create mode 100644 tests/ContentRepository90/Rules/NodeGetIdentifierRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index f4dbb05..04b4bdd 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -42,6 +42,7 @@ use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; use Rector\Transform\ValueObject\MethodCallToPropertyFetch; +use Neos\Rector\ContentRepository90\Rules\NodeGetIdentifierRector; return static function (RectorConfig $rectorConfig): void { // Register FusionFileProcessor. All Fusion Rectors will be auto-registered at this processor. @@ -140,7 +141,7 @@ // getWorkspace $methodCallToWarningComments[] = new MethodCallToWarningComment(Node::class, 'getWorkspace', '!! Node::getWorkspace() does not make sense anymore concept-wise. In Neos < 9, it pointed to the workspace where the node was *at home at*. Now, the closest we have here is the node identity.'); // getIdentifier - $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(Node::class, 'getIdentifier', 'nodeAggregateId'); + $rectorConfig->rule(NodeGetIdentifierRector::class); $rectorConfig->rule(FusionNodeIdentifierRector::class); // setIndex -> internal $methodCallToWarningComments[] = new MethodCallToWarningComment(Node::class, 'setIndex', '!! Node::setIndex() was always internal. To reorder nodes, use the "MoveNodeAggregate" command'); diff --git a/src/ContentRepository90/Rules/NodeGetIdentifierRector.php b/src/ContentRepository90/Rules/NodeGetIdentifierRector.php new file mode 100644 index 0000000..bbe5599 --- /dev/null +++ b/src/ContentRepository90/Rules/NodeGetIdentifierRector.php @@ -0,0 +1,60 @@ +> + */ + public function getNodeTypes(): array + { + return [\PhpParser\Node\Expr\MethodCall::class]; + } + + /** + * @param \PhpParser\Node\Expr\MethodCall $node + */ + public function refactor(Node $node): ?Node + { + assert($node instanceof Node\Expr\MethodCall); + + if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Core\Projection\ContentGraph\Node::class))) { + return null; + } + if (!$this->isName($node->name, 'getIdentifier')) { + return null; + } + + $this->nodesToAddCollector->addNodesBeforeNode( + [ + self::todoComment('Check if you could change your code to work with the NodeAggregateId value object instead.') + ], + $node + ); + + $propertyFetchAggregateId = $this->nodeFactory->createPropertyFetch($node->var, 'nodeAggregateId'); + return $this->nodeFactory->createPropertyFetch($propertyFetchAggregateId, 'value'); + } +} diff --git a/tests/ContentRepository90/Rules/NodeGetIdentifierRector/Fixture/some_class.php.inc b/tests/ContentRepository90/Rules/NodeGetIdentifierRector/Fixture/some_class.php.inc new file mode 100644 index 0000000..49720be --- /dev/null +++ b/tests/ContentRepository90/Rules/NodeGetIdentifierRector/Fixture/some_class.php.inc @@ -0,0 +1,29 @@ +getIdentifier(); + } +} + +?> +----- +nodeAggregateId->value; + } +} + +?> diff --git a/tests/ContentRepository90/Rules/NodeGetIdentifierRector/NodeGetIdentifierRectorTest.php b/tests/ContentRepository90/Rules/NodeGetIdentifierRector/NodeGetIdentifierRectorTest.php new file mode 100644 index 0000000..e60b0a6 --- /dev/null +++ b/tests/ContentRepository90/Rules/NodeGetIdentifierRector/NodeGetIdentifierRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/NodeGetIdentifierRector/config/configured_rule.php b/tests/ContentRepository90/Rules/NodeGetIdentifierRector/config/configured_rule.php new file mode 100644 index 0000000..ac45316 --- /dev/null +++ b/tests/ContentRepository90/Rules/NodeGetIdentifierRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(NodeGetIdentifierRector::class); +}; From 7f84d6c9f851c13a7352097575efbb67f5cc2a17 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 18:10:21 +0200 Subject: [PATCH 20/26] FEATURE: Replace Workspace::getName with Workspace::WorkspaceName::value --- config/set/contentrepository-90.php | 10 ++- .../Rules/WorkspaceGetNameRector.php | 61 +++++++++++++++++++ .../Fixture/some_class.php.inc | 29 +++++++++ .../WorkspaceGetNameRectorTest.php | 31 ++++++++++ .../config/configured_rule.php | 10 +++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/ContentRepository90/Rules/WorkspaceGetNameRector.php create mode 100644 tests/ContentRepository90/Rules/WorkspaceGetNameRector/Fixture/some_class.php.inc create mode 100644 tests/ContentRepository90/Rules/WorkspaceGetNameRector/WorkspaceGetNameRectorTest.php create mode 100644 tests/ContentRepository90/Rules/WorkspaceGetNameRector/config/configured_rule.php diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index f4dbb05..fefc63e 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -42,6 +42,7 @@ use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; use Rector\Transform\ValueObject\MethodCallToPropertyFetch; +use Neos\Rector\ContentRepository90\Rules\WorkspaceGetNameRector; return static function (RectorConfig $rectorConfig): void { // Register FusionFileProcessor. All Fusion Rectors will be auto-registered at this processor. @@ -66,7 +67,9 @@ 'Neos\ContentRepository\Domain\Model\NodeType' => \Neos\ContentRepository\Core\NodeType\NodeType::class, 'Neos\ContentRepository\Domain\Service\NodeTypeManager' => \Neos\ContentRepository\Core\NodeType\NodeTypeManager::class, - 'Neos\ContentRepository\Utility' => \Neos\ContentRepositoryRegistry\Utility::class + 'Neos\ContentRepository\Utility' => \Neos\ContentRepositoryRegistry\Utility::class, + + 'Neos\ContentRepository\Domain\Model\Workspace' => \Neos\ContentRepository\Core\Projection\Workspace\Workspace::class, ]); @@ -296,6 +299,11 @@ */ $rectorConfig->rule(WorkspaceRepositoryCountByNameRector::class); + /** + * Neos\ContentRepository\Domain\Model\Workspace + */ + $rectorConfig->rule(WorkspaceGetNameRector::class); + /** * SPECIAL rules */ diff --git a/src/ContentRepository90/Rules/WorkspaceGetNameRector.php b/src/ContentRepository90/Rules/WorkspaceGetNameRector.php new file mode 100644 index 0000000..ba768ae --- /dev/null +++ b/src/ContentRepository90/Rules/WorkspaceGetNameRector.php @@ -0,0 +1,61 @@ +> + */ + public function getNodeTypes(): array + { + return [\PhpParser\Node\Expr\MethodCall::class]; + } + + /** + * @param \PhpParser\Node\Expr\MethodCall $node + */ + public function refactor(Node $node): ?Node + { + assert($node instanceof Node\Expr\MethodCall); + + if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + return null; + } + if (!$this->isName($node->name, 'getName')) { + return null; + } + + $this->nodesToAddCollector->addNodesBeforeNode( + [ + self::todoComment('Check if you could change your code to work with the WorkspaceName value object instead.') + ], + $node + ); + + $propertyFetchAggregateId = $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'); + return $this->nodeFactory->createPropertyFetch($propertyFetchAggregateId, 'value'); + } +} diff --git a/tests/ContentRepository90/Rules/WorkspaceGetNameRector/Fixture/some_class.php.inc b/tests/ContentRepository90/Rules/WorkspaceGetNameRector/Fixture/some_class.php.inc new file mode 100644 index 0000000..d66c44c --- /dev/null +++ b/tests/ContentRepository90/Rules/WorkspaceGetNameRector/Fixture/some_class.php.inc @@ -0,0 +1,29 @@ +getName(); + } +} + +?> +----- +workspaceName->value; + } +} + +?> diff --git a/tests/ContentRepository90/Rules/WorkspaceGetNameRector/WorkspaceGetNameRectorTest.php b/tests/ContentRepository90/Rules/WorkspaceGetNameRector/WorkspaceGetNameRectorTest.php new file mode 100644 index 0000000..d9e1657 --- /dev/null +++ b/tests/ContentRepository90/Rules/WorkspaceGetNameRector/WorkspaceGetNameRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($fileInfo); + } + + /** + * @return \Iterator + */ + public function provideData(): \Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/ContentRepository90/Rules/WorkspaceGetNameRector/config/configured_rule.php b/tests/ContentRepository90/Rules/WorkspaceGetNameRector/config/configured_rule.php new file mode 100644 index 0000000..981a24c --- /dev/null +++ b/tests/ContentRepository90/Rules/WorkspaceGetNameRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(WorkspaceGetNameRector::class); +}; From b422c5b995f9f65c100f608867cf2a9fccf0dcca Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Mon, 1 May 2023 16:19:32 +0200 Subject: [PATCH 21/26] TASK: Add github workflow to run composer test --- .github/workflows/build.yml | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..6b509a8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,39 @@ +name: build + +on: + push: + branches: + - 'main' + pull_request: + branches: + - 'main' + +jobs: + test: + name: "Test (PHP ${{ matrix.php-versions }})" + + strategy: + fail-fast: false + matrix: + php-versions: ['8.1', '8.2'] + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + path: ${{ env.FLOW_FOLDER }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: mbstring, xml, json, zlib, iconv, intl, pdo_sqlite + ini-values: date.timezone="Africa/Tunis", opcache.fast_shutdown=0, apc.enable_cli=on + + - name: Install + run: composer install + + - name: Run Tests + run: composer test From 31ce18c8a6a936e2166cccf903ec752dc486aff2 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 19:21:03 +0200 Subject: [PATCH 22/26] BUGFIX: Refactor Node::getChildNodes rector refactoring due to the latest changes to FindChildNodesFilter --- .../Rules/Traits/SubgraphTrait.php | 16 ++++++---- .../Fixture/all-with-pagination.php.inc | 32 +++++++++++++++++++ .../{some_class.php.inc => all.php.inc} | 2 +- .../nodetype-filter-with-pagination.php.inc | 32 +++++++++++++++++++ .../Fixture/nodetype-filter.php.inc | 32 +++++++++++++++++++ 5 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc rename tests/Rules/NodeGetChildNodesRector/Fixture/{some_class.php.inc => all.php.inc} (91%) create mode 100644 tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc create mode 100644 tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter.php.inc diff --git a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php index e4e4f50..c3691b5 100644 --- a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php +++ b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php @@ -24,28 +24,30 @@ private function subgraph_findChildNodes( ?Expr $offset = null ): Expr { - if ($nodeTypeConstraintsFilterString) { + if ($nodeTypeConstraintsFilterString && $nodeTypeConstraintsFilterString != null) { $filter = $this->nodeFactory->createStaticCall( FindChildNodesFilter::class, - 'nodeTypeConstraints', + 'create', [ - $nodeTypeConstraintsFilterString + 'nodeTypeConstraints' => $nodeTypeConstraintsFilterString ] ); } else { $filter = $this->nodeFactory->createStaticCall( FindChildNodesFilter::class, - 'all' + 'create' ); } if ($limit || $offset) { $filter = $this->nodeFactory->createMethodCall( $filter, - 'withPagination', + 'with', [ - $limit, - $offset + 'pagination' => [ + 'limit' => $limit, + 'offset' => $offset + ] ] ); } diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc new file mode 100644 index 0000000..ea4cd1c --- /dev/null +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc @@ -0,0 +1,32 @@ +getChildNodes(null, 10,100) as $node) { + } + } +} + +?> +----- +contentRepositoryRegistry->subgraphForNode($node); + // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. + + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: null)->with(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { + } + } +} + +?> diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/some_class.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/all.php.inc similarity index 91% rename from tests/Rules/NodeGetChildNodesRector/Fixture/some_class.php.inc rename to tests/Rules/NodeGetChildNodesRector/Fixture/all.php.inc index 67d41e3..0471fcc 100644 --- a/tests/Rules/NodeGetChildNodesRector/Fixture/some_class.php.inc +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/all.php.inc @@ -24,7 +24,7 @@ class SomeClass $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. - foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::all())) as $node) { + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create())) as $node) { } } } diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc new file mode 100644 index 0000000..1c3b2e3 --- /dev/null +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc @@ -0,0 +1,32 @@ +getChildNodes('Neos.Neos:Document', 10, 100) as $node) { + } + } +} + +?> +----- +contentRepositoryRegistry->subgraphForNode($node); + // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. + + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: 'Neos.Neos:Document')->with(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { + } + } +} + +?> diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter.php.inc new file mode 100644 index 0000000..f2f5868 --- /dev/null +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter.php.inc @@ -0,0 +1,32 @@ +getChildNodes('Neos.Neos:Document') as $node) { + } + } +} + +?> +----- +contentRepositoryRegistry->subgraphForNode($node); + // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. + + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: 'Neos.Neos:Document'))) as $node) { + } + } +} + +?> From 5e9d8de88ec79923067bd0e6d02b080be1326a0a Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 20:03:11 +0200 Subject: [PATCH 23/26] BUGFIX: Refactor ContentRepository::getInterDimensionalVariationGraph to ContentRepository::getVariationGraph --- docs/rules_overview.md | 2 +- ...DimensionCombinatorGetAllAllowedCombinationsRector.php | 2 +- .../Rules/Traits/ContentRepositoryTrait.php | 8 ++++---- .../Fixture/some_class.php.inc | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/rules_overview.md b/docs/rules_overview.md index 0a8a731..903a584 100644 --- a/docs/rules_overview.md +++ b/docs/rules_overview.md @@ -22,7 +22,7 @@ { - $combinations = $this->contentDimensionCombinator->getAllAllowedCombinations(); + $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); -+ $dimensionSpacePoints = $contentRepository->getInterDimensionalVariationGraph()->getDimensionSpacePoints(); ++ $dimensionSpacePoints = $contentRepository->getVariationGraph()->getDimensionSpacePoints(); + // TODO 9.0 migration: try to directly work with $dimensionSpacePoints, instead of converting them to the legacy dimension format + + $combinations = array_map(fn(\Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint $dimensionSpacePoint) => $dimensionSpacePoint->toLegacyDimensionArray(), iterator_to_array($dimensionSpacePoints)); diff --git a/src/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector.php b/src/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector.php index 7dccbd8..2672fd6 100644 --- a/src/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector.php +++ b/src/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector.php @@ -52,7 +52,7 @@ public function refactor(Node $node) : ?Node $this->nodesToAddCollector->addNodesBeforeNode( [ self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - self::assign('dimensionSpacePoints', $this->contentRepository_getInterDimensionalVariationGraph_getDimensionSpacePoints()), + self::assign('dimensionSpacePoints', $this->contentRepository_getVariationGraph_getDimensionSpacePoints()), self::todoComment('try to directly work with $dimensionSpacePoints, instead of converting them to the legacy dimension format') ], $node diff --git a/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php b/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php index 4f2433c..0ffc2b9 100644 --- a/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php +++ b/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php @@ -94,19 +94,19 @@ private function contentRepository_getContentGraph(): Expr ); } - private function contentRepository_getInterDimensionalVariationGraph(): Expr + private function contentRepository_getVariationGraph(): Expr { return $this->nodeFactory->createMethodCall( new Variable('contentRepository'), - 'getInterDimensionalVariationGraph', + 'getVariationGraph', [] ); } - private function contentRepository_getInterDimensionalVariationGraph_getDimensionSpacePoints() + private function contentRepository_getVariationGraph_getDimensionSpacePoints() { return $this->nodeFactory->createMethodCall( - $this->contentRepository_getInterDimensionalVariationGraph(), + $this->contentRepository_getVariationGraph(), 'getDimensionSpacePoints' ); } diff --git a/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/Fixture/some_class.php.inc b/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/Fixture/some_class.php.inc index ff50a0c..6715e0e 100644 --- a/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/Fixture/some_class.php.inc +++ b/tests/ContentRepository90/Rules/ContentDimensionCombinatorGetAllAllowedCombinationsRector/Fixture/some_class.php.inc @@ -33,7 +33,7 @@ class SomeClass public function run() { $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\Factory\ContentRepositoryId::fromString('default')); - $dimensionSpacePoints = $contentRepository->getInterDimensionalVariationGraph()->getDimensionSpacePoints(); + $dimensionSpacePoints = $contentRepository->getVariationGraph()->getDimensionSpacePoints(); // TODO 9.0 migration: try to directly work with $dimensionSpacePoints, instead of converting them to the legacy dimension format $combinations = array_map(fn(\Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint $dimensionSpacePoint) => $dimensionSpacePoint->toLegacyDimensionArray(), iterator_to_array($dimensionSpacePoints)); From 64c72d774a80da912d52bbf9e1b6c307753c7e43 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 20:56:53 +0200 Subject: [PATCH 24/26] BUGFIX: Refactor Node::getChildNodes rector refactoring due to the latest changes to FindChildNodesFilter --- .../Rules/NodeGetChildNodesRector.php | 35 ++++++++------- .../Rules/Traits/SubgraphTrait.php | 44 ++++++++----------- ...ll-with-pagination-named-arguments.php.inc | 32 ++++++++++++++ .../Fixture/all-with-pagination.php.inc | 2 +- .../nodetype-filter-with-pagination.php.inc | 2 +- 5 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination-named-arguments.php.inc diff --git a/src/ContentRepository90/Rules/NodeGetChildNodesRector.php b/src/ContentRepository90/Rules/NodeGetChildNodesRector.php index 72f01a8..c0d0423 100644 --- a/src/ContentRepository90/Rules/NodeGetChildNodesRector.php +++ b/src/ContentRepository90/Rules/NodeGetChildNodesRector.php @@ -48,22 +48,27 @@ public function refactor(Node $node) : ?Node $nodeTypeFilterExpr = null; $limitExpr = null; $offsetExpr = null; - if (count($node->args) >= 1) { - $nodeTypeFilterExpr = $node->args[0]; - assert($nodeTypeFilterExpr instanceof Node\Arg); - $nodeTypeFilterExpr = $nodeTypeFilterExpr->value; - } - if (count($node->args) >= 2) { - $limitExpr = $node->args[1]; - assert($limitExpr instanceof Node\Arg); - $limitExpr = $limitExpr->value; - } - if (count($node->args) >= 3) { - $offsetExpr = $node->args[2]; - assert($offsetExpr instanceof Node\Arg); - $offsetExpr = $offsetExpr->value; - } + foreach ($node->args as $index => $arg) { + $argumentName = $arg?->name?->name; + $namedArgument = $argumentName !== null; + + if (($namedArgument && $argumentName === 'nodeTypeFilter') || !$namedArgument && $index === 0) { + $nodeTypeFilterExpr = $arg; + assert($nodeTypeFilterExpr instanceof Node\Arg); + $nodeTypeFilterExpr = $nodeTypeFilterExpr->value; + } + if (($namedArgument && $argumentName === 'limit') || !$namedArgument && $index === 1) { + $limitExpr = $arg; + assert($limitExpr instanceof Node\Arg); + $limitExpr = $limitExpr->value; + } + if (($namedArgument && $argumentName === 'offset') || !$namedArgument && $index === 2) { + $offsetExpr = $arg; + assert($offsetExpr instanceof Node\Arg); + $offsetExpr = $offsetExpr->value; + } + } $this->nodesToAddCollector->addNodesBeforeNode( [ diff --git a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php index c3691b5..7a0cc86 100644 --- a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php +++ b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php @@ -21,37 +21,29 @@ private function subgraph_findChildNodes( Expr $nodeVariable, ?Expr $nodeTypeConstraintsFilterString = null, ?Expr $limit = null, - ?Expr $offset = null - ): Expr - { - if ($nodeTypeConstraintsFilterString && $nodeTypeConstraintsFilterString != null) { - $filter = $this->nodeFactory->createStaticCall( - FindChildNodesFilter::class, - 'create', - [ - 'nodeTypeConstraints' => $nodeTypeConstraintsFilterString - ] - ); - } else { - $filter = $this->nodeFactory->createStaticCall( - FindChildNodesFilter::class, - 'create' - ); + ?Expr $offset = null, + ): Expr { + $args = []; + + if ($nodeTypeConstraintsFilterString ) { + $args = [ + 'nodeTypeConstraints' => $nodeTypeConstraintsFilterString + ]; } if ($limit || $offset) { - $filter = $this->nodeFactory->createMethodCall( - $filter, - 'with', - [ - 'pagination' => [ - 'limit' => $limit, - 'offset' => $offset - ] - ] - ); + $args['pagination'] = [ + 'limit' => $limit, + 'offset' => $offset + ]; } + $filter = $this->nodeFactory->createStaticCall( + FindChildNodesFilter::class, + 'create', + $args + ); + return $this->nodeFactory->createMethodCall( 'subgraph', 'findChildNodes', diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination-named-arguments.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination-named-arguments.php.inc new file mode 100644 index 0000000..fd73667 --- /dev/null +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination-named-arguments.php.inc @@ -0,0 +1,32 @@ +getChildNodes(offset: 100, limit: 10) as $node) { + } + } +} + +?> +----- +contentRepositoryRegistry->subgraphForNode($node); + // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. + + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { + } + } +} + +?> diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc index ea4cd1c..8010441 100644 --- a/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc @@ -24,7 +24,7 @@ class SomeClass $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. - foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: null)->with(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: null, pagination: ['limit' => 10, 'offset' => 100]))) as $node) { } } } diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc index 1c3b2e3..5db707c 100644 --- a/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/nodetype-filter-with-pagination.php.inc @@ -24,7 +24,7 @@ class SomeClass $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. - foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: 'Neos.Neos:Document')->with(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: 'Neos.Neos:Document', pagination: ['limit' => 10, 'offset' => 100]))) as $node) { } } } From 652ec336ff7fc79d9c639fc0d6b756e287117cd0 Mon Sep 17 00:00:00 2001 From: Denny Lubitz Date: Mon, 1 May 2023 21:23:34 +0200 Subject: [PATCH 25/26] BUGFIX: Refactor Node::getChildNodes rector refactoring due to the latest changes to FindChildNodesFilter --- .../Rules/NodeGetChildNodesRector.php | 18 +++++++++--------- .../Rules/Traits/SubgraphTrait.php | 2 +- .../Fixture/all-with-pagination.php.inc | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ContentRepository90/Rules/NodeGetChildNodesRector.php b/src/ContentRepository90/Rules/NodeGetChildNodesRector.php index c0d0423..cfd1029 100644 --- a/src/ContentRepository90/Rules/NodeGetChildNodesRector.php +++ b/src/ContentRepository90/Rules/NodeGetChildNodesRector.php @@ -54,19 +54,19 @@ public function refactor(Node $node) : ?Node $namedArgument = $argumentName !== null; if (($namedArgument && $argumentName === 'nodeTypeFilter') || !$namedArgument && $index === 0) { - $nodeTypeFilterExpr = $arg; - assert($nodeTypeFilterExpr instanceof Node\Arg); - $nodeTypeFilterExpr = $nodeTypeFilterExpr->value; + assert($arg instanceof Node\Arg); + + if ($arg->value instanceof Node\Scalar\String_) { + $nodeTypeFilterExpr = $arg->value; + } } if (($namedArgument && $argumentName === 'limit') || !$namedArgument && $index === 1) { - $limitExpr = $arg; - assert($limitExpr instanceof Node\Arg); - $limitExpr = $limitExpr->value; + assert($arg instanceof Node\Arg); + $limitExpr = $arg->value; } if (($namedArgument && $argumentName === 'offset') || !$namedArgument && $index === 2) { - $offsetExpr = $arg; - assert($offsetExpr instanceof Node\Arg); - $offsetExpr = $offsetExpr->value; + assert($arg instanceof Node\Arg); + $offsetExpr = $arg->value; } } diff --git a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php index 7a0cc86..96334c7 100644 --- a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php +++ b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php @@ -25,7 +25,7 @@ private function subgraph_findChildNodes( ): Expr { $args = []; - if ($nodeTypeConstraintsFilterString ) { + if ($nodeTypeConstraintsFilterString) { $args = [ 'nodeTypeConstraints' => $nodeTypeConstraintsFilterString ]; diff --git a/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc index 8010441..dc78754 100644 --- a/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc +++ b/tests/Rules/NodeGetChildNodesRector/Fixture/all-with-pagination.php.inc @@ -24,7 +24,7 @@ class SomeClass $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); // TODO 9.0 migration: Try to remove the iterator_to_array($nodes) call. - foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(nodeTypeConstraints: null, pagination: ['limit' => 10, 'offset' => 100]))) as $node) { + foreach (iterator_to_array($subgraph->findChildNodes($node->nodeAggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { } } } From 7f4b7b600e006b793e1bf14d2377533a67165601 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Thu, 14 Sep 2023 19:40:19 +0200 Subject: [PATCH 26/26] TASK: Adjust migration for user interface mode the migration previously adjusted the code to a now deprecated eel helper. In pr https://github.com/neos/neos-development-collection/pull/4505 the renderingMode is introduced as global fusionValue which made an adjustment in the rector necessary. `(node|documentNode|site).context.inBackend` is now migrated to `renderingMode.isEdit` `(node|documentNode|site).context.live` is now migrated to `!renderingMode.isEdit` In cases where it cannot be determined wether a node is affected by the `context` operation a comment is added as before. --- .../Rules/FusionContextInBackendRector.php | 4 ++-- src/ContentRepository90/Rules/FusionContextLiveRector.php | 4 ++-- .../Fixture/some_file.fusion.inc | 8 ++++---- .../FusionContextLiveRector/Fixture/some_file.fusion.inc | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ContentRepository90/Rules/FusionContextInBackendRector.php b/src/ContentRepository90/Rules/FusionContextInBackendRector.php index ebcc196..f7f3afb 100644 --- a/src/ContentRepository90/Rules/FusionContextInBackendRector.php +++ b/src/ContentRepository90/Rules/FusionContextInBackendRector.php @@ -20,12 +20,12 @@ public function refactorFileContent(string $fileContent): string return EelExpressionTransformer::parse($fileContent) ->process(fn(string $eelExpression) => preg_replace( '/(node|documentNode|site)\.context\.inBackend/', - 'Neos.Node.inBackend($1)', + 'renderingMode.isEdit', $eelExpression )) ->addCommentsIfRegexMatches( '/\.context\.inBackend/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Node.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.inBackend" to "renderingMode.isEdit". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' )->getProcessedContent(); } } diff --git a/src/ContentRepository90/Rules/FusionContextLiveRector.php b/src/ContentRepository90/Rules/FusionContextLiveRector.php index 3d2fed9..ca658db 100644 --- a/src/ContentRepository90/Rules/FusionContextLiveRector.php +++ b/src/ContentRepository90/Rules/FusionContextLiveRector.php @@ -20,12 +20,12 @@ public function refactorFileContent(string $fileContent): string return EelExpressionTransformer::parse($fileContent) ->process(fn(string $eelExpression) => preg_replace( '/(node|documentNode|site)\.context\.live/', - 'Neos.Node.isLive($1)', + '!renderingMode.isEdit', $eelExpression )) ->addCommentsIfRegexMatches( '/\.context\.live/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.live" to Neos.Node.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' + '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.live" to "!renderingMode.isEdit". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' )->getProcessedContent(); } } diff --git a/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion.inc index caece89..7f870cf 100644 --- a/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion.inc +++ b/tests/ContentRepository90/Rules/FusionContextInBackendRector/Fixture/some_file.fusion.inc @@ -30,7 +30,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie } } ----- -// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.inBackend" to Neos.Node.inBackend(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.inBackend" to "renderingMode.isEdit". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { renderer = Neos.Fusion:Component { @@ -38,7 +38,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie # # pass down props # - attributes = ${Neos.Node.inBackend(node) || Neos.Node.inBackend(site) || Neos.Node.inBackend(documentNode)} + attributes = ${renderingMode.isEdit || renderingMode.isEdit || renderingMode.isEdit} # # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` @@ -54,10 +54,10 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie renderer = afx` ` } diff --git a/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion.inc b/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion.inc index 27bbb94..ae0e5e0 100644 --- a/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion.inc +++ b/tests/ContentRepository90/Rules/FusionContextLiveRector/Fixture/some_file.fusion.inc @@ -30,7 +30,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie } } ----- -// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.live" to Neos.Node.isLive(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. +// TODO 9.0 migration: Line 26: You very likely need to rewrite "VARIABLE.context.live" to "!renderingMode.isEdit". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { renderer = Neos.Fusion:Component { @@ -38,7 +38,7 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie # # pass down props # - attributes = ${Neos.Node.isLive(node) || Neos.Node.isLive(site) || Neos.Node.isLive(documentNode)} + attributes = ${!renderingMode.isEdit || !renderingMode.isEdit || !renderingMode.isEdit} # # the `checked` state is calculated outside the renderer to allow` overriding via `attributes` @@ -54,10 +54,10 @@ prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Fie renderer = afx` ` }