diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6d200f0..56a4bee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['8.2', '8.3'] + php-versions: ['8.3', '8.4'] runs-on: ubuntu-latest diff --git a/README.md b/README.md index 1ebe3bd..be11e57 100644 --- a/README.md +++ b/README.md @@ -2,19 +2,26 @@ This package contains a Rector ruleset which is used for migrating from Neos 8.x to 9.0 (and lateron also further up). -It will eventually replace Core/Code Migrations (./flow flow:core:migrate) (which are not used anymore for migrating to Neos 9 and further). - Right now we focus especially on rules to migrate from the old Content Repository API (< Neos 9.0) to the Event Sourced Content Repository (>= 9.0). +**PHP-only migrations** + +Earlier version of `neos/rector` also did the migration of Yaml and Fusion file. They have been moved into Neos core migrations starting version 9.0.8. Now `neos/rector` only contains PHP migrations. + +See for more details: https://github.com/neos/neos-development-collection/issues/5607 + + + ## Installation -As Rector has strict dependency requirements, which might not match your own project, we strongly recommend to install -neos/rector in a dedicated directory and **not to add it to your project**. + +Please install the neos/rector package in your Distribution as a dev dependency with composer. +
(This has changed to previous versions of this package) ```bash # inside your Distribution folder -composer create-project neos/rector:dev-main --stability=dev rector -cp rector/rector.template.php rector.php +composer require --dev neos/rector:dev-main +cp Packages/Libraries/neos/rector/rector.template.php rector.php ``` ## Configuration @@ -24,19 +31,12 @@ migrated). By default, all of `./DistributionPackages` will be migrated. Right now, we ship the following sets of Rector rules: -- `\Neos\Rector\NeosRectorSets::CONTENTREPOSITORY_9_0`: all rules needed to migrate to the Event-Sourced Content Repository - -Also you need to add the autoload paths, to allow rector to parse your code properly. By default we added `./Packages` and `./DistributionPackages` to the template. +- `\Neos\Rector\NeosRectorSets::CONTENTREPOSITORY_9_0`: all rules needed to migrate to the Event-Sourced Content + Repository ```php $rectorConfig->sets([ NeosRectorSets::CONTENTREPOSITORY_9_0, - //NeosRectorSets::NEOS_8_4, -]); - -$rectorConfig->autoloadPaths([ - __DIR__ . '/Packages', - __DIR__ . '/DistributionPackages', ]); $rectorConfig->paths([ @@ -52,11 +52,12 @@ Run the following command at the root of your distribution (i.e. where `rector.p ```bash # for trying out what would be done -./rector/vendor/bin/rector --dry-run +./bin/rector --dry-run # for running the migrations -./rector/vendor/bin/rector +./bin/rector ``` + --- # Developing Rector Rules for Neos @@ -77,58 +78,13 @@ The test setup runs completely self contained; does not need *any* Distribution ```bash # if inside a Neos Distribution, change to the Package's folder -cd rector +cd Packages/Libraries/neos/rector # install PHPunit composer install # run PHPUnit -composer test -``` - -## Fusion Rector - -We extended Rector specifically for migrating Fusion files, by providing a `FusionFileProcessor` and a `FusionRectorInterface` -which you can implement if you want to build Fusion transformations. - -The Fusion Rectors will usually use one of the following tooling classes: - -- `EelExpressionTransformer`: for finding all Eel expressions inside Fusion and AFX; and transforming them in some way. - -The Fusion and AFX Parsing functionality is based on the official Fusion and AFX parsers. However, the classes are -vendored/copied into this package by the `./embed-fusion-and-afx-parsers.sh` script, because of the following reasons: - -- Rector needs to run even when Flow cannot compile the classes; so we cannot depend on a Flow package. -- We slightly need to patch the AFX parser, because we need position information for Eel Expressions. - -The Fusion parser was subclassed by `Neos\Rector\Core\FusionProcessing\CustomObjectTreeParser` for retaining position -information of AFX and Eel Expressions. - - -**Updating Fusion and AFX Parser** - -To update the vendored Fusion and AFX parsers, run the `./embed-fusion-and-afx-parsers.sh` script. - - -**Updating the AFX Parser Patch** - -The AFX parser needs a custom patch (see `./scripts/afx-eel-positions.patch`) to retain positions. - -To create/update this patch, do the following: - -```bash -cd Packages/Neos - -# apply the current patch -patch -p1 < ../../rector/scripts/afx-eel-positions.patch - -# Now, do your modifications as needed. - -# when you are finished, create the new patch -git diff -- Neos.Fusion.Afx/ > ../../rector/scripts/afx-eel-positions.patch - -# ... and reset the code changes inside Neos.Fusion.Afx. -git restore -- Neos.Fusion.Afx/ +composer tests ``` ## Generating docs diff --git a/composer.json b/composer.json index e3f4456..c9fc0ab 100644 --- a/composer.json +++ b/composer.json @@ -16,15 +16,14 @@ "tests": "phpunit tests" }, "require": { - "php": "8.0.* || 8.1.* || 8.2.* || 8.3.*", - "rector/rector": "0.15.25", - "phpstan/phpstan": "1.10.14", + "php": "^8.3", + "rector/rector": "^2.0", "symfony/yaml": "*", "neos/utility-arrays": "*", "webmozart/assert": "^1.11" }, "require-dev": { - "phpunit/phpunit": "^9.5", - "symplify/rule-doc-generator": "^11.1" + "phpunit/phpunit": "^12.0", + "symplify/rule-doc-generator": "^12.2" } } diff --git a/config/set/contentrepository-90.php b/config/set/contentrepository-90.php index 3305955..15b909b 100644 --- a/config/set/contentrepository-90.php +++ b/config/set/contentrepository-90.php @@ -9,7 +9,7 @@ use Neos\Neos\Domain\Service\WorkspacePublishingService; use Neos\Neos\Domain\Service\WorkspaceService; use Neos\Rector\ContentRepository90\Legacy\LegacyContextStub; -use Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub; +use Neos\ContentRepository\Domain\Model\Node as NodeLegacyStub; use Neos\Rector\ContentRepository90\Rules\ContentDimensionCombinatorGetAllAllowedCombinationsRector; use Neos\Rector\ContentRepository90\Rules\ContentRepositoryUtilityRenderValidNodeNameRector; use Neos\Rector\ContentRepository90\Rules\ContextFactoryToLegacyContextStubRector; @@ -18,29 +18,6 @@ use Neos\Rector\ContentRepository90\Rules\ContextGetRootNodeRector; use Neos\Rector\ContentRepository90\Rules\ContextIsInBackendRector; use Neos\Rector\ContentRepository90\Rules\ContextIsLiveRector; -use Neos\Rector\ContentRepository90\Rules\FusionCacheLifetimeRector; -use Neos\Rector\ContentRepository90\Rules\FusionCachingNodeInEntryIdentifierRector; -use Neos\Rector\ContentRepository90\Rules\FusionContextCurrentRenderingModeRector; -use Neos\Rector\ContentRepository90\Rules\FusionContextCurrentSiteRector; -use Neos\Rector\ContentRepository90\Rules\FusionContextGetWorkspaceNameRector; -use Neos\Rector\ContentRepository90\Rules\FusionContextGetWorkspaceRector; -use Neos\Rector\ContentRepository90\Rules\FusionContextInBackendRector; -use Neos\Rector\ContentRepository90\Rules\FusionContextLiveRector; -use Neos\Rector\ContentRepository90\Rules\FusionFlowQueryContextRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeAggregateIdentifierRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeAutoCreatedRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeContextPathRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeDepthRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenAfterDateTimeRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenBeforeDateTimeRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenInIndexRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeIdentifierRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeLabelRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeNodeTypeRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeParentRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodePathRector; -use Neos\Rector\ContentRepository90\Rules\FusionNodeTypeNameRector; use Neos\Rector\ContentRepository90\Rules\NodeFactoryResetRector; use Neos\Rector\ContentRepository90\Rules\NodeFindParentNodeRector; use Neos\Rector\ContentRepository90\Rules\NodeGetChildNodesRector; @@ -78,12 +55,6 @@ use Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryFindByIdentifierRector; use Neos\Rector\ContentRepository90\Rules\WorkspaceSetDescriptionRector; use Neos\Rector\ContentRepository90\Rules\WorkspaceSetTitleRector; -use Neos\Rector\ContentRepository90\Rules\YamlDimensionConfigRector; -use Neos\Rector\ContentRepository90\Rules\YamlRoutePartHandlerRector; -use Neos\Rector\Generic\Rules\FusionFlowQueryNodePropertyToWarningCommentRector; -use Neos\Rector\Generic\Rules\FusionNodePropertyPathToWarningCommentRector; -use Neos\Rector\Generic\Rules\FusionPrototypeNameAddCommentRector; -use Neos\Rector\Generic\Rules\FusionReplacePrototypeNameRector; use Neos\Rector\Generic\Rules\InjectServiceIfNeededRector; use Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector; use Neos\Rector\Generic\Rules\ObjectInstantiationToWarningCommentRector; @@ -91,10 +62,6 @@ use Neos\Rector\Generic\Rules\SignalSlotToWarningCommentRector; use Neos\Rector\Generic\Rules\ToStringToMethodCallOrPropertyFetchRector; use Neos\Rector\Generic\ValueObject\AddInjection; -use Neos\Rector\Generic\ValueObject\FusionFlowQueryNodePropertyToWarningComment; -use Neos\Rector\Generic\ValueObject\FusionNodePropertyPathToWarningComment; -use Neos\Rector\Generic\ValueObject\FusionPrototypeNameAddComment; -use Neos\Rector\Generic\ValueObject\FusionPrototypeNameReplacement; use Neos\Rector\Generic\ValueObject\MethodCallToWarningComment; use Neos\Rector\Generic\ValueObject\ObjectInstantiationToWarningComment; use Neos\Rector\Generic\ValueObject\RemoveInjection; @@ -105,744 +72,646 @@ use Rector\Renaming\Rector\MethodCall\RenameMethodRector; use Rector\Renaming\Rector\Name\RenameClassRector; use Rector\Renaming\ValueObject\MethodCallRename; -use Rector\Transform\Rector\MethodCall\MethodCallToPropertyFetchRector; -use Rector\Transform\ValueObject\MethodCallToPropertyFetch; - -return static function (RectorConfig $rectorConfig): void { - // Register FusionFileProcessor. All Fusion Rectors will be auto-registered at this processor. - $services = $rectorConfig->services(); - $services->defaults() - ->public() - ->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 - - $rectorConfig->autoloadPaths([__DIR__ . '/../../src/ContentRepository90/Legacy']); - - $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ - \Neos\ContentRepository\Domain\Model\Node::class => NodeLegacyStub::class, - \Neos\ContentRepository\Domain\Model\NodeInterface::class => NodeLegacyStub::class, - \Neos\ContentRepository\Domain\Projection\Content\NodeInterface::class => NodeLegacyStub::class, - \Neos\ContentRepository\Domain\Projection\Content\TraversableNodeInterface::class => NodeLegacyStub::class, - - \Neos\ContentRepository\Domain\Projection\Content\TraversableNodes::class => \Neos\ContentRepository\Core\Projection\ContentGraph\Nodes::class, - - \Neos\ContentRepository\Domain\Service\Context::class => LegacyContextStub::class, - \Neos\Neos\Domain\Service\ContentContext::class => LegacyContextStub::class, - - \Neos\ContentRepository\Domain\Model\NodeType::class => \Neos\ContentRepository\Core\NodeType\NodeType::class, - \Neos\ContentRepository\Domain\Service\NodeTypeManager::class => \Neos\ContentRepository\Core\NodeType\NodeTypeManager::class, - - \Neos\ContentRepository\Domain\Model\Workspace::class => \Neos\ContentRepository\Core\SharedModel\Workspace\Workspace::class, - \Neos\ContentRepository\Domain\NodeAggregate\NodeAggregateIdentifier::class => \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId::class, - \Neos\ContentRepository\Domain\NodeAggregate\NodeName::class => \Neos\ContentRepository\Core\SharedModel\Node\NodeName::class, - \Neos\ContentRepository\Domain\NodeType\NodeTypeName::class => \Neos\ContentRepository\Core\NodeType\NodeTypeName::class, - \Neos\ContentRepository\Domain\Projection\Content\PropertyCollectionInterface::class => \Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollection::class, - \Neos\ContentRepository\Domain\Model\ArrayPropertyCollection::class => \Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollection::class, - \Neos\Neos\Routing\FrontendNodeRoutePartHandlerInterface::class => \Neos\Neos\FrontendRouting\FrontendNodeRoutePartHandlerInterface::class, - ]); - - $rectorConfig->ruleWithConfiguration(FusionReplacePrototypeNameRector::class, [ - new FusionPrototypeNameReplacement('Neos.Fusion:Array', 'Neos.Fusion:Join'), - new FusionPrototypeNameReplacement('Neos.Fusion:RawArray', 'Neos.Fusion:DataStructure'), - new FusionPrototypeNameReplacement('Neos.Fusion:Collection', 'Neos.Fusion:Loop', - 'Migration of Neos.Fusion:Collection to Neos.Fusion:Loop needs manual action. The key `collection` has to be renamed to `items` which cannot be done automatically' - ), - new FusionPrototypeNameReplacement('Neos.Fusion:RawCollection', 'Neos.Fusion:Map', - 'Migration of Neos.Fusion:RawCollection to Neos.Fusion:Map needs manual action. The key `collection` has to be renamed to `items` which cannot be done automatically' - ), - new FusionPrototypeNameReplacement('Neos.Neos:PrimaryContent', 'Neos.Neos:ContentCollection', '"Neos.Neos:PrimaryContent" has been removed without a complete replacement. We replaced all usages with "Neos.Neos:ContentCollection" but not the prototype definition. Please check the replacements and if you have overridden the "Neos.Neos:PrimaryContent" prototype and rewrite it for your needs.', true), - ]); - - - /** @var MethodCallToPropertyFetch[] $methodCallToPropertyFetches */ - $methodCallToPropertyFetches = []; - - /** @var MethodCallToWarningComment[] $methodCallToWarningComments */ - $methodCallToWarningComments = []; - - - $fusionFlowQueryPropertyToComments = []; - /** - * Neos\ContentRepository\Domain\Model\NodeInterface - */ - // setName - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setName', '!! Node::setName() is not supported by the new CR. Use the "ChangeNodeAggregateName" command to change the node name.'); - // getName - $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getName', 'nodeName'); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_name', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_name")" to "VARIABLE.nodeName". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // getLabel - $rectorConfig->rule(FusionNodeLabelRector::class); - $rectorConfig->rule(NodeLabelGeneratorRector::class); - // setProperty - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setProperty', '!! Node::setProperty() is not supported by the new CR. Use the "SetNodeProperties" command to change property values.'); - // hasProperty -> compatible with ES CR Node (nothing to do) - // getProperty -> compatible with ES CR Node (nothing to do) - // removeProperty - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'removeProperty', '!! Node::removeProperty() is not supported by the new CR. Use the "SetNodeProperties" command to remove a property values.'); - // getProperties -> PropertyCollectionInterface - $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getProperties', 'properties'); - // getPropertyNames - $rectorConfig->rule(NodeGetPropertyNamesRector::class); - // setContentObject -> DEPRECATED / NON-FUNCTIONAL - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setContentObject', '!! Node::setContentObject() is not supported by the new CR. Referencing objects can be done by storing them in Node::properties (and the serialization/deserialization is extensible).'); - // getContentObject -> DEPRECATED / NON-FUNCTIONAL - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getContentObject', '!! Node::getContentObject() is not supported by the new CR. Referencing objects can be done by storing them in Node::properties (and the serialization/deserialization is extensible).'); - // unsetContentObject -> DEPRECATED / NON-FUNCTIONAL - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'unsetContentObject', '!! Node::unsetContentObject() is not supported by the new CR. Referencing objects can be done by storing them in Node::properties (and the serialization/deserialization is extensible).'); - // setNodeType - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setNodeType', '!! Node::setNodeType() is not supported by the new CR. Use the "ChangeNodeAggregateType" command to change nodetype.'); - // getNodeType: NodeType - // PHP: shortcut to Node->nodeTypeName->value - $rectorConfig->rule(NodeGetNodeTypeGetNameRector::class); - $rectorConfig->rule(NodeGetNodeTypeRector::class); - // Fusion: node.nodeType -> Neos.Node.nodeType(node) - // Fusion: node.nodeType.name -> node.nodeTypeName - $rectorConfig->rule(FusionNodeNodeTypeRector::class); - // setHidden - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setHidden', '!! Node::setHidden() is not supported by the new CR. Use the "EnableNodeAggregate" or "DisableNodeAggregate" command to change the visibility of the node.'); - // isHidden - $rectorConfig->rule(NodeIsHiddenRector::class); - $rectorConfig->rule(FusionNodeHiddenRector::class); - // TODO: Fusion NodeAccess - // setHiddenBeforeDateTime - $rectorConfig->rule(NodeGetHiddenBeforeAfterDateTimeRector::class); - // getHiddenBeforeDateTime - // PHP: Covered by NodeGetHiddenBeforeAfterDateTimeRector - $rectorConfig->rule(FusionNodeHiddenBeforeDateTimeRector::class); - // setHiddenAfterDateTime - // PHP: Covered by NodeGetHiddenBeforeAfterDateTimeRector - // getHiddenAfterDateTime - // PHP: Covered by NodeGetHiddenBeforeAfterDateTimeRector - $rectorConfig->rule(FusionNodeHiddenAfterDateTimeRector::class); - // setHiddenInIndex - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setHiddenInIndex', '!! Node::setHiddenInIndex() is not supported by the new CR. Use the "SetNodeProperties" command to change the property value for "hiddenInMenu".'); - // isHiddenInIndex - $rectorConfig->rule(NodeIsHiddenInIndexRector::class); - // Fusion: .hiddenInIndex -> node.properties._hiddenInIndex - $rectorConfig->rule(FusionNodeHiddenInIndexRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_hiddenInIndex', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_hiddenInIndex")" to "VARIABLE.property(\'hiddenInMenu\')". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // setAccessRoles - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setAccessRoles', '!! Node::setAccessRoles() is not supported by the new CR.'); - // getAccessRoles - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getAccessRoles', '!! Node::getAccessRoles() is not supported by the new CR.'); - // getPath - $rectorConfig->rule(NodeGetPathRector::class); - $rectorConfig->rule(FusionNodePathRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_path', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_path")" to "Neos.Node.path(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // getContextPath - $rectorConfig->rule(NodeGetContextPathRector::class); - $rectorConfig->rule(FusionNodeContextPathRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_contextPath', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_contextPath")" to "Neos.Node.serializedNodeAddress(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // getDepth - $rectorConfig->rule(NodeGetDepthRector::class); - $rectorConfig->rule(FusionNodeDepthRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_depth', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_depth")" to "Neos.Node.depth(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // setWorkspace -> internal - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::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.'); - // getWorkspace - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::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.'); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_workspace', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_workspace")". It 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 - $rectorConfig->rule(NodeGetIdentifierRector::class); - $rectorConfig->rule(FusionNodeIdentifierRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_identifier', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_identifier")" to "VARIABLE.aggregateId". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // setIndex -> internal - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setIndex', '!! Node::setIndex() was always internal. To reorder nodes, use the "MoveNodeAggregate" command'); - // getIndex - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getIndex', '!! Node::getIndex() is not supported. You can fetch all siblings and inspect the ordering'); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_index', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_index")". You can fetch all siblings and inspect the ordering.'); - // getParent -> Node - $rectorConfig->rule(NodeGetParentRector::class); - $rectorConfig->rule(FusionNodeParentRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_parent', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_parent")" to "q(VARIABLE).parent().get(0)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - // getParentPath - deprecated - // createNode - // createSingleNode -> internal - // createNodeFromTemplate - // getNode(relative path) - deprecated - // getPrimaryChildNode() - deprecated - // getChildNodes($nodeTypeFilter, $limit, $offset) - deprecated - $rectorConfig->rule(NodeGetChildNodesRector::class); - // hasChildNodes($nodeTypeFilter) - deprecated - // remove() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'remove', '!! Node::remove() is not supported by the new CR. Use the "RemoveNodeAggregate" command to remove a node.'); - // setRemoved() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setRemoved', '!! Node::setRemoved() is not supported by the new CR. Use the "RemoveNodeAggregate" command to remove a node.'); - // isRemoved() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'isRemoved', '!! Node::isRemoved() - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios.'); - $fusionNodePropertyPathToWarningComments[] = 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.'); - // isVisible() - // isAccessible() - // hasAccessRestrictions() - // isNodeTypeAllowedAsChildNode() - // moveBefore() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'moveBefore', '!! Node::moveBefore() is not supported by the new CR. Use the "MoveNodeAggregate" command to move a node.'); - // moveAfter() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'moveAfter', '!! Node::moveAfter() is not supported by the new CR. Use the "MoveNodeAggregate" command to move a node.'); - // moveInto() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'moveInto', '!! Node::moveInto() is not supported by the new CR. Use the "MoveNodeAggregate" command to move a node.'); - // copyBefore() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'copyBefore', '!! Node::copyBefore() is not supported by the new CR. Use the "NodeDuplicationService::copyNodesRecursively" to copy a node.'); - // copyAfter() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'copyAfter', '!! Node::copyAfter() is not supported by the new CR. Use the "NodeDuplicationService::copyNodesRecursively" to copy a node.'); - // copyInto() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'copyInto', '!! Node::copyInto() is not supported by the new CR. Use the "NodeDuplicationService::copyNodesRecursively" to copy a node.'); - // getNodeData() - $methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getNodeData', '!! Node::getNodeData() - the new CR is not based around the concept of NodeData anymore. You need to rewrite your code here.'); - // getContext() - // getContext()->getWorkspace() - $rectorConfig->rule(NodeGetContextGetWorkspaceRector::class); - // TODO: Fusion - // getContext()->getWorkspaceName() - $rectorConfig->rule(NodeGetContextGetWorkspaceNameRector::class); - // TODO: Fusion - // getDimensions() - $rectorConfig->rule(NodeGetDimensionsRector::class); - // TODO: Fusion - // createVariantForContext() - // isAutoCreated() - $rectorConfig->rule(NodeIsAutoCreatedRector::class); - $rectorConfig->rule(FusionNodeAutoCreatedRector::class); - $fusionFlowQueryPropertyToComments[] = new FusionFlowQueryNodePropertyToWarningComment('_autoCreated', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_autoCreated")" to "VARIABLE.classification.tethered". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'); - - // getOtherNodeVariants() - - $rectorConfig->ruleWithConfiguration(FusionFlowQueryNodePropertyToWarningCommentRector::class, $fusionFlowQueryPropertyToComments); - - - /** - * Neos\ContentRepository\Domain\Projection\Content\NodeInterface - */ - // isRoot() - // isTethered() - // getContentStreamIdentifier() -> threw exception in <= Neos 8.0 - so nobody could have used this - // getNodeAggregateIdentifier() - $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getNodeAggregateIdentifier', 'aggregateId'); - $rectorConfig->rule(rectorClass: FusionNodeAggregateIdentifierRector::class); - // getNodeTypeName() - $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getNodeTypeName', 'nodeTypeName'); - // getNodeType() ** (included/compatible in old NodeInterface) - // getNodeName() - $methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getNodeName', 'nodeName'); - // getOriginDimensionSpacePoint() -> threw exception in <= Neos 8.0 - so nobody could have used this - // getProperties() ** (included/compatible in old NodeInterface) - // getProperty() ** (included/compatible in old NodeInterface) - // hasProperty() ** (included/compatible in old NodeInterface) - // getLabel() ** (included/compatible in old NodeInterface) - - /** - * Neos\ContentRepository\Core\NodeType\NodeType - */ - // getName() - $rectorConfig->rule(FusionNodeTypeNameRector::class); - $rectorConfig->rule(NodeTypeGetNameRector::class); - // getAutoCreatedChildNodes - $rectorConfig->rule(NodeTypeGetAutoCreatedChildNodesRector::class); - // hasAutoCreatedChildNode - $rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [new MethodCallRename( - NodeType::class, - 'hasAutoCreatedChildNode', - 'hasTetheredNode' - )]); - // getTypeOfAutoCreatedChildNode - $rectorConfig->rule(NodeTypeGetTypeOfAutoCreatedChildNodeRector::class); - // allowsGrandchildNodeType - $rectorConfig->rule(NodeTypeAllowsGrandchildNodeTypeRector::class); - - - /** - * Neos\ContentRepository\Domain\Projection\Content\TraversableNodeInterface - */ - // getDimensionSpacePoint() -> threw exception in <= Neos 8.0 - so nobody could have used this - // findParentNode() -> TraversableNodeInterface - $rectorConfig->rule(NodeFindParentNodeRector::class); - // findNodePath() -> NodePath - // TODO: PHP - // findNamedChildNode(NodeName $nodeName): TraversableNodeInterface; - // TODO: PHP - // findChildNodes(NodeTypeConstraints $nodeTypeConstraints = null, int $limit = null, int $offset = null): TraversableNodes; - // TODO: PHP - // countChildNodes(NodeTypeConstraints $nodeTypeConstraints = null): int; - // TODO: PHP - // findReferencedNodes(): TraversableNodes; - // TODO: PHP - // findNamedReferencedNodes(PropertyName $edgeName): TraversableNodes; - // TODO: PHP - // findReferencingNodes() -> threw exception in <= Neos 8.0 - so nobody could have used this - // findNamedReferencingNodes() -> threw exception in <= Neos 8.0 - so nobody could have used this - - - /** - * Context - */ - $rectorConfig->rule(ContextFactoryToLegacyContextStubRector::class); - // Context::getWorkspaceName() - // TODO: PHP - $rectorConfig->rule(FusionContextGetWorkspaceNameRector::class); - // Context::getRootNode() - $rectorConfig->rule(ContextGetRootNodeRector::class); - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.rootNode', 'Line %LINE: !! node.context.rootNode is removed in Neos 9.0.'); - // Context::getNode() - // TODO: PHP - // Context::getNodeByIdentifier() - // TODO: PHP - // Context::getNodeVariantsByIdentifier() - // TODO: PHP - // Context::getNodesOnPath() - // TODO: PHP - // Context::adoptNode() - // TODO: PHP - // Context::getFirstLevelNodeCache() - $rectorConfig->rule(ContextGetFirstLevelNodeCacheRector::class); - // getCurrentDateTime(): DateTime|DateTimeInterface - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.currentDateTime', 'Line %LINE: !! node.context.currentDateTime is removed in Neos 9.0.'); - // getDimensions(): array - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.dimensions', 'Line %LINE: !! node.context.dimensions is removed in Neos 9.0. You can get node DimensionSpacePoints via node.dimensionSpacePoints now or use the `Neos.Dimension.*` helper.'); - // getNodeByIdentifier(identifier: string): NodeInterface|null - // TODO: PHP - // getProperties(): array - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.properties', 'Line %LINE: !! node.context.properties is removed in Neos 9.0.'); - // getTargetDimensions(): array - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.targetDimensions', 'Line %LINE: !! node.context.targetDimensions is removed in Neos 9.0.'); - // getTargetDimensionValues(): array - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.targetDimensionValues', 'Line %LINE: !! node.context.targetDimensionValues is removed in Neos 9.0.'); - // getWorkspace([createWorkspaceIfNecessary: bool = true]): Workspace - // TODO: PHP - $rectorConfig->rule(FusionContextGetWorkspaceRector::class); - // isInaccessibleContentShown(): bool - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.isInaccessibleContentShown', 'Line %LINE: !! node.context.isInaccessibleContentShown is removed in Neos 9.0.'); - // isInvisibleContentShown(): bool - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.isInvisibleContentShown', 'Line %LINE: !! node.context.isInvisibleContentShown is removed in Neos 9.0.'); - // isRemovedContentShown(): bool - // TODO: PHP - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.isRemovedContentShown', 'Line %LINE: !! node.context.isRemovedContentShown is removed in Neos 9.0.'); - // validateWorkspace(workspace: Workspace): void - // TODO: PHP - - - - /** - * ContentContext - */ - // ContentContext::getCurrentSite - $methodCallToWarningComments[] = new MethodCallToWarningComment(LegacyContextStub::class, 'getCurrentSite', '!! ContentContext::getCurrentSite() is removed in Neos 9.0.'); - $rectorConfig->rule(FusionContextCurrentSiteRector::class); - // ContentContext::getCurrentDomain - $methodCallToWarningComments[] = new MethodCallToWarningComment(LegacyContextStub::class, 'getCurrentDomain', '!! ContentContext::getCurrentDomain() is removed in Neos 9.0.'); - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.currentDomain', 'Line %LINE: !! node.context.currentDomain is removed in Neos 9.0.'); - // ContentContext::getCurrentSiteNode - $methodCallToWarningComments[] = new MethodCallToWarningComment(LegacyContextStub::class, 'getCurrentSiteNode', '!! ContentContext::getCurrentSiteNode() is removed in Neos 9.0. Use Subgraph and traverse up to "Neos.Neos:Site" node.'); - $fusionNodePropertyPathToWarningComments[] = new FusionNodePropertyPathToWarningComment('context.currentSiteNode', 'Line %LINE: !! node.context.currentSiteNode is removed in Neos 9.0. Check if you can\'t simply use ${site}.'); - // ContentContext::isLive -> renderingMode.isLive - $rectorConfig->rule(ContextIsLiveRector::class); - $rectorConfig->rule(FusionContextLiveRector::class); - // ContentContext::isInBackend -> renderingMode.inBackend - $rectorConfig->rule(ContextIsInBackendRector::class); - $rectorConfig->rule(FusionContextInBackendRector::class); - // ContentContext::getCurrentRenderingMode... -> renderingMode... - $rectorConfig->rule(ContextGetCurrentRenderingModeRector::class); - $rectorConfig->rule(FusionContextCurrentRenderingModeRector::class); - - /** - * CreateContentContextTrait - */ - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\Neos\Controller\CreateContentContextTrait::class, 'createContentContext', '!! CreateContentContextTrait::createContentContext() is removed in Neos 9.0.'); - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\Neos\Controller\CreateContentContextTrait::class, 'createContextMatchingNodeData', '!! CreateContentContextTrait::createContextMatchingNodeData() is removed in Neos 9.0.'); - - /** - * CacheLifetimeOperation - */ - $rectorConfig->rule(FusionCacheLifetimeRector::class); - - - /** - * ContentDimensionCombinator - */ - // ContentDimensionCombinator::getAllAllowedCombinations - $rectorConfig->rule(ContentDimensionCombinatorGetAllAllowedCombinationsRector::class); - - /** - * Neos\Neos\Domain\Service\NodeSearchService - */ - $rectorConfig->rule(NodeSearchServiceRector::class); - - - /** - * Neos\ContentRepository\Domain\Factory\NodeFactory - */ - // TODO: What other methods? - // NodeFactory::reset - $rectorConfig->rule(NodeFactoryResetRector::class); - - /** - * Neos\ContentRepository\Domain\Repository\WorkspaceRepository - */ - // countByName(workspace): int - $rectorConfig->rule(WorkspaceRepositoryCountByNameRector::class); - // findByBaseWorkspace(baseWorkspace): QueryResultInterface - $rectorConfig->rule(WorkspaceRepositoryFindByBaseWorkspaceRector::class); - // findByIdentifier(baseWorkspace): Workspace - $rectorConfig->rule(WorkspaceRepositoryFindByIdentifierRector::class); - - /** - * Neos\ContentRepository\Domain\Model\Workspace - */ - $rectorConfig->rule(WorkspaceGetNameRector::class); - - /** - * NodeTemplate - */ - $nodeTemplateWarningMessage = '!! NodeTemplate::%2$s is removed in Neos 9.0. Use the "CreateNodeAggregateWithNode" command to create new nodes or "CreateNodeVariant" command to create variants of an existing node in other dimensions.'; - // getIdentifier(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'getIdentifier', $nodeTemplateWarningMessage); - // getName(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'getName', $nodeTemplateWarningMessage); - // getWorkspace(): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'getWorkspace', $nodeTemplateWarningMessage); - // setIdentifier(identifier: string): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'setIdentifier', $nodeTemplateWarningMessage); - // setName(newName: string): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'setName', $nodeTemplateWarningMessage); - - /** - * ObjectInstantiationToWarningComment - */ - $rectorConfig->ruleWithConfiguration(ObjectInstantiationToWarningCommentRector::class, [ +use Neos\Rector\Generic\Rules\MethodCallToPropertyFetchRector; +use Neos\Rector\Generic\ValueObject\MethodCallToPropertyFetch; + + +$rectorConfig = RectorConfig::configure() + ->withoutParallel() + ->withAutoloadPaths( + [__DIR__ . '/../../src/ContentRepository90/Legacy'] + ); + + +/** @var MethodCallToPropertyFetch[] $methodCallToPropertyFetches */ +$methodCallToPropertyFetches = []; + +/** @var MethodCallToWarningComment[] $methodCallToWarningComments */ +$methodCallToWarningComments = []; + + +/** + * Neos\ContentRepository\Domain\Model\NodeInterface + */ +// setName +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setName', '!! Node::setName() is not supported by the new CR. Use the "ChangeNodeAggregateName" command to change the node name.'); +// getName +$methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getName', 'nodeName'); +$rectorConfig->withRules([NodeLabelGeneratorRector::class]); +// setProperty +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setProperty', '!! Node::setProperty() is not supported by the new CR. Use the "SetNodeProperties" command to change property values.'); +// hasProperty -> compatible with ES CR Node (nothing to do) +// getProperty -> compatible with ES CR Node (nothing to do) +// removeProperty +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'removeProperty', '!! Node::removeProperty() is not supported by the new CR. Use the "SetNodeProperties" command to remove a property values.'); +// getProperties -> PropertyCollectionInterface +$methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getProperties', 'properties'); +// getPropertyNames +$rectorConfig->withRules([NodeGetPropertyNamesRector::class]); +// setContentObject -> DEPRECATED / NON-FUNCTIONAL +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setContentObject', '!! Node::setContentObject() is not supported by the new CR. Referencing objects can be done by storing them in Node::properties (and the serialization/deserialization is extensible).'); +// getContentObject -> DEPRECATED / NON-FUNCTIONAL +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getContentObject', '!! Node::getContentObject() is not supported by the new CR. Referencing objects can be done by storing them in Node::properties (and the serialization/deserialization is extensible).'); +// unsetContentObject -> DEPRECATED / NON-FUNCTIONAL +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'unsetContentObject', '!! Node::unsetContentObject() is not supported by the new CR. Referencing objects can be done by storing them in Node::properties (and the serialization/deserialization is extensible).'); +// setNodeType +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setNodeType', '!! Node::setNodeType() is not supported by the new CR. Use the "ChangeNodeAggregateType" command to change nodetype.'); +// getNodeType: NodeType +// PHP: shortcut to Node->nodeTypeName->value +$rectorConfig->withRules([NodeGetNodeTypeGetNameRector::class]); +$rectorConfig->withRules([NodeGetNodeTypeRector::class]); +// setHidden +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setHidden', '!! Node::setHidden() is not supported by the new CR. Use the "EnableNodeAggregate" or "DisableNodeAggregate" command to change the visibility of the node.'); +// isHidden +$rectorConfig->withRules([NodeIsHiddenRector::class]); +// setHiddenBeforeDateTime +$rectorConfig->withRules([NodeGetHiddenBeforeAfterDateTimeRector::class]); +// getHiddenBeforeDateTime +// PHP: Covered by NodeGetHiddenBeforeAfterDateTimeRector +// setHiddenAfterDateTime +// PHP: Covered by NodeGetHiddenBeforeAfterDateTimeRector +// getHiddenAfterDateTime +// PHP: Covered by NodeGetHiddenBeforeAfterDateTimeRector +// setHiddenInIndex +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setHiddenInIndex', '!! Node::setHiddenInIndex() is not supported by the new CR. Use the "SetNodeProperties" command to change the property value for "hiddenInMenu".'); +// isHiddenInIndex +$rectorConfig->withRules([NodeIsHiddenInIndexRector::class]); +// setAccessRoles +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setAccessRoles', '!! Node::setAccessRoles() is not supported by the new CR.'); +// getAccessRoles +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getAccessRoles', '!! Node::getAccessRoles() is not supported by the new CR.'); +// getPath +$rectorConfig->withRules([NodeGetPathRector::class]); +// getContextPath +$rectorConfig->withRules([NodeGetContextPathRector::class]); +// getDepth +$rectorConfig->withRules([NodeGetDepthRector::class]); +// setWorkspace -> internal +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::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.'); +// getWorkspace +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::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 +$rectorConfig->withRules([NodeGetIdentifierRector::class]); +// setIndex -> internal +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setIndex', '!! Node::setIndex() was always internal. To reorder nodes, use the "MoveNodeAggregate" command'); +// getIndex +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getIndex', '!! Node::getIndex() is not supported. You can fetch all siblings and inspect the ordering'); +// getParent -> Node +$rectorConfig->withRules([NodeGetParentRector::class]); +// getParentPath - deprecated +// createNode +// createSingleNode -> internal +// createNodeFromTemplate +// getNode(relative path) - deprecated +// getPrimaryChildNode() - deprecated +// getChildNodes($nodeTypeFilter, $limit, $offset) - deprecated +$rectorConfig->withRules([NodeGetChildNodesRector::class]); +// hasChildNodes($nodeTypeFilter) - deprecated +// remove() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'remove', '!! Node::remove() is not supported by the new CR. Use the "RemoveNodeAggregate" command to remove a node.'); +// setRemoved() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'setRemoved', '!! Node::setRemoved() is not supported by the new CR. Use the "RemoveNodeAggregate" command to remove a node.'); +// isRemoved() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'isRemoved', '!! Node::isRemoved() - the new CR *never* returns removed nodes; so you can simplify your code and just assume removed == FALSE in all scenarios.'); +// isVisible() +// isAccessible() +// hasAccessRestrictions() +// isNodeTypeAllowedAsChildNode() +// moveBefore() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'moveBefore', '!! Node::moveBefore() is not supported by the new CR. Use the "MoveNodeAggregate" command to move a node.'); +// moveAfter() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'moveAfter', '!! Node::moveAfter() is not supported by the new CR. Use the "MoveNodeAggregate" command to move a node.'); +// moveInto() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'moveInto', '!! Node::moveInto() is not supported by the new CR. Use the "MoveNodeAggregate" command to move a node.'); +// copyBefore() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'copyBefore', '!! Node::copyBefore() is not supported by the new CR. Use the "NodeDuplicationService::copyNodesRecursively" to copy a node.'); +// copyAfter() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'copyAfter', '!! Node::copyAfter() is not supported by the new CR. Use the "NodeDuplicationService::copyNodesRecursively" to copy a node.'); +// copyInto() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'copyInto', '!! Node::copyInto() is not supported by the new CR. Use the "NodeDuplicationService::copyNodesRecursively" to copy a node.'); +// getNodeData() +$methodCallToWarningComments[] = new MethodCallToWarningComment(NodeLegacyStub::class, 'getNodeData', '!! Node::getNodeData() - the new CR is not based around the concept of NodeData anymore. You need to rewrite your code here.'); +// getContext() +// getContext()->getWorkspace() +$rectorConfig->withRules([NodeGetContextGetWorkspaceRector::class]); +// getContext()->getWorkspaceName() +$rectorConfig->withRules([NodeGetContextGetWorkspaceNameRector::class]); +// getDimensions() +$rectorConfig->withRules([NodeGetDimensionsRector::class]); +// createVariantForContext() +// isAutoCreated() +$rectorConfig->withRules([NodeIsAutoCreatedRector::class]); + +// getOtherNodeVariants() + + +/** + * Neos\ContentRepository\Domain\Projection\Content\NodeInterface + */ +// isRoot() +// isTethered() +// getContentStreamIdentifier() -> threw exception in <= Neos 8.0 - so nobody could have used this +// getNodeAggregateIdentifier() +$methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getNodeAggregateIdentifier', 'aggregateId'); +// getNodeTypeName() +$methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getNodeTypeName', 'nodeTypeName'); +// getNodeType() ** (included/compatible in old NodeInterface) +// getNodeName() +$methodCallToPropertyFetches[] = new MethodCallToPropertyFetch(NodeLegacyStub::class, 'getNodeName', 'nodeName'); +// getOriginDimensionSpacePoint() -> threw exception in <= Neos 8.0 - so nobody could have used this +// getProperties() ** (included/compatible in old NodeInterface) +// getProperty() ** (included/compatible in old NodeInterface) +// hasProperty() ** (included/compatible in old NodeInterface) +// getLabel() ** (included/compatible in old NodeInterface) + +/** + * Neos\ContentRepository\Core\NodeType\NodeType + */ +// getName() +$rectorConfig->withRules([NodeTypeGetNameRector::class]); +// getAutoCreatedChildNodes +$rectorConfig->withRules([NodeTypeGetAutoCreatedChildNodesRector::class]); +// hasAutoCreatedChildNode +$rectorConfig->withConfiguredRule(RenameMethodRector::class, [new MethodCallRename( + NodeType::class, + 'hasAutoCreatedChildNode', + 'hasTetheredNode' +)]); +// getTypeOfAutoCreatedChildNode +$rectorConfig->withRules([NodeTypeGetTypeOfAutoCreatedChildNodeRector::class]); +// allowsGrandchildNodeType +$rectorConfig->withRules([NodeTypeAllowsGrandchildNodeTypeRector::class]); + + +/** + * Neos\ContentRepository\Domain\Projection\Content\TraversableNodeInterface + */ +// getDimensionSpacePoint() -> threw exception in <= Neos 8.0 - so nobody could have used this +// findParentNode() -> TraversableNodeInterface +$rectorConfig->withRules([NodeFindParentNodeRector::class]); +// findNodePath() -> NodePath +// TODO: PHP +// findNamedChildNode(NodeName $nodeName): TraversableNodeInterface; +// TODO: PHP +// findChildNodes(NodeTypeConstraints $nodeTypeConstraints = null, int $limit = null, int $offset = null): TraversableNodes; +// TODO: PHP +// countChildNodes(NodeTypeConstraints $nodeTypeConstraints = null): int; +// TODO: PHP +// findReferencedNodes(): TraversableNodes; +// TODO: PHP +// findNamedReferencedNodes(PropertyName $edgeName): TraversableNodes; +// TODO: PHP +// findReferencingNodes() -> threw exception in <= Neos 8.0 - so nobody could have used this +// findNamedReferencingNodes() -> threw exception in <= Neos 8.0 - so nobody could have used this + + +/** + * Context + */ +$rectorConfig->withRules([ContextFactoryToLegacyContextStubRector::class]); +// Context::getWorkspaceName() +// TODO: PHP +// Context::getRootNode() +$rectorConfig->withRules([ContextGetRootNodeRector::class]); +// Context::getNode() +// TODO: PHP +// Context::getNodeByIdentifier() +// TODO: PHP +// Context::getNodeVariantsByIdentifier() +// TODO: PHP +// Context::getNodesOnPath() +// TODO: PHP +// Context::adoptNode() +// TODO: PHP +// Context::getFirstLevelNodeCache() +$rectorConfig->withRules([ContextGetFirstLevelNodeCacheRector::class]); +// getCurrentDateTime(): DateTime|DateTimeInterface +// TODO: PHP +// getDimensions(): array +// TODO: PHP +// getNodeByIdentifier(identifier: string): NodeInterface|null +// TODO: PHP +// getProperties(): array +// TODO: PHP +// getTargetDimensions(): array +// TODO: PHP +// getTargetDimensionValues(): array +// TODO: PHP +// getWorkspace([createWorkspaceIfNecessary: bool = true]): Workspace +// TODO: PHP +// isInaccessibleContentShown(): bool +// TODO: PHP +// isInvisibleContentShown(): bool +// TODO: PHP +// isRemovedContentShown(): bool +// TODO: PHP +// validateWorkspace(workspace: Workspace): void +// TODO: PHP + + +/** + * ContentContext + */ +// ContentContext::getCurrentSite +$methodCallToWarningComments[] = new MethodCallToWarningComment(LegacyContextStub::class, 'getCurrentSite', '!! ContentContext::getCurrentSite() is removed in Neos 9.0.'); +// ContentContext::getCurrentDomain +$methodCallToWarningComments[] = new MethodCallToWarningComment(LegacyContextStub::class, 'getCurrentDomain', '!! ContentContext::getCurrentDomain() is removed in Neos 9.0.'); +// ContentContext::getCurrentSiteNode +$methodCallToWarningComments[] = new MethodCallToWarningComment(LegacyContextStub::class, 'getCurrentSiteNode', '!! ContentContext::getCurrentSiteNode() is removed in Neos 9.0. Use Subgraph and traverse up to "Neos.Neos:Site" node.'); +// ContentContext::isLive -> renderingMode.isLive +$rectorConfig->withRules([ContextIsLiveRector::class]); +// ContentContext::isInBackend -> renderingMode.inBackend +$rectorConfig->withRules([ContextIsInBackendRector::class]); +// ContentContext::getCurrentRenderingMode... -> renderingMode... +$rectorConfig->withRules([ContextGetCurrentRenderingModeRector::class]); + +/** + * CreateContentContextTrait + */ +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\Neos\Controller\CreateContentContextTrait::class, 'createContentContext', '!! CreateContentContextTrait::createContentContext() is removed in Neos 9.0.'); +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\Neos\Controller\CreateContentContextTrait::class, 'createContextMatchingNodeData', '!! CreateContentContextTrait::createContextMatchingNodeData() is removed in Neos 9.0.'); + +/** + * CacheLifetimeOperation + */ + + +/** + * ContentDimensionCombinator + */ +// ContentDimensionCombinator::getAllAllowedCombinations +$rectorConfig->withRules([ContentDimensionCombinatorGetAllAllowedCombinationsRector::class]); + +/** + * Neos\Neos\Domain\Service\NodeSearchService + */ +$rectorConfig->withRules([NodeSearchServiceRector::class]); + + +/** + * Neos\ContentRepository\Domain\Factory\NodeFactory + */ +// TODO: What other methods? +// NodeFactory::reset +$rectorConfig->withRules([NodeFactoryResetRector::class]); + +/** + * Neos\ContentRepository\Domain\Repository\WorkspaceRepository + */ +// countByName(workspace): int +$rectorConfig->withRules([WorkspaceRepositoryCountByNameRector::class]); +// findByBaseWorkspace(baseWorkspace): QueryResultInterface +$rectorConfig->withRules([WorkspaceRepositoryFindByBaseWorkspaceRector::class]); +// findByIdentifier(baseWorkspace): Workspace +$rectorConfig->withRules([WorkspaceRepositoryFindByIdentifierRector::class]); + +/** + * Neos\ContentRepository\Domain\Model\Workspace + */ +$rectorConfig->withRules([WorkspaceGetNameRector::class]); + +/** + * NodeTemplate + */ +$nodeTemplateWarningMessage = '!! NodeTemplate::%2$s is removed in Neos 9.0. Use the "CreateNodeAggregateWithNode" command to create new nodes or "CreateNodeVariant" command to create variants of an existing node in other dimensions.'; +// getIdentifier(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'getIdentifier', $nodeTemplateWarningMessage); +// getName(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'getName', $nodeTemplateWarningMessage); +// getWorkspace(): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'getWorkspace', $nodeTemplateWarningMessage); +// setIdentifier(identifier: string): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'setIdentifier', $nodeTemplateWarningMessage); +// setName(newName: string): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, 'setName', $nodeTemplateWarningMessage); + +/** + * ObjectInstantiationToWarningComment + */ +$rectorConfig->withConfiguredRule(ObjectInstantiationToWarningCommentRector::class, [ new ObjectInstantiationToWarningComment(\Neos\ContentRepository\Domain\Model\NodeTemplate::class, '!! NodeTemplate is removed in Neos 9.0. Use the "CreateNodeAggregateWithNode" command to create new nodes or "CreateNodeVariant" command to create variants of an existing node in other dimensions.'), new ObjectInstantiationToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, '!! NodeTypeConstraints is removed in Neos 9.0. Please use the proper filter in subgraph finders e.g. "FindChildNodesFilter" for ContentSubgraphInterface::findChildNodes().') ] - ); - - /** - * Neos\ContentRepository\Domain\Service\NodeTypeManager - */ - $rectorConfig->rule(NodeTypeManagerAccessRector::class); - // createNodeType(nodeTypeName: string): NodeType - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Service\NodeTypeManager::class, 'createNodeType', '!! NodeTypeManager::createNodeType() was never implemented and is removed in Neos 9.0.'); - // getNodeType(nodeTypeName: string): NodeType - // --> Compatible with 9.0 - // getNodeTypes([includeAbstractNodeTypes: bool = true]): NodeType[] - // --> Compatible with 9.0 - // getSubNodeTypes(superTypeName: string, [includeAbstractNodeTypes: bool = true]): NodeType[] - // --> Compatible with 9.0 - // hasNodeType(nodeTypeName: string): bool - // --> Compatible with 9.0 - // overrideNodeTypes(completeNodeTypeConfiguration: array): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Service\NodeTypeManager::class, 'overrideNodeTypes', '!! NodeTypeManager::createNodeType() was never meant to be used outside of testing and is removed in Neos 9.0.'); - - /** - * NodeData - */ - $nodeDataWarningMessage = '!! NodeData::%2$s is removed in Neos 9.0 - the new CR is not based around the concept of NodeData anymore. You need to rewrite your code here.'; - // createNodeData(name: string, [nodeType: NodeType|null = null], [identifier: null|string = null], [workspace: Workspace|null = null], [dimensions: array|null = null]): NodeData - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createNodeData', $nodeDataWarningMessage); - // createNodeDataFromTemplate(nodeTemplate: NodeTemplate, [nodeName: null|string = null], [workspace: Workspace|null = null], [dimensions: array|null = null]): NodeData - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createNodeDataFromTemplate', $nodeDataWarningMessage); - // createShadow(path: string): NodeData - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createShadow', $nodeDataWarningMessage); - // createSingleNodeData(name: string, [nodeType: NodeType|null = null], [identifier: null|string = null], [workspace: Workspace|null = null], [dimensions: array|null = null]): NodeData - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createSingleNodeData', $nodeDataWarningMessage); - // getContextPath(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getContextPath', $nodeDataWarningMessage); - // getDepth(): int - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDepth', $nodeDataWarningMessage); - // getDimensions(): NodeDimension[] - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDimensions', $nodeDataWarningMessage); - // getDimensionsHash(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDimensionsHash', $nodeDataWarningMessage); - // getDimensionValues(): array - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDimensionValues', $nodeDataWarningMessage); - // getIdentifier(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getIdentifier', $nodeDataWarningMessage); - // getIndex(): int - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getIndex', $nodeDataWarningMessage); - // getMovedTo(): NodeData - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getMovedTo', $nodeDataWarningMessage); - // getName(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getName', $nodeDataWarningMessage); - // getNumberOfChildNodes(nodeTypeFilter: string, workspace: Workspace, dimensions: array): int - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getNumberOfChildNodes', $nodeDataWarningMessage); - // getParent(): NodeData|null - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getParent', $nodeDataWarningMessage); - // getParentPath(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getParentPath', $nodeDataWarningMessage); - // getPath(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getPath', $nodeDataWarningMessage); - // getWorkspace(): Workspace - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getWorkspace', $nodeDataWarningMessage); - // hasAccessRestrictions(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'hasAccessRestrictions', $nodeDataWarningMessage); - // isAccessible(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isAccessible', $nodeDataWarningMessage); - // isInternal(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isInternal', $nodeDataWarningMessage); - // isRemoved(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isRemoved', $nodeDataWarningMessage); - // isVisible(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isVisible', $nodeDataWarningMessage); - // matchesWorkspaceAndDimensions(workspace: Workspace, [dimensions: array|null = null]): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'matchesWorkspaceAndDimensions', $nodeDataWarningMessage); - // move(targetPath: string, targetWorkspace: Workspace): NodeData|null - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'targetPath', $nodeDataWarningMessage); - // remove(): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'remove', $nodeDataWarningMessage); - // setDimensions(dimensionsToBeSet: array): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setDimensions', $nodeDataWarningMessage); - // setIdentifier(identifier: string): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setIdentifier', $nodeDataWarningMessage); - // setIndex(index: int): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setIndex', $nodeDataWarningMessage); - // setMovedTo([nodeData: NodeData|null = null]): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setMovedTo', $nodeDataWarningMessage); - // setPath(path: string, [recursive: bool = true]): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setPath', $nodeDataWarningMessage); - // setRemoved(removed: bool): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setRemoved', $nodeDataWarningMessage); - // setWorkspace([workspace: Workspace|null = null]): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setWorkspace', $nodeDataWarningMessage); - // similarize(sourceNode: AbstractNodeData, [isCopy: bool = false]): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'similarize', $nodeDataWarningMessage); - - /** - * \Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory - */ - // parseFilterStrnig(serializedFilters: string): NodeTypeConstraints - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory::class, 'parseFilterString', '!! The "NodeTypeConstraintFactory" has been removed in Neos 9. Please use the proper filter in subgraph finders e.g. "FindChildNodesFilter" for ContentSubgraphInterface::findChildNodes().'); - - /** - * \Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints - */ - $nodeTypeConstraintsWarning = '!! NodeTypeConstraints has been removed in Neos 9. Please use the proper filter in subgraph finders e.g. "FindChildNodesFilter" for ContentSubgraphInterface::findChildNodes().'; - // asLegacyNodeTypeFilterString(): string - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'asLegacyNodeTypeFilterString', $nodeTypeConstraintsWarning); - // getExplicitlyAllowedNodeTypeNames(): NodeTypeName[] - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'getExplicitlyAllowedNodeTypeNames', $nodeTypeConstraintsWarning); - // getExplicitlyDisallowedNodeTypeNames(): NodeTypeName[] - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'getExplicitlyDisallowedNodeTypeNames', $nodeTypeConstraintsWarning); - // isWildcardAllowed(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'isWildcardAllowed', $nodeTypeConstraintsWarning); - // matches(nodeTypeName: NodeTypeName): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'nodeTypeName', $nodeTypeConstraintsWarning); - // withExplicitlyDisallowedNodeType(nodeTypeName: NodeTypeName): NodeTypeConstraints - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'withExplicitlyDisallowedNodeType', $nodeTypeConstraintsWarning); - - /** - * Signals and Slots - * https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots - */ - $signalsAndSlotsToComment = []; - // Neos\ContentRepository\Domain\Service\PublishingService - // - nodePublished - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\PublishingService::class, 'nodePublished', 'The signal "nodePublished" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodeDiscarded - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\PublishingService::class, 'nodeDiscarded', 'The signal "nodeDiscarded" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // Neos\Neos\Service\PublishingService - // - nodePublished - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\Neos\Service\PublishingService::class, 'nodePublished', 'The signal "nodePublished" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodeDiscarded - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\Neos\Service\PublishingService::class, 'nodeDiscarded', 'The signal "nodeDiscarded" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // Neos\ContentRepository\Domain\Service\Context - // - beforeAdoptNode - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\Context::class, 'beforeAdoptNode', 'The signal "beforeAdoptNode" on "Context" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - afterAdoptNode - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\Context::class, 'afterAdoptNode', 'The signal "afterAdoptNode" on "Context" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // Neos\ContentRepository\Domain\Repository\NodeDataRepository - // - repositoryObjectsPersisted - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Repository\NodeDataRepository::class, 'repositoryObjectsPersisted', 'The signal "repositoryObjectsPersisted" on "NodeDataRepository" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // Neos\ContentRepository\Domain\Model\Workspace - // - baseWorkspaceChanged - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'baseWorkspaceChanged', 'The signal "baseWorkspaceChanged" on "Workspace" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - beforeNodePublishing - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'beforeNodePublishing', 'The signal "beforeNodePublishing" on "Workspace" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - afterNodePublishing - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'afterNodePublishing', 'The signal "afterNodePublishing" on "Workspace" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // Neos\ContentRepository\Domain\Model\NodeData - // - nodePathChanged - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'nodePathChanged', 'The signal "nodePathChanged" on "NodeData" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // Neos\ContentRepository\Domain\Model\Node - // - beforeNodeMove - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodeMove', 'The signal "beforeNodeMove" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - afterNodeMove - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'afterNodeMove', 'The signal "afterNodeMove" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - beforeNodeCopy - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodeCopy', 'The signal "beforeNodeCopy" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - afterNodeCopy - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'afterNodeCopy', 'The signal "afterNodeCopy" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodePathChanged - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodePathChanged', 'The signal "nodePathChanged" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - beforeNodeCreate - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodeCreate', 'The signal "beforeNodeCreate" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - afterNodeCreate - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'afterNodeCreate', 'The signal "afterNodeCreate" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodeAdded - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodeAdded', 'The signal "nodeAdded" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodeUpdated - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodeUpdated', 'The signal "nodeUpdated" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodeRemoved - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodeRemoved', 'The signal "nodeRemoved" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - beforeNodePropertyChange - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodePropertyChange', 'The signal "beforeNodePropertyChange" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - // - nodePropertyChanged - $signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodePropertyChanged', 'The signal "nodePropertyChanged" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); - - $rectorConfig->ruleWithConfiguration(SignalSlotToWarningCommentRector::class, $signalsAndSlotsToComment); - - - /** - * Neos.Fusion:Attributes - */ - $rectorConfig->ruleWithConfiguration(FusionPrototypeNameAddCommentRector::class, [ - new FusionPrototypeNameAddComment('Neos.Fusion:Attributes', 'TODO 9.0 migration: Neos.Fusion:Attributes has been removed without a replacement. You need to replace it by the property attributes in "Neos.Fusion:Tag" or apply the Eel helper "Neos.Array.toHtmlAttributesString(attributes)".') - ]); - - $rectorConfig->rule(ContentRepositoryUtilityRenderValidNodeNameRector::class); - - /** - * FlowQuery Operation context() - */ - $rectorConfig->rule(FusionFlowQueryContextRector::class); - - /** - * \Neos\ContentRepository\Domain\Model\Workspace - */ - // getBaseWorkspace(): Workspace|null - $rectorConfig->rule(WorkspaceGetBaseWorkspaceRector::class); - // getBaseWorkspaces(): Workspace[] - $rectorConfig->rule(WorkspaceGetBaseWorkspacesRector::class); - // getDescription(): null|string - $rectorConfig->rule(WorkspaceGetDescriptionRector::class); - // getName(): string - // ->name - // getNodeCount(): int - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'getNodeCount', '!! Workspace::getNodeCount() has been removed in Neos 9.0 without a replacement.'); - // getOwner(): UserInterface|null - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'getOwner', '!! Workspace::getOwner() has been removed in Neos 9.0. Use WorkspaceService::getWorkspaceMetadata()->ownerUserId to get the userId of the owner.'); - // getRootNodeData(): NodeData - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'getRootNodeData', '!! Workspace::getRootNodeData() has been removed in Neos 9.0 without a replacement.'); - // getTitle(): string - $rectorConfig->rule(WorkspaceGetTitleRector::class); - // meta->setWorkspaceTitle - // isInternalWorkspace(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isInternalWorkspace', '!! Workspace::isInternalWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); - // isPersonalWorkspace(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isPersonalWorkspace', '!! Workspace::isPersonalWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); - // isPrivateWorkspace(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isPrivateWorkspace', '!! Workspace::isPrivateWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); - // isPublicWorkspace(): bool - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isPublicWorkspace', '!! Workspace::isPublicWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); - // publish(targetWorkspace: Workspace): void - $rectorConfig->rule(WorkspacePublishRector::class); - // publishNode(nodeToPublish: NodeInterface, targetWorkspace: Workspace): void - $rectorConfig->rule(WorkspacePublishNodeRector::class); - // publishNodes(nodes: NodeInterface[], targetWorkspace: Workspace): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'publishNodes', '!! Workspace::publishNodes() has been removed in Neos 9.0. Use the \Neos\Neos\Domain\Service\WorkspacePublishingService to publish a workspace or changes in a document.'); - // setBaseWorkspace(baseWorkspace: Workspace): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'setBaseWorkspace', '!! Workspace::setBaseWorkspace() is not supported by the new CR. Use the "ChangeBaseWorkspace" command to change the baseWorkspace of a workspace.'); - // setDescription(description: string): void - $rectorConfig->rule(WorkspaceSetDescriptionRector::class); - // setOwner(user: UserInterface|null|string): void - $methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'setOwner', '!! Workspace::setOwner() has been removed in Neos 9.0. You can set the owner of a workspace during creation WorkspaceService::createPersonalWorkspace().'); - // setTitle(title: string): void - $rectorConfig->rule(WorkspaceSetTitleRector::class); - - /** - * SPECIAL rules - */ - $rectorConfig->rule(FusionCachingNodeInEntryIdentifierRector::class); - $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' - ) - ]); - - $rectorConfig->rule(YamlDimensionConfigRector::class); - $rectorConfig->rule(YamlRoutePartHandlerRector::class); - - /** - * CLEAN UP / END GLOBAL RULES - */ - $rectorConfig->ruleWithConfiguration(MethodCallToPropertyFetchRector::class, $methodCallToPropertyFetches); - $rectorConfig->ruleWithConfiguration(MethodCallToWarningCommentRector::class, $methodCallToWarningComments); - $rectorConfig->ruleWithConfiguration(FusionNodePropertyPathToWarningCommentRector::class, $fusionNodePropertyPathToWarningComments); - - // Remove injections to classes which are gone now - $rectorConfig->ruleWithConfiguration(RemoveInjectionsRector::class, [ - new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContextFactoryInterface::class), - new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContextFactory::class), - new RemoveInjection(\Neos\Neos\Domain\Service\ContentContextFactory::class), - new RemoveInjection(\Neos\Rector\ContentRepository90\Legacy\LegacyContextStub::class), - new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContentDimensionCombinator::class), - new RemoveInjection(\Neos\ContentRepository\Domain\Factory\NodeFactory::class), - new RemoveInjection(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class), - new RemoveInjection(\Neos\ContentRepository\Core\NodeType\NodeTypeManager::class), - new RemoveInjection(\Neos\Neos\Domain\Service\NodeSearchServiceInterface::class), - new RemoveInjection(\Neos\Neos\Domain\Service\NodeSearchService::class), - new RemoveInjection(\Neos\ContentRepository\Domain\Repository\NodeDataRepository::class), - new RemoveInjection(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory::class), - ]); - - // Remove traits which are gone - $rectorConfig->ruleWithConfiguration(RemoveTraitUseRector::class, [ - \Neos\Neos\Controller\CreateContentContextTrait::class - ]); - - // todo these ToStringToMethodCallOrPropertyFetchRector rules are likely mostly obsolete and only to migrate from one Neos 9 beta to another but NOT for upgrading from 8.3 - // see https://github.com/neos/neos-development-collection/pull/4156 - $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', - \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\ContentRepository\ContentRepositoryId::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', - \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\Projection\ContentGraph\CoverageByOrigin::class => 'toJson()', - \Neos\ContentRepository\Core\Projection\ContentGraph\OriginByCoverage::class => 'toJson()', - \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds::class => 'toJson()', - ]); - - // We can only add one rule per class name. As workaround, we need to alias the RenameClassRector, so we are able to - // add this rule twice. - if (!class_exists(\Alias\RenameClassRectorLegacy::class)) { - class_alias(RenameClassRector::class, \Alias\RenameClassRectorLegacy::class); - } - $rectorConfig->ruleWithConfiguration(\Alias\RenameClassRectorLegacy::class, [ - NodeLegacyStub::class => Node::class, - ]); - - // Should run LAST - as other rules above might create $this->contentRepositoryRegistry calls. - $rectorConfig->ruleWithConfiguration(InjectServiceIfNeededRector::class, [ - new AddInjection('contentRepositoryRegistry', ContentRepositoryRegistry::class), - new AddInjection('renderingModeService', RenderingModeService::class), - new AddInjection('nodeLabelGenerator', NodeLabelGeneratorInterface::class), - new AddInjection('workspacePublishingService', WorkspacePublishingService::class), - new AddInjection('workspaceService', WorkspaceService::class), - ]); - // TODO: does not fully seem to work.$rectorConfig->rule(RemoveDuplicateCommentRector::class); -}; +); + +/** + * Neos\ContentRepository\Domain\Service\NodeTypeManager + */ +$rectorConfig->withRules([NodeTypeManagerAccessRector::class]); +// createNodeType(nodeTypeName: string): NodeType +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Service\NodeTypeManager::class, 'createNodeType', '!! NodeTypeManager::createNodeType() was never implemented and is removed in Neos 9.0.'); +// getNodeType(nodeTypeName: string): NodeType +// --> Compatible with 9.0 +// getNodeTypes([includeAbstractNodeTypes: bool = true]): NodeType[] +// --> Compatible with 9.0 +// getSubNodeTypes(superTypeName: string, [includeAbstractNodeTypes: bool = true]): NodeType[] +// --> Compatible with 9.0 +// hasNodeType(nodeTypeName: string): bool +// --> Compatible with 9.0 +// overrideNodeTypes(completeNodeTypeConfiguration: array): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Service\NodeTypeManager::class, 'overrideNodeTypes', '!! NodeTypeManager::createNodeType() was never meant to be used outside of testing and is removed in Neos 9.0.'); + +/** + * NodeData + */ +$nodeDataWarningMessage = '!! NodeData::%2$s is removed in Neos 9.0 - the new CR is not based around the concept of NodeData anymore. You need to rewrite your code here.'; +// createNodeData(name: string, [nodeType: NodeType|null = null], [identifier: null|string = null], [workspace: Workspace|null = null], [dimensions: array|null = null]): NodeData +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createNodeData', $nodeDataWarningMessage); +// createNodeDataFromTemplate(nodeTemplate: NodeTemplate, [nodeName: null|string = null], [workspace: Workspace|null = null], [dimensions: array|null = null]): NodeData +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createNodeDataFromTemplate', $nodeDataWarningMessage); +// createShadow(path: string): NodeData +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createShadow', $nodeDataWarningMessage); +// createSingleNodeData(name: string, [nodeType: NodeType|null = null], [identifier: null|string = null], [workspace: Workspace|null = null], [dimensions: array|null = null]): NodeData +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'createSingleNodeData', $nodeDataWarningMessage); +// getContextPath(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getContextPath', $nodeDataWarningMessage); +// getDepth(): int +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDepth', $nodeDataWarningMessage); +// getDimensions(): NodeDimension[] +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDimensions', $nodeDataWarningMessage); +// getDimensionsHash(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDimensionsHash', $nodeDataWarningMessage); +// getDimensionValues(): array +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getDimensionValues', $nodeDataWarningMessage); +// getIdentifier(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getIdentifier', $nodeDataWarningMessage); +// getIndex(): int +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getIndex', $nodeDataWarningMessage); +// getMovedTo(): NodeData +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getMovedTo', $nodeDataWarningMessage); +// getName(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getName', $nodeDataWarningMessage); +// getNumberOfChildNodes(nodeTypeFilter: string, workspace: Workspace, dimensions: array): int +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getNumberOfChildNodes', $nodeDataWarningMessage); +// getParent(): NodeData|null +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getParent', $nodeDataWarningMessage); +// getParentPath(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getParentPath', $nodeDataWarningMessage); +// getPath(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getPath', $nodeDataWarningMessage); +// getWorkspace(): Workspace +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'getWorkspace', $nodeDataWarningMessage); +// hasAccessRestrictions(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'hasAccessRestrictions', $nodeDataWarningMessage); +// isAccessible(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isAccessible', $nodeDataWarningMessage); +// isInternal(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isInternal', $nodeDataWarningMessage); +// isRemoved(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isRemoved', $nodeDataWarningMessage); +// isVisible(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'isVisible', $nodeDataWarningMessage); +// matchesWorkspaceAndDimensions(workspace: Workspace, [dimensions: array|null = null]): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'matchesWorkspaceAndDimensions', $nodeDataWarningMessage); +// move(targetPath: string, targetWorkspace: Workspace): NodeData|null +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'targetPath', $nodeDataWarningMessage); +// remove(): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'remove', $nodeDataWarningMessage); +// setDimensions(dimensionsToBeSet: array): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setDimensions', $nodeDataWarningMessage); +// setIdentifier(identifier: string): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setIdentifier', $nodeDataWarningMessage); +// setIndex(index: int): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setIndex', $nodeDataWarningMessage); +// setMovedTo([nodeData: NodeData|null = null]): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setMovedTo', $nodeDataWarningMessage); +// setPath(path: string, [recursive: bool = true]): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setPath', $nodeDataWarningMessage); +// setRemoved(removed: bool): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setRemoved', $nodeDataWarningMessage); +// setWorkspace([workspace: Workspace|null = null]): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'setWorkspace', $nodeDataWarningMessage); +// similarize(sourceNode: AbstractNodeData, [isCopy: bool = false]): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'similarize', $nodeDataWarningMessage); + +/** + * \Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory + */ +// parseFilterStrnig(serializedFilters: string): NodeTypeConstraints +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory::class, 'parseFilterString', '!! The "NodeTypeConstraintFactory" has been removed in Neos 9. Please use the proper filter in subgraph finders e.g. "FindChildNodesFilter" for ContentSubgraphInterface::findChildNodes().'); + +/** + * \Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints + */ +$nodeTypeConstraintsWarning = '!! NodeTypeConstraints has been removed in Neos 9. Please use the proper filter in subgraph finders e.g. "FindChildNodesFilter" for ContentSubgraphInterface::findChildNodes().'; +// asLegacyNodeTypeFilterString(): string +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'asLegacyNodeTypeFilterString', $nodeTypeConstraintsWarning); +// getExplicitlyAllowedNodeTypeNames(): NodeTypeName[] +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'getExplicitlyAllowedNodeTypeNames', $nodeTypeConstraintsWarning); +// getExplicitlyDisallowedNodeTypeNames(): NodeTypeName[] +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'getExplicitlyDisallowedNodeTypeNames', $nodeTypeConstraintsWarning); +// isWildcardAllowed(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'isWildcardAllowed', $nodeTypeConstraintsWarning); +// matches(nodeTypeName: NodeTypeName): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'nodeTypeName', $nodeTypeConstraintsWarning); +// withExplicitlyDisallowedNodeType(nodeTypeName: NodeTypeName): NodeTypeConstraints +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraints::class, 'withExplicitlyDisallowedNodeType', $nodeTypeConstraintsWarning); + +/** + * Signals and Slots + * https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots + */ +$signalsAndSlotsToComment = []; +// Neos\ContentRepository\Domain\Service\PublishingService +// - nodePublished +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\PublishingService::class, 'nodePublished', 'The signal "nodePublished" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodeDiscarded +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\PublishingService::class, 'nodeDiscarded', 'The signal "nodeDiscarded" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// Neos\Neos\Service\PublishingService +// - nodePublished +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\Neos\Service\PublishingService::class, 'nodePublished', 'The signal "nodePublished" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodeDiscarded +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\Neos\Service\PublishingService::class, 'nodeDiscarded', 'The signal "nodeDiscarded" on "PublishingService" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// Neos\ContentRepository\Domain\Service\Context +// - beforeAdoptNode +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\Context::class, 'beforeAdoptNode', 'The signal "beforeAdoptNode" on "Context" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - afterAdoptNode +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Service\Context::class, 'afterAdoptNode', 'The signal "afterAdoptNode" on "Context" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// Neos\ContentRepository\Domain\Repository\NodeDataRepository +// - repositoryObjectsPersisted +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Repository\NodeDataRepository::class, 'repositoryObjectsPersisted', 'The signal "repositoryObjectsPersisted" on "NodeDataRepository" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// Neos\ContentRepository\Domain\Model\Workspace +// - baseWorkspaceChanged +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'baseWorkspaceChanged', 'The signal "baseWorkspaceChanged" on "Workspace" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - beforeNodePublishing +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'beforeNodePublishing', 'The signal "beforeNodePublishing" on "Workspace" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - afterNodePublishing +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'afterNodePublishing', 'The signal "afterNodePublishing" on "Workspace" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// Neos\ContentRepository\Domain\Model\NodeData +// - nodePathChanged +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\NodeData::class, 'nodePathChanged', 'The signal "nodePathChanged" on "NodeData" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// Neos\ContentRepository\Domain\Model\Node +// - beforeNodeMove +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodeMove', 'The signal "beforeNodeMove" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - afterNodeMove +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'afterNodeMove', 'The signal "afterNodeMove" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - beforeNodeCopy +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodeCopy', 'The signal "beforeNodeCopy" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - afterNodeCopy +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'afterNodeCopy', 'The signal "afterNodeCopy" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodePathChanged +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodePathChanged', 'The signal "nodePathChanged" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - beforeNodeCreate +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodeCreate', 'The signal "beforeNodeCreate" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - afterNodeCreate +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'afterNodeCreate', 'The signal "afterNodeCreate" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodeAdded +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodeAdded', 'The signal "nodeAdded" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodeUpdated +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodeUpdated', 'The signal "nodeUpdated" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodeRemoved +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodeRemoved', 'The signal "nodeRemoved" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - beforeNodePropertyChange +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'beforeNodePropertyChange', 'The signal "beforeNodePropertyChange" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); +// - nodePropertyChanged +$signalsAndSlotsToComment[] = new SignalSlotToWarningComment(\Neos\ContentRepository\Domain\Model\Node::class, 'nodePropertyChanged', 'The signal "nodePropertyChanged" on "Node" has been removed. Please check https://docs.neos.io/api/upgrade-instructions/9/signals-and-slots for further information, how to replace a signal.'); + +$rectorConfig->withConfiguredRule(SignalSlotToWarningCommentRector::class, $signalsAndSlotsToComment); + + +$rectorConfig->withRules([ContentRepositoryUtilityRenderValidNodeNameRector::class]); + +/** + * \Neos\ContentRepository\Domain\Model\Workspace + */ +// getBaseWorkspace(): Workspace|null +$rectorConfig->withRules([WorkspaceGetBaseWorkspaceRector::class]); +// getBaseWorkspaces(): Workspace[] +$rectorConfig->withRules([WorkspaceGetBaseWorkspacesRector::class]); +// getDescription(): null|string +$rectorConfig->withRules([WorkspaceGetDescriptionRector::class]); +// getName(): string +// ->name +// getNodeCount(): int +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'getNodeCount', '!! Workspace::getNodeCount() has been removed in Neos 9.0 without a replacement.'); +// getOwner(): UserInterface|null +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'getOwner', '!! Workspace::getOwner() has been removed in Neos 9.0. Use WorkspaceService::getWorkspaceMetadata()->ownerUserId to get the userId of the owner.'); +// getRootNodeData(): NodeData +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'getRootNodeData', '!! Workspace::getRootNodeData() has been removed in Neos 9.0 without a replacement.'); +// getTitle(): string +$rectorConfig->withRules([WorkspaceGetTitleRector::class]); +// meta->setWorkspaceTitle +// isInternalWorkspace(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isInternalWorkspace', '!! Workspace::isInternalWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); +// isPersonalWorkspace(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isPersonalWorkspace', '!! Workspace::isPersonalWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); +// isPrivateWorkspace(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isPrivateWorkspace', '!! Workspace::isPrivateWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); +// isPublicWorkspace(): bool +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'isPublicWorkspace', '!! Workspace::isPublicWorkspace() has been removed in Neos 9.0. Please use the new Workspace permission api instead. See ContentRepositoryAuthorizationService::getWorkspacePermissions()'); +// publish(targetWorkspace: Workspace): void +$rectorConfig->withRules([WorkspacePublishRector::class]); +// publishNode(nodeToPublish: NodeInterface, targetWorkspace: Workspace): void +$rectorConfig->withRules([WorkspacePublishNodeRector::class]); +// publishNodes(nodes: NodeInterface[], targetWorkspace: Workspace): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'publishNodes', '!! Workspace::publishNodes() has been removed in Neos 9.0. Use the \Neos\Neos\Domain\Service\WorkspacePublishingService to publish a workspace or changes in a document.'); +// setBaseWorkspace(baseWorkspace: Workspace): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'setBaseWorkspace', '!! Workspace::setBaseWorkspace() is not supported by the new CR. Use the "ChangeBaseWorkspace" command to change the baseWorkspace of a workspace.'); +// setDescription(description: string): void +$rectorConfig->withRules([WorkspaceSetDescriptionRector::class]); +// setOwner(user: UserInterface|null|string): void +$methodCallToWarningComments[] = new MethodCallToWarningComment(\Neos\ContentRepository\Domain\Model\Workspace::class, 'setOwner', '!! Workspace::setOwner() has been removed in Neos 9.0. You can set the owner of a workspace during creation WorkspaceService::createPersonalWorkspace().'); +// setTitle(title: string): void +$rectorConfig->withRules([WorkspaceSetTitleRector::class]); + +$rectorConfig->withConfiguredRule(RenameClassRector::class, [ + \Neos\ContentRepository\Domain\Model\Node::class => Node::class, + \Neos\ContentRepository\Domain\Model\NodeInterface::class => Node::class, + \Neos\ContentRepository\Domain\Projection\Content\NodeInterface::class => Node::class, + \Neos\ContentRepository\Domain\Projection\Content\TraversableNodeInterface::class => Node::class, + + \Neos\ContentRepository\Domain\Projection\Content\TraversableNodes::class => \Neos\ContentRepository\Core\Projection\ContentGraph\Nodes::class, + + \Neos\ContentRepository\Domain\Service\Context::class => LegacyContextStub::class, + \Neos\Neos\Domain\Service\ContentContext::class => LegacyContextStub::class, + + \Neos\ContentRepository\Domain\Model\NodeType::class => \Neos\ContentRepository\Core\NodeType\NodeType::class, + \Neos\ContentRepository\Domain\Service\NodeTypeManager::class => \Neos\ContentRepository\Core\NodeType\NodeTypeManager::class, + + \Neos\ContentRepository\Domain\Model\Workspace::class => \Neos\ContentRepository\Core\SharedModel\Workspace\Workspace::class, + \Neos\ContentRepository\Domain\NodeAggregate\NodeAggregateIdentifier::class => \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId::class, + \Neos\ContentRepository\Domain\NodeAggregate\NodeName::class => \Neos\ContentRepository\Core\SharedModel\Node\NodeName::class, + \Neos\ContentRepository\Domain\NodeType\NodeTypeName::class => \Neos\ContentRepository\Core\NodeType\NodeTypeName::class, + \Neos\ContentRepository\Domain\Projection\Content\PropertyCollectionInterface::class => \Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollection::class, + \Neos\ContentRepository\Domain\Model\ArrayPropertyCollection::class => \Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollection::class, + \Neos\Neos\Routing\FrontendNodeRoutePartHandlerInterface::class => \Neos\Neos\FrontendRouting\FrontendNodeRoutePartHandlerInterface::class, +]); + +/** + * SPECIAL rules + */ +$rectorConfig->withConfiguredRule(\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 + */ +$rectorConfig->withConfiguredRule(MethodCallToPropertyFetchRector::class, $methodCallToPropertyFetches); +$rectorConfig->withConfiguredRule(MethodCallToWarningCommentRector::class, $methodCallToWarningComments); + +// Remove injections to classes which are gone now +$rectorConfig->withConfiguredRule(RemoveInjectionsRector::class, [ + new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContextFactoryInterface::class), + new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContextFactory::class), + new RemoveInjection(\Neos\Neos\Domain\Service\ContentContextFactory::class), + new RemoveInjection(\Neos\Rector\ContentRepository90\Legacy\LegacyContextStub::class), + new RemoveInjection(\Neos\ContentRepository\Domain\Service\ContentDimensionCombinator::class), + new RemoveInjection(\Neos\ContentRepository\Domain\Factory\NodeFactory::class), + new RemoveInjection(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class), + new RemoveInjection(\Neos\ContentRepository\Core\NodeType\NodeTypeManager::class), + new RemoveInjection(\Neos\Neos\Domain\Service\NodeSearchServiceInterface::class), + new RemoveInjection(\Neos\Neos\Domain\Service\NodeSearchService::class), + new RemoveInjection(\Neos\ContentRepository\Domain\Repository\NodeDataRepository::class), + new RemoveInjection(\Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory::class), +]); + +// Remove traits which are gone +$rectorConfig->withConfiguredRule(RemoveTraitUseRector::class, [ + \Neos\Neos\Controller\CreateContentContextTrait::class +]); + +// todo these ToStringToMethodCallOrPropertyFetchRector rules are likely mostly obsolete and only to migrate from one Neos 9 beta to another but NOT for upgrading from 8.3 +// see https://github.com/neos/neos-development-collection/pull/4156 +$rectorConfig->withConfiguredRule(ToStringToMethodCallOrPropertyFetchRector::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\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\ContentRepository\ContentRepositoryId::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', + \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\Projection\ContentGraph\CoverageByOrigin::class => 'toJson()', + \Neos\ContentRepository\Core\Projection\ContentGraph\OriginByCoverage::class => 'toJson()', + \Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds::class => 'toJson()', +]); + +// Should run LAST - as other rules above might create $this->contentRepositoryRegistry calls. +$rectorConfig->withConfiguredRule(InjectServiceIfNeededRector::class, [ + new AddInjection('contentRepositoryRegistry', ContentRepositoryRegistry::class), + new AddInjection('renderingModeService', RenderingModeService::class), + new AddInjection('nodeLabelGenerator', NodeLabelGeneratorInterface::class), + new AddInjection('workspacePublishingService', WorkspacePublishingService::class), + new AddInjection('workspaceService', WorkspaceService::class), +]); + + +return $rectorConfig; \ No newline at end of file diff --git a/config/set/neos-84.php b/config/set/neos-84.php deleted file mode 100644 index de8c01e..0000000 --- a/config/set/neos-84.php +++ /dev/null @@ -1,50 +0,0 @@ -services(); - $services->defaults() - ->public() - ->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 - - /** @var MethodCallToPropertyFetch[] $methodCallToPropertyFetches */ - $methodCallToPropertyFetches = []; - - /** @var MethodCallToWarningComment[] $methodCallToWarningComments */ - $methodCallToWarningComments = []; - - - $fusionFlowQueryPropertyToComments = []; - $fusionNodePropertyPathToWarningComments = []; - - - /** - * Put your rules below here - */ - - - - - - /** - * CLEAN UP / END GLOBAL RULES - */ - $rectorConfig->ruleWithConfiguration(FusionNodePropertyPathToWarningCommentRector::class, $fusionFlowQueryPropertyToComments); - $rectorConfig->ruleWithConfiguration(MethodCallToPropertyFetchRector::class, $methodCallToPropertyFetches); - $rectorConfig->ruleWithConfiguration(MethodCallToWarningCommentRector::class, $methodCallToWarningComments); - $rectorConfig->ruleWithConfiguration(FusionNodePropertyPathToWarningCommentRector::class, $fusionNodePropertyPathToWarningComments); - -}; diff --git a/docs/rules_overview.md b/docs/rules_overview.md index 84c3d15..2a6c007 100644 --- a/docs/rules_overview.md +++ b/docs/rules_overview.md @@ -1,4 +1,4 @@ -# 66 Rules Overview +# 53 Rules Overview ## ContentDimensionCombinatorGetAllAllowedCombinationsRector @@ -149,11 +149,12 @@ Replaces Utility::renderValidNodeName(...) into NodeName::fromString(...)->value public function run(\Neos\Rector\ContentRepository90\Legacy\LegacyContextStub $context) { - return $context->getRootNode(); ++ + // TODO 9.0 migration: !! MEGA DIRTY CODE! Ensure to rewrite this; by getting rid of LegacyContextStub. + $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\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()); ++ $workspace = $contentRepository->findWorkspaceByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($context->workspaceName ?? 'live')); ++ $rootNodeAggregate = $contentRepository->getContentGraph($workspace->workspaceName)->findRootNodeAggregateByType(\Neos\ContentRepository\Core\NodeType\NodeTypeName::fromString('Neos.Neos:Sites')); ++ $subgraph = $contentRepository->getContentGraph($workspace->workspaceName)->getSubgraph(\Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint::fromLegacyDimensionArray($context->dimensions ?? []), $context->invisibleContentShown ? \Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints::withoutRestrictions() : \Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints::default()); + return $subgraph->findNodeById($rootNodeAggregate->nodeAggregateId); } } @@ -221,1003 +222,54 @@ Replaces Utility::renderValidNodeName(...) into NodeName::fromString(...)->value
-## 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} - } - } - } -``` - -
- -## FusionContextCurrentRenderingModeRector - -Fusion: Rewrite node.context.currentRenderingMode... to renderingMode... - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionContextCurrentRenderingModeRector`](../src/ContentRepository90/Rules/FusionContextCurrentRenderingModeRector.php) - -```diff -+// TODO 9.0 migration: Line 9: You very likely need to rewrite "VARIABLE.context.currentRenderingMode..." to "renderingMode...". 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 { - -- nodeAttributes = ${node.context.currentRenderingMode.edit || node.context.currentRenderingMode.preview || node.context.currentRenderingMode.title || node.context.currentRenderingMode.name || node.context.currentRenderingMode.fusionPath || node.context.currentRenderingMode.options['foo']} -- siteAttributes = ${site.context.currentRenderingMode.edit || site.context.currentRenderingMode.preview || site.context.currentRenderingMode.title || site.context.currentRenderingMode.name || site.context.currentRenderingMode.fusionPath || site.context.currentRenderingMode.options['foo']} -- documentNodeAttributes = ${documentNode.context.currentRenderingMode.edit || documentNode.context.currentRenderingMode.preview || documentNode.context.currentRenderingMode.title || documentNode.context.currentRenderingMode.name || documentNode.context.currentRenderingMode.fusionPath || documentNode.context.currentRenderingMode.options['foo']} -+ nodeAttributes = ${renderingMode.isEdit || renderingMode.isPreview || renderingMode.title || renderingMode.name || renderingMode.fusionPath || renderingMode.options['foo']} -+ siteAttributes = ${renderingMode.isEdit || renderingMode.isPreview || renderingMode.title || renderingMode.name || renderingMode.fusionPath || renderingMode.options['foo']} -+ documentNodeAttributes = ${renderingMode.isEdit || renderingMode.isPreview || renderingMode.title || renderingMode.name || renderingMode.fusionPath || renderingMode.options['foo']} - other = ${other.context.currentRenderingMode.edit || other.context.currentRenderingMode.preview || other.context.currentRenderingMode.title || other.context.currentRenderingMode.name || other.context.currentRenderingMode.fusionPath || other.context.currentRenderingMode.options['foo']} - - renderer = afx` - - ` - } - } -``` - -
- -## FusionContextCurrentSiteRector - -Fusion: Rewrite node.context.currentSite to Neos.Site.findBySiteNode(site) - -- 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 "renderingMode.isEdit" - -- 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 "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 { - - # - # pass down props - # -- attributes = ${node.context.inBackend || site.context.inBackend || documentNode.context.inBackend} -+ attributes = ${renderingMode.isEdit || renderingMode.isEdit || renderingMode.isEdit} - - # - # 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 "!renderingMode.isEdit" - -- 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 "!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 { - - # - # pass down props - # -- attributes = ${node.context.live || site.context.live || documentNode.context.live} -+ attributes = ${!renderingMode.isEdit || !renderingMode.isEdit || !renderingMode.isEdit} - - # - # 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` - - ` - } - } -``` - -
- -## FusionFlowQueryNodePropertyToWarningCommentRector +## InjectServiceIfNeededRector -Fusion: Adds a warning comment when the defined property is used within an FlowQuery `"property()".` +add injection for `$contentRepositoryRegistry` if in use. :wrench: **configure it!** -- class: [`Neos\Rector\Generic\Rules\FusionFlowQueryNodePropertyToWarningCommentRector`](../src/Generic/Rules/FusionFlowQueryNodePropertyToWarningCommentRector.php) - -```php -extension('rectorConfig', [ - [ - 'class' => FusionFlowQueryNodePropertyToWarningCommentRector::class, - 'configuration' => [ - new FusionFlowQueryNodePropertyToWarningComment('_autoCreated', 'Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property("_autoCreated")" to "VARIABLE.classification.tethered". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.'), - ], - ], - ]); -}; -``` - -↓ - -```diff -+// TODO 9.0 migration: Line 11: !! You very likely need to rewrite "q(VARIABLE).property("_autoCreated")" to "VARIABLE.classification.tethered". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. -+// TODO 9.0 migration: Line 12: !! You very likely need to rewrite "q(VARIABLE).property("_autoCreated")" to "VARIABLE.classification.tethered". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. -+// TODO 9.0 migration: Line 13: !! You very likely need to rewrite "q(VARIABLE).property("_autoCreated")" to "VARIABLE.classification.tethered". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. -+// TODO 9.0 migration: Line 11: !! You very likely need to rewrite "q(VARIABLE).property("_contextPath")" to "Neos.Node.serializedNodeAddress(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. -+// TODO 9.0 migration: Line 12: !! You very likely need to rewrite "q(VARIABLE).property("_contextPath")" to "Neos.Node.serializedNodeAddress(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node. -+// TODO 9.0 migration: Line 13: !! You very likely need to rewrite "q(VARIABLE).property("_contextPath")" to "Neos.Node.serializedNodeAddress(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 { - - attributes = ${q(node).property('_autoCreated') || q(site).property("_contextPath")} - attributes2 = ${q(site).property('_autoCreated') || q(site).property("_contextPath")} - attributes3 = ${q(node).parent().property('_autoCreated') || q(node).parent().property("_contextPath")} - - } - } -``` - -
- -## FusionNodeAggregateIdentifierRector - -Fusion: Rewrite node.nodeAggregateIdentifier to node.aggregateId - -- 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.aggregateId. 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.aggregateId || documentNode.aggregateId} - renderer = afx` - - ` - } - } -``` - -
- -## FusionNodeAutoCreatedRector - -Fusion: Rewrite node.autoCreated to node.classification.tethered - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeAutoCreatedRector`](../src/ContentRepository90/Rules/FusionNodeAutoCreatedRector.php) +- class: [`Neos\Rector\Generic\Rules\InjectServiceIfNeededRector`](../src/Generic/Rules/InjectServiceIfNeededRector.php) ```diff -+// TODO 9.0 migration: Line 26: !! You very likely need to rewrite "VARIABLE.autoCreated" to "VARIABLE.classification.tethered". 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.autoCreated || documentNode.autoCreated} -+ attributes = ${node.classification.tethered || documentNode.classification.tethered} - - # - # 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` - - ` - } - } -``` - -
- -## FusionNodeContextPathRector - -Fusion: Rewrite node.contextPath to Neos.Node.serializedNodeAddress(node) + -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` - - ` + class SomeClass + { ++ #[\Neos\Flow\Annotations\Inject] ++ protected \Neos\ContentRepositoryRegistry\ContentRepositoryRegistry $contentRepositoryRegistry; ++ #[\Neos\Flow\Annotations\Inject] ++ protected \Neos\Neos\Domain\Service\RenderingModeService $renderingModeService; + public function run(Node $node) + { + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); + $currentRenderingMode = $this->renderingModeService->findByCurrentUser(); } } -``` - -
- -## FusionNodeDepthRector - -Fusion: Rewrite node.depth and q(node).property("_depth") to Neos.Node.depth(node) - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeDepthRector`](../src/ContentRepository90/Rules/FusionNodeDepthRector.php) - -```diff -+// TODO 9.0 migration: Line 13: You may need to rewrite "q(VARIABLE).property('_depth')" to Neos.Node.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 29: 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. -+// TODO 9.0 migration: Line 30: 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 || q(node).property('_depth') || q(documentNode).property("_depth")} -- foo = ${q(bar).property('_depth') || q(bar).property("_depth")} -+ attributes = ${Neos.Node.depth(node) || Neos.Node.depth(documentNode) || Neos.Node.depth(node) || Neos.Node.depth(documentNode)} -+ foo = ${Neos.Node.depth(bar) || Neos.Node.depth(bar)} - boo = ${q(nodes).first().property('_depth') || q(nodes).first().property("_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` - - ` - } - } -``` - -
- -## FusionNodeHiddenAfterDateTimeRector - -Fusion: Rewrite node.hiddenAfterDateTime to q(node).property("disableAfterDateTime") - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenAfterDateTimeRector`](../src/ContentRepository90/Rules/FusionNodeHiddenAfterDateTimeRector.php) - -```diff -+// TODO 9.0 migration: Line 16: You may need to rewrite "VARIABLE.hiddenAfterDateTime" to q(VARIABLE).property("disableAfterDateTime"). 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.hiddenAfterDateTime || documentNode.hiddenAfterDateTime} -- attributes2 = ${q(node).property("_hiddenAfterDateTime")} -+ attributes = ${q(node).property("disableAfterDateTime") || q(documentNode).property("disableAfterDateTime")} -+ attributes2 = ${q(node).property("disableAfterDateTime")} - - renderer = afx` - - ` - } - } -``` - -
- -## FusionNodeHiddenBeforeDateTimeRector - -Fusion: Rewrite node.hiddenBeforeDateTime to q(node).property("enableAfterDateTime") - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenBeforeDateTimeRector`](../src/ContentRepository90/Rules/FusionNodeHiddenBeforeDateTimeRector.php) - -```diff -+// TODO 9.0 migration: Line 16: You may need to rewrite "VARIABLE.hiddenBeforeDateTime" to q(VARIABLE).property("enableAfterDateTime"). 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.hiddenBeforeDateTime || documentNode.hiddenBeforeDateTime} -- attribute2 = ${q(node).property("_hiddenBeforeDateTime")} -+ attributes = ${q(node).property("enableAfterDateTime") || q(documentNode).property("enableAfterDateTime")} -+ attribute2 = ${q(node).property("enableAfterDateTime")} - - renderer = afx` - - ` - } - } -``` - -
- -## FusionNodeHiddenInIndexRector - -Fusion: Rewrite node.hiddenInIndex and q(node).property("_hiddenInIndex") to node.property('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.property('hiddenInMenu'). 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 || q(node).property('_hiddenInIndex') || q(documentNode).property("_hiddenInIndex")} -+ attributes = ${node.property('hiddenInMenu') || documentNode.property('hiddenInMenu') || site.property('hiddenInMenu') || q(node).property('hiddenInMenu') || q(documentNode).property('hiddenInMenu')} - - # - # 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` - - ` - } - } -``` - -
- -## FusionNodeHiddenRector - -Fusion: Rewrite node.hidden and q(node).property("_hidden") to Neos.Node.isDisabled(node) - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeHiddenRector`](../src/ContentRepository90/Rules/FusionNodeHiddenRector.php) - -```diff -+// TODO 9.0 migration: Line 5: You may need to rewrite "q(VARIABLE).property('_hidden')" to Neos.Node.isDisabled(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node. - prototype(Neos.Rector:Test) < prototype(Neos.Fusion:Value) { -- node = ${q(node).property('_hidden') || q(documentNode).property("_hidden") || q(site).property("_hidden")} -- otherVariable = ${q(someOtherVariable).property('_hidden')} -+ node = ${Neos.Node.isDisabled(node) || Neos.Node.isDisabled(documentNode) || Neos.Node.isDisabled(site)} -+ otherVariable = ${Neos.Node.isDisabled(someOtherVariable)} - flowQuery = ${q(someOtherVariable).first().property('_hidden')} -- inAfx = afx`` -+ inAfx = afx`` - } -``` - -
- -## FusionNodeIdentifierRector - -Fusion: Rewrite "node.identifier" and "q(node).property('_identifier')" to "node.aggregateId" - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeIdentifierRector`](../src/ContentRepository90/Rules/FusionNodeIdentifierRector.php) - -```diff - prototype(Neos.Fusion.Form:Checkbox) < prototype(Neos.Fusion.Form:Component.Field) { - - renderer = Neos.Fusion:Component { - - # - # pass down props - # -- attributes = ${q(node).property("_identifier") || q(documentNode).property("_identifier")} -+ attributes = ${node.aggregateId || documentNode.aggregateId} - renderer = afx` - - ` - } - } -``` - -
- -## FusionNodeLabelRector - -Fusion: Rewrite "node.label" and "q(node).property('_label')" to "Neos.Node.label(node)" - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeLabelRector`](../src/ContentRepository90/Rules/FusionNodeLabelRector.php) - -```diff - prototype(Neos.Rector:Test) < prototype(Neos.Fusion:Value) { -- node = ${q(node).property('_label') || q(documentNode).property("_label") || q(site).property("_label")} -- otherVariable = ${q(someOtherVariable).property('_label')} -- inAfx = afx`` -+ node = ${Neos.Node.label(node) || Neos.Node.label(documentNode) || Neos.Node.label(site)} -+ otherVariable = ${Neos.Node.label(someOtherVariable)} -+ inAfx = afx`` - } -``` - -
- -## FusionNodeNodeTypeRector - -Fusion: Rewrite "node.nodeType" and "q(node).property('_nodeType')" to "Neos.Node.nodeType(node)" - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeNodeTypeRector`](../src/ContentRepository90/Rules/FusionNodeNodeTypeRector.php) - -```diff - prototype(Neos.Rector:Test) < prototype(Neos.Fusion:Value) { -- node = ${q(node).property('_nodeType') || q(documentNode).property("_nodeType") || q(site).property("_nodeType")} -- otherVariable = ${q(someOtherVariable).property('_nodeType')} -- nested = ${q(someOtherVariable).property('_nodeType.properties')} -- deepNested = ${q(someOtherVariable).property('_nodeType.options.myOption')} -- inAfx = afx`` -+ node = ${Neos.Node.nodeType(node) || Neos.Node.nodeType(documentNode) || Neos.Node.nodeType(site)} -+ otherVariable = ${Neos.Node.nodeType(someOtherVariable)} -+ nested = ${Neos.Node.nodeType(someOtherVariable).properties} -+ deepNested = ${Neos.Node.nodeType(someOtherVariable).options.myOption} -+ inAfx = afx`` - } -``` - -
- -## FusionNodeParentRector - -Fusion: Rewrite node.parent to `q(node).parent().get(0)` - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeParentRector`](../src/ContentRepository90/Rules/FusionNodeParentRector.php) - -```diff -+// TODO 9.0 migration: Line 15: You may need to rewrite "VARIABLE.parent" to "q(VARIABLE).parent().get(0)". 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 = ${q(node).parent().get(0) || q(documentNode).parent().get(0)} - - renderer = afx` - - ` - } - } -``` - -
- -## FusionNodePathRector - -Fusion: Rewrite node.path and q(node).property("_path") to Neos.Node.path(node) - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodePathRector`](../src/ContentRepository90/Rules/FusionNodePathRector.php) - -```diff -+// TODO 9.0 migration: Line 29: 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. -+// TODO 9.0 migration: Line 30: 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 || q(node).property('_path') || q(documentNode).property("_path")} -- foo = ${q(bar).property('_path') || q(bar).property("_path")} -+ attributes = ${Neos.Node.path(node) || Neos.Node.path(documentNode) || Neos.Node.path(node) || Neos.Node.path(documentNode)} -+ foo = ${Neos.Node.path(bar) || Neos.Node.path(bar)} - boo = ${q(nodes).first().property('_path') || q(nodes).first().property("_path")} - - # - # 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 -extension('rectorConfig', [ - [ - 'class' => FusionNodePropertyPathToWarningCommentRector::class, - 'configuration' => [ - 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` - - - - ` - } - } -``` - -
- -## FusionNodeTypeNameRector - -Fusion: Rewrite node.nodeType.name to node.nodeTypeName - -- class: [`Neos\Rector\ContentRepository90\Rules\FusionNodeTypeNameRector`](../src/ContentRepository90/Rules/FusionNodeTypeNameRector.php) - -```diff -+// TODO 9.0 migration: Line 13: You may need to rewrite "VARIABLE.nodeType.name" to "VARIABLE.nodeTypeName". 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.nodeType.name || documentNode.nodeType.name} -+attributes = ${node.nodeTypeName || documentNode.nodeTypeName} - renderer = afx` - - ` - } - } -``` - -
- -## FusionPrototypeNameAddCommentRector - -Fusion: Add comment to file if prototype name matches at least once. - -:wrench: **configure it!** - -- class: [`Neos\Rector\Generic\Rules\FusionPrototypeNameAddCommentRector`](../src/Generic/Rules/FusionPrototypeNameAddCommentRector.php) - -```php -extension('rectorConfig', [ - [ - 'class' => FusionPrototypeNameAddCommentRector::class, - 'configuration' => [ - 'Neos.Neos:Raw', - 'Neos.Neos:Raw: Add this comment to top of file.', - ], - ], - ]); -}; -``` - -↓ - -```diff -+// TODO 9.0 migration: You need to refactor "Neos.Neos:PrimaryContent" to use "Neos.Neos:ContentCollection" instead. - prototype(My.Fancy:Component) < prototype(Neos.Fusion:Join) { - main = Neos.Neos:PrimaryContent { - nodePath = 'main' - } - - content = Neos.Neos:PrimaryContent - content.nodePath = 'content' - } - - prototype(My.Evil:Component) < prototype(Neos.Neos:PrimaryContent) { - - } -``` -
- -## FusionReplacePrototypeNameRector - -Fusion: Rewrite prototype names form e.g Foo.Bar:Boo to Boo.Bar:Foo - -:wrench: **configure it!** - -- class: [`Neos\Rector\Generic\Rules\FusionReplacePrototypeNameRector`](../src/Generic/Rules/FusionReplacePrototypeNameRector.php) - -```php -extension('rectorConfig', [ - [ - 'class' => FusionReplacePrototypeNameRector::class, - 'configuration' => [ - 'Neos.Neos:Raw', - 'Neos.Neos:NewRaw', - 'Neos.Neos:Raw: This comment should be added on top of the file.', - ], - ], - ]); -}; -``` - -↓ - -```diff -+// TODO 9.0 migration: Neos.Neos:FooReplaced: This comment should be added on top of the file. -+// TODO 9.0 migration: Neos.Neos:BarReplaced: This comment should be added on top of the file. - prototype(Neos.Neos:Foo) < prototype(Neos.Neos:Bar) { - -- raw = Neos.Neos:Foo -+ raw = Neos.Neos:FooReplaced - renderer = afx` -- -+ - ` - } + ?> ```
-## InjectServiceIfNeededRector +## MethodCallToPropertyFetchRector -add injection for `$contentRepositoryRegistry` if in use. +Turn method call `$this->getFirstname()` to property fetch `$this->firstname` :wrench: **configure it!** -- class: [`Neos\Rector\Generic\Rules\InjectServiceIfNeededRector`](../src/Generic/Rules/InjectServiceIfNeededRector.php) - -```php -extension('rectorConfig', [ - [ - 'class' => InjectServiceIfNeededRector::class, - 'configuration' => [ - new AddInjection('contentRepositoryRegistry', 'Neos\ContentRepositoryRegistry\ContentRepositoryRegistry'), - ], - ], - ]); -}; -``` - -↓ +- class: [`Neos\Rector\Generic\Rules\MethodCallToPropertyFetchRector`](../src/Generic/Rules/MethodCallToPropertyFetchRector.php) ```diff - contentRepositoryRegistry->subgraphForNode($node); - $currentRenderingMode = $this->renderingModeService->findByCurrentUser(); +- $this->getFirstname(); ++ $this->firstname; } } - - ?> ```
@@ -1230,40 +282,18 @@ return static function (RectorConfig $rectorConfig): void { - class: [`Neos\Rector\Generic\Rules\MethodCallToWarningCommentRector`](../src/Generic/Rules/MethodCallToWarningCommentRector.php) -```php -extension('rectorConfig', [ - [ - 'class' => MethodCallToWarningCommentRector::class, - 'configuration' => [ - new MethodCallToWarningComment('Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub', '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 getNode(); + // TODO 9.0 migration: !! 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. -+ return $node->getWorkspace(); } } @@ -1294,6 +324,8 @@ return static function (RectorConfig $rectorConfig): void { public function run() { - $this->nodeFactory->reset(); +- return $this->nodeFactory->reset(); ++ return; } } @@ -1311,11 +343,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff findParentNode(); + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); @@ -1337,16 +369,15 @@ return static function (RectorConfig $rectorConfig): void { ```diff getChildNodes(offset: 100, limit: 10) as $node) { + $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->aggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter::create(pagination: ['limit' => 10, 'offset' => 100]))) as $node) { } } @@ -1366,15 +397,14 @@ return static function (RectorConfig $rectorConfig): void { ```diff getContext()->getWorkspaceName(); -+ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); -+ return $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($node->subgraphIdentity->contentStreamId)->workspaceName; ++ return $node->workspaceName; } } @@ -1392,15 +422,29 @@ return static function (RectorConfig $rectorConfig): void { ```diff node; + } + + public function run(Node $node) { +- $workspace = $this->getNode()->getContext()->getWorkspace(); ++ $contentRepository = $this->contentRepositoryRegistry->get($this->getNode()->contentRepositoryId); ++ $workspace = $contentRepository->findWorkspaceByName($this->getNode()->workspaceName); ++ $contentRepository = $this->contentRepositoryRegistry->get($this->node->contentRepositoryId); + +- $workspace = $this->node->getContext()->getWorkspace(); ++ $workspace = $contentRepository->findWorkspaceByName($this->node->workspaceName); ++ $contentRepository = $this->contentRepositoryRegistry->get($node->contentRepositoryId); + - return $node->getContext()->getWorkspace(); -+ $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); -+ return $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($node->subgraphIdentity->contentStreamId); ++ return $contentRepository->findWorkspaceByName($node->workspaceName); } } @@ -1418,11 +462,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff getContextPath(); + return \Neos\ContentRepository\Core\SharedModel\Node\NodeAddress::fromNode($node)->toJson(); @@ -1443,11 +487,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff getDepth(); + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); @@ -1469,15 +513,22 @@ return static function (RectorConfig $rectorConfig): void { ```diff getDimensions()[0]; ++ // TODO 9.0 migration: Try to remove the toLegacyDimensionArray() call and make your codebase more typesafe. ++ $dimension = $node->originDimensionSpacePoint->toLegacyDimensionArray()[0]; + +- $dimensions = MyFoo::do($node->getDimensions()); ++ // TODO 9.0 migration: Try to remove the toLegacyDimensionArray() call and make your codebase more typesafe. ++ $dimensions = MyFoo::do($node->originDimensionSpacePoint->toLegacyDimensionArray()); + - return $node->getDimensions(); + // TODO 9.0 migration: Try to remove the toLegacyDimensionArray() call and make your codebase more typesafe. -+ + return $node->originDimensionSpacePoint->toLegacyDimensionArray(); } } @@ -1496,33 +547,29 @@ return static function (RectorConfig $rectorConfig): void { ```diff getHiddenBeforeDateTime(); + // TODO 9.0 migration: Timed publishing has been conceptually changed and has been extracted into a dedicated package. Please check https://github.com/neos/timeable-node-visibility for further details. - + $dateTime = $node->getProperty('enableAfterDateTime'); + + // TODO 9.0 migration: Timed publishing has been conceptually changed and has been extracted into a dedicated package. Please check https://github.com/neos/timeable-node-visibility for further details. + // Use the "SetNodeProperties" command to change property values for "enableAfterDateTime" or "disableAfterDateTime". -+ -+ $node->setHiddenBeforeDateTime($dateTime); } - public function nodeHiddenAfterDateTime(NodeLegacyStub $node) + public function nodeHiddenAfterDateTime(Node $node) { - $dateTime = $node->getHiddenAfterDateTime(); + // TODO 9.0 migration: Timed publishing has been conceptually changed and has been extracted into a dedicated package. Please check https://github.com/neos/timeable-node-visibility for further details. -+ + $dateTime = $node->getProperty('disableAfterDateTime'); + + // TODO 9.0 migration: Timed publishing has been conceptually changed and has been extracted into a dedicated package. Please check https://github.com/neos/timeable-node-visibility for further details. + // Use the "SetNodeProperties" command to change property values for "enableAfterDateTime" or "disableAfterDateTime". -+ - $node->setHiddenAfterDateTime($dateTime); } } @@ -1541,15 +588,14 @@ return static function (RectorConfig $rectorConfig): void { ```diff getIdentifier(); + // TODO 9.0 migration: Check if you could change your code to work with the NodeAggregateId value object instead. -+ + $nodeIdentifier = $node->aggregateId->value; } } @@ -1568,11 +614,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff getNodeType(); $nodeTypeName = $nodeType->getName(); @@ -1596,11 +642,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff getNodeType(); + $contentRepository = $this->contentRepositoryRegistry->get($node->contentRepositoryId); @@ -1622,11 +668,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff getParent(); + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); @@ -1648,18 +694,45 @@ return static function (RectorConfig $rectorConfig): void { ```diff getPath(); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); ++ // TODO 9.0 migration: Try to remove the (string) cast and make your code more type-safe. ++ $path = (string) $subgraph->findNodePath($node->aggregateId); + } + + public function getPathByGetNode() + { +- $path = $this->getNode()->getPath(); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($this->getNode()); ++ // TODO 9.0 migration: Try to remove the (string) cast and make your code more type-safe. ++ $path = (string) $subgraph->findNodePath($this->getNode()->aggregateId); + } + + public function getPath(Node $node) { - return $node->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->aggregateId); } + + public function getPathAsParameter(Node $node) + { +- $path = MyFoo::do($node->getPath()); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); ++ // TODO 9.0 migration: Try to remove the (string) cast and make your code more type-safe. ++ $path = MyFoo::do((string) $subgraph->findNodePath($node->aggregateId)); + } } ?> @@ -1676,11 +749,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff getPropertyNames(); + $propertyNames = array_keys(iterator_to_array($node->properties)); @@ -1701,11 +774,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff isAutoCreated(); + $bool = $node->classification->isTethered(); @@ -1726,11 +799,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff isHiddenInIndex(); + return $node->getProperty('hiddenInMenu'); @@ -1751,11 +824,11 @@ return static function (RectorConfig $rectorConfig): void { ```diff isHidden(); + return $node->tags->contain(\Neos\Neos\Domain\SubtreeTagging\NeosSubtreeTag::disabled()); @@ -1776,13 +849,13 @@ return static function (RectorConfig $rectorConfig): void { ```diff getLabel(); + $label = $this->nodeLabelGenerator->getLabel($node); @@ -1811,25 +884,27 @@ return static function (RectorConfig $rectorConfig): void { class SomeClass extends AnotherClass { /** - * @var \Neos\Neos\Domain\Service\NodeSearchService + * @var \Neos\Neos\Domain\Service\NodeSearchServiceInterface */ - private $nodeSearchService; + private $nodeSearchServiceInterface; - public function startingPointNodeIsGiven(Node $node, Context $context) + public function startingPointNodeIsGiven(Node $startingNode, Context $context) { $term = "term"; $searchNodeTypes = []; -- $nodes = $this->nodeSearchService->findByProperties($term, $searchNodeTypes, $context, $node); +- $nodes = $this->nodeSearchServiceInterface->findByProperties($term, $searchNodeTypes, $context, $startingNode); ++ + // TODO 9.0 migration: This could be a suitable replacement. Please check if all your requirements are still fulfilled. -+ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); -+ $nodes = $subgraph->findDescendantNodes($node->aggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter::create(nodeTypes: \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria::create(\Neos\ContentRepository\Core\NodeType\NodeTypeNames::fromStringArray($searchNodeTypes), \Neos\ContentRepository\Core\NodeType\NodeTypeNames::createEmpty()), searchTerm: $term)); ++ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($startingNode); ++ $nodes = $subgraph->findDescendantNodes($startingNode->aggregateId, \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter::create(nodeTypes: \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria::create(\Neos\ContentRepository\Core\NodeType\NodeTypeNames::fromStringArray($searchNodeTypes), \Neos\ContentRepository\Core\NodeType\NodeTypeNames::createEmpty()), searchTerm: $term)); } public function startingPointNodeIsNotGiven(Context $context) { $term = "term"; $searchNodeTypes = []; -- $nodes = $this->nodeSearchService->findByProperties($term, $searchNodeTypes, $context); +- $nodes = $this->nodeSearchServiceInterface->findByProperties($term, $searchNodeTypes, $context); ++ + // TODO 9.0 migration: The replacement needs a node as starting point for the search. Please provide a node, to make this replacement working. + $node = 'we-need-a-node-here'; + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); @@ -1849,22 +924,23 @@ return static function (RectorConfig $rectorConfig): void { ```diff getNodeType(); $grandParentsNodeType = $node->getParent()->getParent()->getNodeType(); -+ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. -+ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); - $grandParentsNodeType->allowsGrandchildNodeType($parentNodeName, $nodeType); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ + $contentRepository->getNodeTypeManager()->isNodeTypeAllowedAsChildToTetheredNode($grandParentsNodeType->name, \Neos\ContentRepository\Core\SharedModel\Node\NodeName::fromString($parentNodeName), $nodeType->name); } } @@ -1883,16 +959,15 @@ return static function (RectorConfig $rectorConfig): void { ```diff getNodeType(); - $childNodes = $nodeType->getAutoCreatedChildNodes(); + // TODO 9.0 migration: NodeType::tetheredNodeTypeDefinitions() is not a 1:1 replacement of NodeType::getAutoCreatedChildNodes(). You need to change your code to work with new TetheredNodeTypeDefinition object. -+ + $childNodes = $nodeType->tetheredNodeTypeDefinitions; } } @@ -1936,18 +1011,19 @@ return static function (RectorConfig $rectorConfig): void { ```diff getNodeType(); - $type = $nodeType->getTypeOfAutoCreatedChildNode($nodeName); ++ + // TODO 9.0 migration: Make this code aware of multiple Content Repositories. If you have a Node object around you can use $node->contentRepositoryId. + $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); + $type = $contentRepository->getNodeTypeManager()->getNodeType($nodeType->tetheredNodeTypeDefinitions->get($nodeName)); @@ -1971,7 +1047,7 @@ return static function (RectorConfig $rectorConfig): void { class SomeClass { /** - * @var \Neos\ContentRepository\Core\NodeType\NodeTypeManager + * @var \Neos\ContentRepository\Domain\Service\NodeTypeManager * @Flow\Inject */ protected $nodeTypeManager; @@ -1979,6 +1055,7 @@ return static function (RectorConfig $rectorConfig): void { { - $nt = $this->nodeTypeManager->getNodeTypes(false); + // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ + $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); + $nt = $contentRepository->getNodeTypeManager()->getNodeTypes(false); } @@ -1989,19 +1066,26 @@ return static function (RectorConfig $rectorConfig): void {
-## RemoveDuplicateCommentRector +## ObjectInstantiationToWarningCommentRector + +"Warning comments for various non-supported signals -"Warning comments for various non-supported use cases +:wrench: **configure it!** -- class: [`Neos\Rector\Generic\Rules\RemoveDuplicateCommentRector`](../src/Generic/Rules/RemoveDuplicateCommentRector.php) +- class: [`Neos\Rector\Generic\Rules\ObjectInstantiationToWarningCommentRector`](../src/Generic/Rules/ObjectInstantiationToWarningCommentRector.php) ```diff @@ -2017,29 +1101,6 @@ Remove properties marked with a @Flow\Inject annotation and a certain type - class: [`Neos\Rector\Generic\Rules\RemoveInjectionsRector`](../src/Generic/Rules/RemoveInjectionsRector.php) -```php -extension('rectorConfig', [ - [ - 'class' => RemoveInjectionsRector::class, - 'configuration' => [ - new RemoveInjection('Foo\Bar\Baz'), - ], - ], - ]); -}; -``` - -↓ - ```diff extension('rectorConfig', [ - [ - 'class' => RemoveParentClassRector::class, - 'configuration' => [ - new RemoveParentClass('Foo\Bar\Baz', '// TODO: Neos 9.0 Migration: Stuff'), - ], - ], - ]); -}; -``` - -↓ - ```diff extension('rectorConfig', [ - [ - 'class' => SignalSlotToWarningCommentRector::class, - 'configuration' => [ - new SignalSlotToWarningComment('PhpParser\Node', 'beforeMove', '!! This signal "beforeMove" on Node doesn\'t exist anymore'), - ], - ], - ]); -}; -``` - -↓ - ```diff getSignalSlotDispatcher(); -+ // TODO 9.0 migration: Signal "beforeMove" doesn't exist anymore -+ ++ // TODO 9.0 migration: Signal "beforeMove" doesn't exist anymore $dispatcher->connect( - NodeLegacyStub::class, + Node::class, 'beforeMove', SomeOtherClass::class, 'someMethod' ); -+ // TODO 9.0 migration: Signal "afterMove" doesn't exist anymore -+ ++ // TODO 9.0 migration: Signal "afterMove" doesn't exist anymore $dispatcher->connect( - 'Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub', + 'Neos\ContentRepository\Domain\Model\Node', 'afterMove', SomeOtherClass::class, 'someMethod' ); $dispatcher->connect( - NodeLegacyStub::class, + Node::class, 'otherMethod', SomeOtherClass::class, 'someMethod' @@ -2217,34 +1230,106 @@ Turns defined code uses of `"__toString()"` method to specific method calls or p - class: [`Neos\Rector\Generic\Rules\ToStringToMethodCallOrPropertyFetchRector`](../src/Generic/Rules/ToStringToMethodCallOrPropertyFetchRector.php) -```php -__toString(); ++$result = $someValue->getPath(); ++$result = $someValue->getPath(); +``` + +
+ +## WorkspaceGetBaseWorkspaceRector + +`"Workspace::getBaseWorkspace()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceGetBaseWorkspaceRector`](../src/ContentRepository90/Rules/WorkspaceGetBaseWorkspaceRector.php) + +```diff + getBaseWorkspace(); -use Neos\Rector\Generic\Rules\ToStringToMethodCallOrPropertyFetchRector; -use Rector\Config\RectorConfig; +- return $workspace->getBaseWorkspace(); ++ // TODO 9.0 migration: Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ $baseWorkspace = $contentRepository->findWorkspaceByName($workspace->baseWorkspaceName); ++ ++ // TODO 9.0 migration: Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ ++ return $contentRepository->findWorkspaceByName($workspace->baseWorkspaceName); + } + } -return static function (RectorConfig $rectorConfig): void { - $containerConfigurator->extension('rectorConfig', [ - [ - 'class' => ToStringToMethodCallOrPropertyFetchRector::class, - 'configuration' => [ - 'SomeObject' => 'getPath()', - ], - ], - ]); -}; + ?> ``` -↓ +
+ +## WorkspaceGetBaseWorkspacesRector + +`"Workspace::getBaseWorkspaces()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceGetBaseWorkspacesRector`](../src/ContentRepository90/Rules/WorkspaceGetBaseWorkspacesRector.php) ```diff - $someValue = new SomeObject; --$result = (string) $someValue; --$result = $someValue->__toString(); -+$result = $someValue->getPath(); -+$result = $someValue->getPath(); + getBaseWorkspaces(); + +- return $workspace->getBaseWorkspaces(); ++ // TODO 9.0 migration: Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ $baseWorkspaces = $contentRepository->findWorkspaces()->getBaseWorkspaces($workspace->workspaceName); ++ ++ // TODO 9.0 migration: Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ ++ return $contentRepository->findWorkspaces()->getBaseWorkspaces($workspace->workspaceName); + } + } + + ?> +``` + +
+ +## WorkspaceGetDescriptionRector + +`"Workspace::getDescription()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceGetDescriptionRector`](../src/ContentRepository90/Rules/WorkspaceGetDescriptionRector.php) + +```diff + getDescription(); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $description = $this->workspaceService->getWorkspaceMetadata(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName)->description->value; + } + } + + ?> ```
@@ -2258,7 +1343,7 @@ return static function (RectorConfig $rectorConfig): void { ```diff getName(); + // TODO 9.0 migration: Check if you could change your code to work with the WorkspaceName value object instead. -+ + $workspaceName = $workspace->workspaceName->value; } } @@ -2276,6 +1360,85 @@ return static function (RectorConfig $rectorConfig): void {
+## WorkspaceGetTitleRector + +`"Workspace::getTitle()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceGetTitleRector`](../src/ContentRepository90/Rules/WorkspaceGetTitleRector.php) + +```diff + getTitle(); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $title = $this->workspaceService->getWorkspaceMetadata(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName)->title->value; + } + } + + ?> +``` + +
+ +## WorkspacePublishNodeRector + +`"Workspace::publishNode()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspacePublishNodeRector`](../src/ContentRepository90/Rules/WorkspacePublishNodeRector.php) + +```diff + publishNode($node); ++ // TODO 9.0 migration: Check if this matches your requirements as this is not a 100% replacement. Make this code aware of multiple Content Repositories. ++ $this->workspacePublishingService->publishChangesInDocument(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName, $node); + } + } + + ?> +``` + +
+ +## WorkspacePublishRector + +`"Workspace::publish()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspacePublishRector`](../src/ContentRepository90/Rules/WorkspacePublishRector.php) + +```diff + publish(); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $this->workspacePublishingService->publishWorkspace(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName); + } + } + + ?> +``` + +
+ ## WorkspaceRepositoryCountByNameRector `"WorkspaceRepository::countByName()"` will be rewritten. @@ -2290,20 +1453,118 @@ return static function (RectorConfig $rectorConfig): void { class SomeClass { -- /** -- * @var WorkspaceRepository -- * @Flow\Inject -- */ -- protected $workspaceRepository; -+ #[\Neos\Flow\Annotations\Inject] -+ protected \Neos\ContentRepositoryRegistry\ContentRepositoryRegistry $contentRepositoryRegistry; + /** + * @var WorkspaceRepository + * @Flow\Inject + */ + protected $workspaceRepository; public function run(string $workspace) { - return $this->workspaceRepository->countByName($workspace); + $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); + // TODO 9.0 migration: remove ternary operator (...? 1 : 0 ) - unnecessary complexity ++ return $contentRepository->findWorkspaceByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($workspace)) !== null ? 1 : 0; + } + } + + ?> +``` + +
+ +## WorkspaceRepositoryFindByBaseWorkspaceRector + +`"WorkspaceRepository::findByBaseWorkspace()"` will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryFindByBaseWorkspaceRector`](../src/ContentRepository90/Rules/WorkspaceRepositoryFindByBaseWorkspaceRector.php) + +```diff + workspaceRepository->findByBaseWorkspace($workspaceIdentifier); ++ ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ $dependentWorkspaces = $contentRepository->findWorkspaces()->getDependantWorkspaces(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($workspaceIdentifier)); + } + } +``` + +
+ +## WorkspaceRepositoryFindByIdentifierRector + +`"WorkspaceRepository::findByIdentifier()"` will be rewritten. + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceRepositoryFindByIdentifierRector`](../src/ContentRepository90/Rules/WorkspaceRepositoryFindByIdentifierRector.php) + +```diff + workspaceRepository->findByIdentifier($workspaceIdentifier); + -+ return $contentRepository->getWorkspaceFinder()->findOneByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($workspace)) !== null ? 1 : 0; ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $contentRepository = $this->contentRepositoryRegistry->get(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default')); ++ $workspace = $contentRepository->findWorkspaceByName(\Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName::fromString($workspaceIdentifier)); + } + } +``` + +
+ +## WorkspaceSetDescriptionRector + +`"Workspace::setDescription()"` will be rewritten + +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceSetDescriptionRector`](../src/ContentRepository90/Rules/WorkspaceSetDescriptionRector.php) + +```diff + setDescription("description"); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $this->workspaceService->setWorkspaceDescription(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName, \Neos\Neos\Domain\Model\WorkspaceDescription::fromString("description")); + } + + public function get(Workspace $workspace) + { +- return $workspace->setDescription("description"); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ return $this->workspaceService->setWorkspaceDescription(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName, \Neos\Neos\Domain\Model\WorkspaceDescription::fromString("description")); } } @@ -2312,73 +1573,46 @@ return static function (RectorConfig $rectorConfig): void {
-## YamlDimensionConfigRector +## WorkspaceSetTitleRector -Fusion: Rewrite Settings.yaml config to new language +`"Workspace::setTitle()"` will be rewritten -- class: [`Neos\Rector\ContentRepository90\Rules\YamlDimensionConfigRector`](../src/ContentRepository90/Rules/YamlDimensionConfigRector.php) +- class: [`Neos\Rector\ContentRepository90\Rules\WorkspaceSetTitleRector`](../src/ContentRepository90/Rules/WorkspaceSetTitleRector.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 + setTitle("title"); +- $bar = $workspace->setTitle("title"); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $this->workspaceService->setWorkspaceTitle(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName, \Neos\Neos\Domain\Model\WorkspaceTitle::fromString("title")); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $bar = $this->workspaceService->setWorkspaceTitle(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName, \Neos\Neos\Domain\Model\WorkspaceTitle::fromString("title")); + +- $foo = $this->getWorkspace()->setTitle("title"); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ $foo = $this->workspaceService->setWorkspaceTitle(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $this->getWorkspace()->workspaceName, \Neos\Neos\Domain\Model\WorkspaceTitle::fromString("title")); + } + + public function get(Workspace $workspace) + { +- return $workspace->setTitle("title"); ++ // TODO 9.0 migration: Make this code aware of multiple Content Repositories. ++ return $this->workspaceService->setWorkspaceTitle(\Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId::fromString('default'), $workspace->workspaceName, \Neos\Neos\Domain\Model\WorkspaceTitle::fromString("title")); + } + + public function getWorkspace(): Workspace { + return Workspace::create('default'); + } + } + + ?> ```
diff --git a/rector.template.php b/rector.template.php index c150c44..8d9c11b 100644 --- a/rector.template.php +++ b/rector.template.php @@ -8,12 +8,6 @@ return static function (RectorConfig $rectorConfig): void { $rectorConfig->sets([ NeosRectorSets::CONTENTREPOSITORY_9_0, - //NeosRectorSets::NEOS_8_4, - ]); - - $rectorConfig->autoloadPaths([ - __DIR__ . '/Packages', - __DIR__ . '/DistributionPackages', ]); $rectorConfig->paths([ diff --git a/src/ContentRepository90/Legacy/CreateContentContextTrait.php b/src/ContentRepository90/Legacy/CreateContentContextTrait.php index 83ade57..f459ec8 100644 --- a/src/ContentRepository90/Legacy/CreateContentContextTrait.php +++ b/src/ContentRepository90/Legacy/CreateContentContextTrait.php @@ -1,9 +1,10 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Service\ContentDimensionCombinator::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getAllAllowedCombinations')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getAllAllowedCombinations' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Service\ContentDimensionCombinator::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->dimensionSpacePoints_toLegacyDimensionArray(); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - 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 - ); + $node->expr = $newExpr; - return $this->dimensionSpacePoints_toLegacyDimensionArray(); + return [ + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + self::assign('dimensionSpacePoints', $this->contentRepository_getVariationGraph_getDimensionSpacePoints()), + self::withTodoComment( + 'try to directly work with $dimensionSpacePoints, instead of converting them to the legacy dimension format', + $node + ), + ]; } } diff --git a/src/ContentRepository90/Rules/ContentRepositoryUtilityRenderValidNodeNameRector.php b/src/ContentRepository90/Rules/ContentRepositoryUtilityRenderValidNodeNameRector.php index 4cffb5c..ec2199d 100644 --- a/src/ContentRepository90/Rules/ContentRepositoryUtilityRenderValidNodeNameRector.php +++ b/src/ContentRepository90/Rules/ContentRepositoryUtilityRenderValidNodeNameRector.php @@ -8,12 +8,13 @@ use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; use PhpParser\Node\Name\FullyQualified; -use Rector\Core\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use PHPStan\Type\ObjectType; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class ContentRepositoryUtilityRenderValidNodeNameRector extends AbstractRector +final class ContentRepositoryUtilityRenderValidNodeNameRector extends AbstractRector implements DocumentedRuleInterface { public function getRuleDefinition(): RuleDefinition diff --git a/src/ContentRepository90/Rules/ContextFactoryToLegacyContextStubRector.php b/src/ContentRepository90/Rules/ContextFactoryToLegacyContextStubRector.php index 8a3b067..357a817 100644 --- a/src/ContentRepository90/Rules/ContextFactoryToLegacyContextStubRector.php +++ b/src/ContentRepository90/Rules/ContextFactoryToLegacyContextStubRector.php @@ -1,27 +1,26 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Expr\MethodCall::class]; } + /** * @param \PhpParser\Node\Expr\MethodCall $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?Node { assert($node instanceof Node\Expr\MethodCall); @@ -51,7 +51,7 @@ public function refactor(Node $node) : ?Node } return new Node\Expr\New_( - // TODO clean up + // TODO clean up new Node\Name('\\' . LegacyContextStub::class), $node->args ); diff --git a/src/ContentRepository90/Rules/ContextGetCurrentRenderingModeRector.php b/src/ContentRepository90/Rules/ContextGetCurrentRenderingModeRector.php index 797764a..2fd8782 100644 --- a/src/ContentRepository90/Rules/ContextGetCurrentRenderingModeRector.php +++ b/src/ContentRepository90/Rules/ContextGetCurrentRenderingModeRector.php @@ -7,19 +7,17 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Rector\PostRector\Collector\NodesToAddCollector; -use PhpParser\Node\Expr\Assign; -final class ContextGetCurrentRenderingModeRector extends AbstractRector +final class ContextGetCurrentRenderingModeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; use ContextRectorTrait; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } diff --git a/src/ContentRepository90/Rules/ContextGetFirstLevelNodeCacheRector.php b/src/ContentRepository90/Rules/ContextGetFirstLevelNodeCacheRector.php index 6849139..e2067db 100644 --- a/src/ContentRepository90/Rules/ContextGetFirstLevelNodeCacheRector.php +++ b/src/ContentRepository90/Rules/ContextGetFirstLevelNodeCacheRector.php @@ -4,23 +4,21 @@ namespace Neos\Rector\ContentRepository90\Rules; -use Neos\Rector\ContentRepository90\Legacy\LegacyContextStub; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PhpParser\NodeFinder; -use PhpParser\NodeTraverser; +use PhpParser\NodeVisitor; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class ContextGetFirstLevelNodeCacheRector extends AbstractRector +final class ContextGetFirstLevelNodeCacheRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } @@ -38,14 +36,14 @@ public function getNodeTypes(): array } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param \PhpParser\Node\Stmt\Expression $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node) { assert($node instanceof Node\Stmt\Expression); if ($this->containsContextGetFirstLevelNodeCache($node->expr)) { - $this->removeNode($node); + return NodeVisitor::REMOVE_NODE; } return $node; } @@ -55,7 +53,7 @@ private function containsContextGetFirstLevelNodeCache(Node\Expr $expr): bool $nodeFinder = new NodeFinder(); return $nodeFinder->findFirst( $expr, - fn(Node $node) => $node instanceof Node\Expr\MethodCall + fn (Node $node) => $node instanceof Node\Expr\MethodCall // WARNING: The System cannot infer the Context type properly, as the factory has no types. // Thus, we simply check on the method name getFirstLevelNodeCache() which is unique enough. //&& ( diff --git a/src/ContentRepository90/Rules/ContextGetRootNodeRector.php b/src/ContentRepository90/Rules/ContextGetRootNodeRector.php index f33422f..8765ca5 100644 --- a/src/ContentRepository90/Rules/ContextGetRootNodeRector.php +++ b/src/ContentRepository90/Rules/ContextGetRootNodeRector.php @@ -1,6 +1,7 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(LegacyContextStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getRootNode')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getRootNode' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(LegacyContextStub::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->subgraph_findNodeById( + $this->nodeFactory->createPropertyFetch('rootNodeAggregate', 'nodeAggregateId') + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - 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_findWorkspaceByName($this->workspaceName_fromString($this->context_workspaceName_fallbackToLive($node->var)))), - self::assign('rootNodeAggregate', $this->contentRepository_getContentGraph_findRootNodeAggregateByType($this->nodeFactory->createPropertyFetch('workspace', 'workspaceName') , $this->nodeTypeName_fromString('Neos.Neos:Sites'))), - self::assign('subgraph', $this->contentRepository_getContentGraph_getSubgraph($this->nodeFactory->createPropertyFetch('workspace', 'workspaceName'), $this->dimensionSpacePoint_fromLegacyDimensionArray($this->context_dimensions_fallbackToEmpty($node->var)), $this->visibilityConstraints($node->var))), - - ], - $node - ); + $node->expr = $newExpr; - return $this->subgraph_findNodeById( - $this->nodeFactory->createPropertyFetch('rootNodeAggregate', 'nodeAggregateId') - ); + return [ + new Nop(), // Needed, to render the comment below + 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_findWorkspaceByName( + $this->workspaceName_fromString( + $this->context_workspaceName_fallbackToLive($visitor->nodeVar) + ) + ) + ), + self::assign('rootNodeAggregate', + $this->contentRepository_getContentGraph_findRootNodeAggregateByType( + $this->nodeFactory->createPropertyFetch('workspace', 'workspaceName'), + $this->nodeTypeName_fromString('Neos.Neos:Sites') + ) + ), + self::assign('subgraph', + $this->contentRepository_getContentGraph_getSubgraph( + $this->nodeFactory->createPropertyFetch('workspace', 'workspaceName'), + $this->dimensionSpacePoint_fromLegacyDimensionArray( + $this->context_dimensions_fallbackToEmpty($visitor->nodeVar) + ), + $this->visibilityConstraints($visitor->nodeVar) + ) + ), + $node + ]; } - private function context_workspaceName_fallbackToLive(Node\Expr $legacyContextStub) { return new Node\Expr\BinaryOp\Coalesce( diff --git a/src/ContentRepository90/Rules/ContextIsInBackendRector.php b/src/ContentRepository90/Rules/ContextIsInBackendRector.php index d22bfab..5ea67df 100644 --- a/src/ContentRepository90/Rules/ContextIsInBackendRector.php +++ b/src/ContentRepository90/Rules/ContextIsInBackendRector.php @@ -7,20 +7,17 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Rector\PostRector\Collector\NodesToAddCollector; -use PhpParser\Node\Expr\Assign; -use PhpParser\NodeDumper; -final class ContextIsInBackendRector extends AbstractRector +final class ContextIsInBackendRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; use ContextRectorTrait; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } @@ -38,7 +35,7 @@ public function getNodeTypes(): array } /** - * @param \PhpParser\Node\Stmt\Expression $node + * @param \PhpParser\Node\Expr\MethodCall $node */ public function refactor(Node $node): ?Node { diff --git a/src/ContentRepository90/Rules/ContextIsLiveRector.php b/src/ContentRepository90/Rules/ContextIsLiveRector.php index c587160..df65009 100644 --- a/src/ContentRepository90/Rules/ContextIsLiveRector.php +++ b/src/ContentRepository90/Rules/ContextIsLiveRector.php @@ -7,20 +7,17 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Rector\PostRector\Collector\NodesToAddCollector; -use PhpParser\Node\Expr\Assign; -use PhpParser\NodeDumper; -final class ContextIsLiveRector extends AbstractRector +final class ContextIsLiveRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; use ContextRectorTrait; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } @@ -38,7 +35,7 @@ public function getNodeTypes(): array } /** - * @param \PhpParser\Node\Stmt\Expression $node + * @param \PhpParser\Node\Expr\MethodCall $node */ public function refactor(Node $node): ?Node { diff --git a/src/ContentRepository90/Rules/ContextRectorTrait.php b/src/ContentRepository90/Rules/ContextRectorTrait.php index d14b509..0ad1807 100644 --- a/src/ContentRepository90/Rules/ContextRectorTrait.php +++ b/src/ContentRepository90/Rules/ContextRectorTrait.php @@ -4,15 +4,8 @@ namespace Neos\Rector\ContentRepository90\Rules; -use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\NodeFinder; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Rector\PostRector\Collector\NodesToAddCollector; -use PhpParser\Node\Expr\Assign; -use PhpParser\NodeDumper; trait ContextRectorTrait { diff --git a/src/ContentRepository90/Rules/FusionCacheLifetimeRector.php b/src/ContentRepository90/Rules/FusionCacheLifetimeRector.php deleted file mode 100644 index 08a1adc..0000000 --- a/src/ContentRepository90/Rules/FusionCacheLifetimeRector.php +++ /dev/null @@ -1,28 +0,0 @@ -addCommentsIfRegexMatches( - '/\.cacheLifetime()/', - '// TODO 9.0 migration: Line %LINE: You may need to remove ".cacheLifetime()" as this FlowQuery Operation has been removed. This is not needed anymore as the concept of timeable node visibility has changed. See https://github.com/neos/timeable-node-visibility' - )->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php b/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php deleted file mode 100644 index 9ff1008..0000000 --- a/src/ContentRepository90/Rules/FusionCachingNodeInEntryIdentifierRector.php +++ /dev/null @@ -1,33 +0,0 @@ -process(function (string $eelExpression, FusionPath $path) { - if (!$path->containsSegments('__meta', 'cache', 'entryIdentifier')) { - return $eelExpression; - } - return preg_replace( - '/(?getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionContextCurrentRenderingModeRector.php b/src/ContentRepository90/Rules/FusionContextCurrentRenderingModeRector.php deleted file mode 100644 index 7c7f545..0000000 --- a/src/ContentRepository90/Rules/FusionContextCurrentRenderingModeRector.php +++ /dev/null @@ -1,41 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.context\.currentRenderingMode\.(name|title|fusionPath|options)/', - 'renderingMode.$2', - $eelExpression - )) - ->process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.context\.currentRenderingMode\.edit/', - 'renderingMode.isEdit', - $eelExpression - )) - ->process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.context\.currentRenderingMode\.preview/', - 'renderingMode.isPreview', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.context\.currentRenderingMode/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.currentRenderingMode..." to "renderingMode...". 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/FusionContextCurrentSiteRector.php b/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php deleted file mode 100644 index de21d64..0000000 --- a/src/ContentRepository90/Rules/FusionContextCurrentSiteRector.php +++ /dev/null @@ -1,27 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site|[a-zA-Z]+)\.context\.currentSite\b/', - 'Neos.Site.findBySiteNode(site)', - $eelExpression - ))->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionContextGetWorkspaceNameRector.php b/src/ContentRepository90/Rules/FusionContextGetWorkspaceNameRector.php deleted file mode 100644 index c4e9653..0000000 --- a/src/ContentRepository90/Rules/FusionContextGetWorkspaceNameRector.php +++ /dev/null @@ -1,31 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.context\.(workspaceName|workspace\.name)\b/', - '$1.workspaceName', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.context\.workspaceName/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.workspaceName" to "VARIABLE.workspaceName". 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/FusionContextGetWorkspaceRector.php b/src/ContentRepository90/Rules/FusionContextGetWorkspaceRector.php deleted file mode 100644 index f91333e..0000000 --- a/src/ContentRepository90/Rules/FusionContextGetWorkspaceRector.php +++ /dev/null @@ -1,26 +0,0 @@ -addCommentsIfRegexMatches( - '/\.context\.workspace(\.\w)?\b/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.context.workspace" as the "context" of nodes has been removed without a direct replacement in Neos 9. If you really need the workspace in fusion you need to create a dedicated helper yourself which should ideally do ALL the complex logic in php directly and return the computed result.' - )->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionContextInBackendRector.php b/src/ContentRepository90/Rules/FusionContextInBackendRector.php deleted file mode 100644 index 87f073c..0000000 --- a/src/ContentRepository90/Rules/FusionContextInBackendRector.php +++ /dev/null @@ -1,31 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.context\.inBackend/', - 'renderingMode.isEdit', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.context\.inBackend/', - '// 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 deleted file mode 100644 index 4f95fba..0000000 --- a/src/ContentRepository90/Rules/FusionContextLiveRector.php +++ /dev/null @@ -1,31 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.context\.live/', - '!renderingMode.isEdit', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.context\.live/', - '// 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/src/ContentRepository90/Rules/FusionFlowQueryContextRector.php b/src/ContentRepository90/Rules/FusionFlowQueryContextRector.php deleted file mode 100644 index 592602f..0000000 --- a/src/ContentRepository90/Rules/FusionFlowQueryContextRector.php +++ /dev/null @@ -1,26 +0,0 @@ -addCommentsIfRegexMatches( - '/context\(\s*\{(.*)[\'"](targetDimensions|currentDateTime|removedContentShown|inaccessibleContentShown)[\'"](.*)\}\s*\)/', - '// TODO 9.0 migration: Line %LINE: The "context()" FlowQuery operation has changed and does not support the following properties anymore: targetDimensions,currentDateTime,removedContentShown,inaccessibleContentShown.' - )->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php b/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php deleted file mode 100644 index 61cf25d..0000000 --- a/src/ContentRepository90/Rules/FusionNodeAggregateIdentifierRector.php +++ /dev/null @@ -1,33 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.nodeAggregateIdentifier/', - '$1.aggregateId', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.nodeAggregateIdentifier/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.nodeAggregateIdentifier" to VARIABLE.aggregateId. 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/FusionNodeAutoCreatedRector.php b/src/ContentRepository90/Rules/FusionNodeAutoCreatedRector.php deleted file mode 100644 index 8b00d80..0000000 --- a/src/ContentRepository90/Rules/FusionNodeAutoCreatedRector.php +++ /dev/null @@ -1,33 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.autoCreated/', - '$1.classification.tethered', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.autoCreated/', - '// TODO 9.0 migration: Line %LINE: !! You very likely need to rewrite "VARIABLE.autoCreated" to "VARIABLE.classification.tethered". 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/FusionNodeContextPathRector.php b/src/ContentRepository90/Rules/FusionNodeContextPathRector.php deleted file mode 100644 index 8fa66d8..0000000 --- a/src/ContentRepository90/Rules/FusionNodeContextPathRector.php +++ /dev/null @@ -1,43 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.contextPath\b(?!\.|\()/', - 'Neos.Node.serializedNodeAddress($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.contextPath\b(?!\.|\()/', - '// TODO 9.0 migration: Line %LINE: !! You very likely need to rewrite "VARIABLE.contextPath" to "Neos.Node.serializedNodeAddress(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\([\'"]_contextPath[\'"]\)/', - 'Neos.Node.serializedNodeAddress($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.property\([\'"]_contextPath[\'"]\)/', - '// TODO 9.0 migration: Line %LINE: !! You very likely need to rewrite "q(VARIABLE).property(\'_contextPath\')" to "Neos.Node.serializedNodeAddress(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/FusionNodeDepthRector.php b/src/ContentRepository90/Rules/FusionNodeDepthRector.php deleted file mode 100644 index 9a0f8d1..0000000 --- a/src/ContentRepository90/Rules/FusionNodeDepthRector.php +++ /dev/null @@ -1,42 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/([a-zA-Z.]+)?(site|node|documentNode)\.depth\b(?!\.|\()/', - 'Neos.Node.depth($1$2)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.depth\b(?!\.|\()/', - '// 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.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\([\'"]_depth[\'"]\)/', - 'Neos.Node.depth($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.property\([\'"]_depth[\'"]\)/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "q(VARIABLE).property(\'_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/FusionNodeHiddenAfterDateTimeRector.php b/src/ContentRepository90/Rules/FusionNodeHiddenAfterDateTimeRector.php deleted file mode 100644 index e93a58f..0000000 --- a/src/ContentRepository90/Rules/FusionNodeHiddenAfterDateTimeRector.php +++ /dev/null @@ -1,38 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode)\.hiddenAfterDateTime/', - 'q($1).property("disableAfterDateTime")', - $eelExpression - )) - ->process(fn(string $eelExpression) => preg_replace( - '/.property\(["\']_hiddenAfterDateTime["\']\)/', - '.property("disableAfterDateTime")', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.hiddenAfterDateTime/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.hiddenAfterDateTime" to q(VARIABLE).property("disableAfterDateTime"). 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/FusionNodeHiddenBeforeDateTimeRector.php b/src/ContentRepository90/Rules/FusionNodeHiddenBeforeDateTimeRector.php deleted file mode 100644 index 9cc4a28..0000000 --- a/src/ContentRepository90/Rules/FusionNodeHiddenBeforeDateTimeRector.php +++ /dev/null @@ -1,38 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode)\.hiddenBeforeDateTime/', - 'q($1).property("enableAfterDateTime")', - $eelExpression - )) - ->process(fn(string $eelExpression) => preg_replace( - '/.property\(["\']_hiddenBeforeDateTime["\']\)/', - '.property("enableAfterDateTime")', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.hiddenBeforeDateTime/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.hiddenBeforeDateTime" to q(VARIABLE).property("enableAfterDateTime"). 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/FusionNodeHiddenInIndexRector.php b/src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php deleted file mode 100644 index 4bcb633..0000000 --- a/src/ContentRepository90/Rules/FusionNodeHiddenInIndexRector.php +++ /dev/null @@ -1,39 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.hiddenInIndex\b(?!\.|\()/', - '$1.property(\'hiddenInMenu\')', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.hiddenInIndex\b(?!\.|\()/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.hiddenInIndex" to VARIABLE.property(\'hiddenInMenu\'). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/\.property\([\'"]_hiddenInIndex[\'"]\)/', - '.property(\'hiddenInMenu\')', - $eelExpression - ) - )->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionNodeHiddenRector.php b/src/ContentRepository90/Rules/FusionNodeHiddenRector.php deleted file mode 100644 index afa3d75..0000000 --- a/src/ContentRepository90/Rules/FusionNodeHiddenRector.php +++ /dev/null @@ -1,42 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.hidden\b(?!\.|\()/', - 'Neos.Node.isDisabled($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.hidden\b(?!\.|\()/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.hidden" to Neos.Node.isDisabled(VARIABLE). We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\([\'"]_hidden[\'"]\)/', - 'Neos.Node.isDisabled($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.property\([\'"]_hidden[\'"]\)/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "q(VARIABLE).property(\'_hidden\')" to Neos.Node.isDisabled(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/FusionNodeIdentifierRector.php b/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php deleted file mode 100644 index 5435ca7..0000000 --- a/src/ContentRepository90/Rules/FusionNodeIdentifierRector.php +++ /dev/null @@ -1,42 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.identifier/', - '$1.aggregateId', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.identifier/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.identifier" to "VARIABLE.aggregateId". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\([\'"]_identifier[\'"]\)/', - '$1.aggregateId', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.property\([\'"]_identifier[\'"]\)/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.identifier" to "VARIABLE.aggregateId". 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/FusionNodeLabelRector.php b/src/ContentRepository90/Rules/FusionNodeLabelRector.php deleted file mode 100644 index 405ca96..0000000 --- a/src/ContentRepository90/Rules/FusionNodeLabelRector.php +++ /dev/null @@ -1,38 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.label/', - 'Neos.Node.label($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.label\b(?!\()/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.label" to "Neos.Node.label(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\\([\'"]_label[\'"]\\)/', - 'Neos.Node.label($1)', - $eelExpression - )) - ->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionNodeNodeTypeRector.php b/src/ContentRepository90/Rules/FusionNodeNodeTypeRector.php deleted file mode 100644 index cdc012e..0000000 --- a/src/ContentRepository90/Rules/FusionNodeNodeTypeRector.php +++ /dev/null @@ -1,48 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.nodeType\.name/', - '$1.nodeTypeName', - $eelExpression - )) - ->process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.nodeType\b/', - 'Neos.Node.nodeType($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.nodeType\b(?!\()/', - '// TODO 9.0 migration: Line %LINE: You very likely need to rewrite "VARIABLE.nodeType" to "Neos.Node.nodeType(VARIABLE)". We did not auto-apply this migration because we cannot be sure whether the variable is a Node.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\\([\'"]_nodeType\.name[\'"]\\)/', - '$1.nodeTypeName', - $eelExpression - )) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\\([\'"]_nodeType(\.[^\'"]*)?[\'"]\\)/', - 'Neos.Node.nodeType($1)$2', - $eelExpression - )) - ->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionNodeParentRector.php b/src/ContentRepository90/Rules/FusionNodeParentRector.php deleted file mode 100644 index aa01b0f..0000000 --- a/src/ContentRepository90/Rules/FusionNodeParentRector.php +++ /dev/null @@ -1,33 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode)\.parent/', - 'q($1).parent().get(0)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.parent($|[^a-z(])/i', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.parent" to "q(VARIABLE).parent().get(0)". 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 deleted file mode 100644 index ddbc120..0000000 --- a/src/ContentRepository90/Rules/FusionNodePathRector.php +++ /dev/null @@ -1,39 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.path\b(?!\.|\()/', - 'Neos.Node.path($1)', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.path\b(?!\.|\()/', - '// 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.' - ) - ->process(fn(string $eelExpression) => preg_replace( - '/q\(([^)]+)\)\.property\([\'"]_path[\'"]\)/', - 'Neos.Node.path($1)', - $eelExpression - ) - )->getProcessedContent(); - } -} diff --git a/src/ContentRepository90/Rules/FusionNodeTypeNameRector.php b/src/ContentRepository90/Rules/FusionNodeTypeNameRector.php deleted file mode 100644 index fe30df3..0000000 --- a/src/ContentRepository90/Rules/FusionNodeTypeNameRector.php +++ /dev/null @@ -1,33 +0,0 @@ -process(fn(string $eelExpression) => preg_replace( - '/(node|documentNode|site)\.nodeType\.name/', - '$1.nodeTypeName', - $eelExpression - )) - ->addCommentsIfRegexMatches( - '/\.nodeType.name/', - '// TODO 9.0 migration: Line %LINE: You may need to rewrite "VARIABLE.nodeType.name" to "VARIABLE.nodeTypeName". 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/NodeFactoryResetRector.php b/src/ContentRepository90/Rules/NodeFactoryResetRector.php index 7078e9f..7df5e95 100644 --- a/src/ContentRepository90/Rules/NodeFactoryResetRector.php +++ b/src/ContentRepository90/Rules/NodeFactoryResetRector.php @@ -1,24 +1,29 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node) { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Factory\NodeFactory::class))) { - return null; - } - if (!$this->isName($node->name, 'reset')) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - $this->removeNode($node); + $methodCall = $this->betterNodeFinder->findFirst($node, function (Node $subNode) { + return $subNode instanceof MethodCall + && $this->isObjectType($subNode->var, new ObjectType(\Neos\ContentRepository\Domain\Factory\NodeFactory::class)) + && $this->isName($subNode->name, 'reset'); + }); + + if ($methodCall) { + if ($node instanceof Node\Stmt\Return_) { + return new Node\Stmt\Return_(); + } + return NodeVisitor::REMOVE_NODE; + } return $node; } diff --git a/src/ContentRepository90/Rules/NodeFindParentNodeRector.php b/src/ContentRepository90/Rules/NodeFindParentNodeRector.php index 18c9deb..f0991fe 100644 --- a/src/ContentRepository90/Rules/NodeFindParentNodeRector.php +++ b/src/ContentRepository90/Rules/NodeFindParentNodeRector.php @@ -6,18 +6,22 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeFindParentNodeRector extends AbstractRector +final class NodeFindParentNodeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) + public function __construct() { } @@ -31,30 +35,61 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'findParentNode')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'findParentNode' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->subgraph_findParentNode($node->var); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($node->var)), - ], - $node - ); + $node->expr = $newExpr; - return $this->subgraph_findParentNode($node->var); + return [ + self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar)), + $node + ]; } } diff --git a/src/ContentRepository90/Rules/NodeGetChildNodesRector.php b/src/ContentRepository90/Rules/NodeGetChildNodesRector.php index eafa683..86aa4f7 100644 --- a/src/ContentRepository90/Rules/NodeGetChildNodesRector.php +++ b/src/ContentRepository90/Rules/NodeGetChildNodesRector.php @@ -1,26 +1,31 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getChildNodes')) { - return null; - } - $nodeTypeFilterExpr = null; - $limitExpr = null; - $offsetExpr = null; - foreach ($node->args as $index => $arg) { - $argumentName = $arg?->name?->name; - $namedArgument = $argumentName !== null; + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getChildNodes' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + $nodeTypeFilterExpr = null; + $limitExpr = null; + $offsetExpr = null; - if (($namedArgument && $argumentName === 'nodeTypeFilter') || !$namedArgument && $index === 0) { - assert($arg instanceof Node\Arg); + foreach ($node->args as $index => $arg) { + $argumentName = $arg?->name?->name; + $namedArgument = $argumentName !== null; - if ($arg->value instanceof Node\Scalar\String_) { - $nodeTypeFilterExpr = $arg->value; + if (($namedArgument && $argumentName === 'nodeTypeFilter') || (!$namedArgument && $index === 0)) { + assert($arg instanceof Node\Arg); + + if ($arg->value instanceof Node\Scalar\String_) { + $nodeTypeFilterExpr = $arg->value; + } + } + if (($namedArgument && $argumentName === 'limit') || (!$namedArgument && $index === 1)) { + assert($arg instanceof Node\Arg); + $limitExpr = $arg->value; + } + if (($namedArgument && $argumentName === 'offset') || (!$namedArgument && $index === 2)) { + assert($arg instanceof Node\Arg); + $offsetExpr = $arg->value; + } + } + return $this->iteratorToArray( + $this->subgraph_findChildNodes($node->var, $nodeTypeFilterExpr, $limitExpr, $offsetExpr) + ); + } + } + return null; } - } - if (($namedArgument && $argumentName === 'limit') || !$namedArgument && $index === 1) { - assert($arg instanceof Node\Arg); - $limitExpr = $arg->value; - } - if (($namedArgument && $argumentName === 'offset') || !$namedArgument && $index === 2) { - assert($arg instanceof Node\Arg); - $offsetExpr = $arg->value; - } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { + return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($node->var)), - self::todoComment('Try to remove the iterator_to_array($nodes) call.') - ], - $node - ); - - return $this->iteratorToArray( - $this->subgraph_findChildNodes($node->var, $nodeTypeFilterExpr, $limitExpr, $offsetExpr) - ); + $node->expr = $newExpr; + + return [ + self::assign( + 'subgraph', + $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar) + ), + self::withTodoComment( + 'Try to remove the iterator_to_array($nodes) call.', + $node + ) + ]; } } diff --git a/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceNameRector.php b/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceNameRector.php index 1649f8a..cf46541 100644 --- a/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceNameRector.php +++ b/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceNameRector.php @@ -7,17 +7,16 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetContextGetWorkspaceNameRector extends AbstractRector +final class NodeGetContextGetWorkspaceNameRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -50,7 +49,7 @@ public function refactor(Node $node): ?Node return null; } - if (!$this->isObjectType($node->var->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!$this->isObjectType($node->var->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { return null; } diff --git a/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceRector.php b/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceRector.php index 5b84945..e188174 100644 --- a/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceRector.php +++ b/src/ContentRepository90/Rules/NodeGetContextGetWorkspaceRector.php @@ -4,20 +4,25 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Domain\Model\Node as NodeLegacyStub; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetContextGetWorkspaceRector extends AbstractRector +final class NodeGetContextGetWorkspaceRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) + public function __construct() { } @@ -31,45 +36,71 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - // Node->getContext()->getWorkspace() - if (!$this->isName($node->name, 'getWorkspace')) { - return null; - } - if (!$node->var instanceof Node\Expr\MethodCall) { - return null; - } - if (!$this->isName($node->var->name, 'getContext')) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isObjectType($node->var->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { - return null; - } + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } - $nodeVar = $node->var->var; - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign( - 'contentRepository', - $this->this_contentRepositoryRegistry_get( - $this->nodeFactory->createPropertyFetch($nodeVar, 'contentRepositoryId') - ) + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getWorkspace' && + $node->var instanceof MethodCall && + $node->var->name instanceof Identifier && + $node->var->name->toString() === 'getContext' + ) { + $this->nodeVar = $node->var->var; + if ($this->nodeTypeResolver->isObjectType($this->nodeVar, new ObjectType(NodeLegacyStub::class))) { + $this->changed = true; + + return $this->contentRepository_findWorkspaceByName( + $this->nodeFactory->createPropertyFetch($this->nodeVar, 'workspaceName') + ); + } + } + + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + + $contentRepository = self::assign( + 'contentRepository', + $this->this_contentRepositoryRegistry_get( + $this->nodeFactory->createPropertyFetch($visitor->nodeVar, 'contentRepositoryId') ) - ], - $node - ); + ); + + return [ + $contentRepository, $node + ]; + } - return $this->contentRepository_findWorkspaceByName( - $this->nodeFactory->createPropertyFetch($nodeVar, 'workspaceName') - ); + return null; } } diff --git a/src/ContentRepository90/Rules/NodeGetContextPathRector.php b/src/ContentRepository90/Rules/NodeGetContextPathRector.php index 459bb48..8d2bbcc 100644 --- a/src/ContentRepository90/Rules/NodeGetContextPathRector.php +++ b/src/ContentRepository90/Rules/NodeGetContextPathRector.php @@ -8,17 +8,16 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetContextPathRector extends AbstractRector +final class NodeGetContextPathRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -41,7 +40,7 @@ public function refactor(Node $node): ?Node { assert($node instanceof Node\Expr\MethodCall); - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { return null; } if (!$this->isName($node->name, 'getContextPath')) { diff --git a/src/ContentRepository90/Rules/NodeGetDepthRector.php b/src/ContentRepository90/Rules/NodeGetDepthRector.php index e232dc9..020ecaa 100644 --- a/src/ContentRepository90/Rules/NodeGetDepthRector.php +++ b/src/ContentRepository90/Rules/NodeGetDepthRector.php @@ -1,26 +1,31 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getDepth')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getDepth' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->subgraph_findNodePath_getDepth($node->var); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($node->var)), - ], - $node - ); + $node->expr = $newExpr; - return $this->subgraph_findNodePath_getDepth($node->var); + return [ + self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar)), + $node + ]; } } diff --git a/src/ContentRepository90/Rules/NodeGetDimensionsRector.php b/src/ContentRepository90/Rules/NodeGetDimensionsRector.php index 25e6ffe..b3285b8 100644 --- a/src/ContentRepository90/Rules/NodeGetDimensionsRector.php +++ b/src/ContentRepository90/Rules/NodeGetDimensionsRector.php @@ -4,19 +4,27 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Domain\Model\Node as NodeLegacyStub; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetDimensionsRector extends AbstractRector +final class NodeGetDimensionsRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector + private BetterNodeFinder $betterNodeFinder, ) { } @@ -30,30 +38,54 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { - return null; - } - if (!$this->isName($node->name, 'getDimensions')) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Try to remove the toLegacyDimensionArray() call and make your codebase more typesafe.') - ], - $node - ); + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getDimensions' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(NodeLegacyStub::class))) { + $this->changed = true; + + return $this->node_originDimensionSpacePoint_toLegacyDimensionArray($node->var); + } + } + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + self::withTodoComment('Try to remove the toLegacyDimensionArray() call and make your codebase more typesafe.', $node); + return $node; + } - return $this->node_originDimensionSpacePoint_toLegacyDimensionArray($node->var); + return null; } } diff --git a/src/ContentRepository90/Rules/NodeGetHiddenBeforeAfterDateTimeRector.php b/src/ContentRepository90/Rules/NodeGetHiddenBeforeAfterDateTimeRector.php index 88afd33..5eec0ed 100644 --- a/src/ContentRepository90/Rules/NodeGetHiddenBeforeAfterDateTimeRector.php +++ b/src/ContentRepository90/Rules/NodeGetHiddenBeforeAfterDateTimeRector.php @@ -6,18 +6,23 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetHiddenBeforeAfterDateTimeRector extends AbstractRector +final class NodeGetHiddenBeforeAfterDateTimeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -30,47 +35,77 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if ( - !$this->isName($node->name, 'getHiddenBeforeDateTime') - && !$this->isName($node->name, 'setHiddenBeforeDateTime') - && !$this->isName($node->name, 'getHiddenAfterDateTime') - && !$this->isName($node->name, 'setHiddenAfterDateTime') - ) { + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public bool $isGetter = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + $methodNames = ['getHiddenBeforeDateTime', 'setHiddenBeforeDateTime', 'getHiddenAfterDateTime', 'setHiddenAfterDateTime']; + + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + in_array($node->name->toString(), $methodNames) + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + if ($node->name->toString() === 'getHiddenBeforeDateTime') { + $this->isGetter = true; + return $this->nodeFactory->createMethodCall($node->var, 'getProperty', ['enableAfterDateTime']); + } + if ($node->name->toString() === 'getHiddenAfterDateTime') { + $this->isGetter = true; + return $this->nodeFactory->createMethodCall($node->var, 'getProperty', ['disableAfterDateTime']); + } + return $node; + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $comment = 'Timed publishing has been conceptually changed and has been extracted into a dedicated package. Please check https://github.com/neos/timeable-node-visibility for further details.'; + $node->expr = $newExpr; - if ($this->isName($node->name, 'getHiddenBeforeDateTime')) { - $newNode = $this->nodeFactory->createMethodCall($node->var, 'getProperty', ['enableAfterDateTime']); - } elseif ($this->isName($node->name, 'getHiddenAfterDateTime')) { - $newNode = $this->nodeFactory->createMethodCall($node->var, 'getProperty', ['disableAfterDateTime']); - } else { - $newNode = $node; + $comment = 'Timed publishing has been conceptually changed and has been extracted into a dedicated package. Please check https://github.com/neos/timeable-node-visibility for further details.'; + if ($visitor->isGetter === false) { $comment .= PHP_EOL . '// Use the "SetNodeProperties" command to change property values for "enableAfterDateTime" or "disableAfterDateTime".'; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment($comment) - ], + return self::withTodoComment( + $comment, $node ); - - return $newNode; } } diff --git a/src/ContentRepository90/Rules/NodeGetIdentifierRector.php b/src/ContentRepository90/Rules/NodeGetIdentifierRector.php index fab0e18..870f5ae 100644 --- a/src/ContentRepository90/Rules/NodeGetIdentifierRector.php +++ b/src/ContentRepository90/Rules/NodeGetIdentifierRector.php @@ -6,18 +6,23 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetIdentifierRector extends AbstractRector +final class NodeGetIdentifierRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -30,31 +35,63 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getIdentifier')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getIdentifier' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + $propertyFetchAggregateId = $this->nodeFactory->createPropertyFetch($node->var, 'aggregateId'); + return $this->nodeFactory->createPropertyFetch($propertyFetchAggregateId, 'value'); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Check if you could change your code to work with the NodeAggregateId value object instead.') - ], - $node - ); + $node->expr = $newExpr; - $propertyFetchAggregateId = $this->nodeFactory->createPropertyFetch($node->var, 'aggregateId'); - return $this->nodeFactory->createPropertyFetch($propertyFetchAggregateId, 'value'); + return + self::withTodoComment( + 'Check if you could change your code to work with the NodeAggregateId value object instead.', + $node + ); } } diff --git a/src/ContentRepository90/Rules/NodeGetNodeTypeGetNameRector.php b/src/ContentRepository90/Rules/NodeGetNodeTypeGetNameRector.php index d341321..d3bab31 100644 --- a/src/ContentRepository90/Rules/NodeGetNodeTypeGetNameRector.php +++ b/src/ContentRepository90/Rules/NodeGetNodeTypeGetNameRector.php @@ -6,18 +6,23 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetNodeTypeGetNameRector extends AbstractRector +final class NodeGetNodeTypeGetNameRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -30,43 +35,66 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Core\NodeType\NodeType::class))) { - return null; - } - if (!$this->isName($node->name, 'getName')) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$node->var instanceof Node\Expr\MethodCall) { - return null; - } + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; - $nodeTypeVar = $node->var; + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + ) { + } - if (!$this->isObjectType($nodeTypeVar->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { - return null; - } + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getName' + ) { + // ensure `$something->getNodeType()->getName()` where something is a legacy Node + if ($node->var instanceof MethodCall && $node->var->name instanceof Identifier && $node->var->name->toString() === 'getNodeType') { + // optional: check that $node->var is NodeType and inner var is Node + $isNodeType = $this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Core\NodeType\NodeType::class)); + $isLegacyNode = $this->nodeTypeResolver->isObjectType($node->var->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class)); + if ($isNodeType && $isLegacyNode) { + $this->changed = true; + return $this->nodeFactory->createPropertyFetch( + $this->nodeFactory->createPropertyFetch( + $node->var->var, + 'nodeTypeName' + ), 'value' + ); + } + } + } + return null; + } + } + ); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; - if (!$this->isName($nodeTypeVar->name, 'getNodeType')) { + if (!$visitor->changed) { return null; } - return - $this->nodeFactory->createPropertyFetch( - $this->nodeFactory->createPropertyFetch( - $node->var->var, - 'nodeTypeName' - ), 'value' - ); + $node->expr = $newExpr; + return $node; } } diff --git a/src/ContentRepository90/Rules/NodeGetNodeTypeRector.php b/src/ContentRepository90/Rules/NodeGetNodeTypeRector.php index b99afa8..8cca299 100644 --- a/src/ContentRepository90/Rules/NodeGetNodeTypeRector.php +++ b/src/ContentRepository90/Rules/NodeGetNodeTypeRector.php @@ -6,18 +6,23 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeGetNodeTypeRector extends AbstractRector +final class NodeGetNodeTypeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -30,42 +35,71 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getNodeType')) { + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getNodeType' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->nodeFactory->createMethodCall( + $this->contentRepository_getNodeTypeManager() + , 'getNodeType', + [ + $this->nodeFactory->createPropertyFetch($node->var, 'nodeTypeName') + ]); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign( - 'contentRepository', - $this->this_contentRepositoryRegistry_get( - $this->nodeFactory->createPropertyFetch($node->var, 'contentRepositoryId') - ) - ), - ], - $node - ); - - return - $this->nodeFactory->createMethodCall( - $this->contentRepository_getNodeTypeManager() - , 'getNodeType', - [ - $this->nodeFactory->createPropertyFetch($node->var, 'nodeTypeName') - ]); + $node->expr = $newExpr; + + return [ + self::assign( + 'contentRepository', + $this->this_contentRepositoryRegistry_get( + $this->nodeFactory->createPropertyFetch($visitor->nodeVar, 'contentRepositoryId') + ) + ), + $node, + ]; } } diff --git a/src/ContentRepository90/Rules/NodeGetParentRector.php b/src/ContentRepository90/Rules/NodeGetParentRector.php index a05627e..cc0b489 100644 --- a/src/ContentRepository90/Rules/NodeGetParentRector.php +++ b/src/ContentRepository90/Rules/NodeGetParentRector.php @@ -1,26 +1,32 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node) { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getParent')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getParent' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(NodeLegacyStub::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->subgraph_findParentNode($node->var); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($node->var)), - ], - $node - ); + $node->expr = $newExpr; - return $this->subgraph_findParentNode($node->var); + return [ + self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar)), + $node + ]; } } diff --git a/src/ContentRepository90/Rules/NodeGetPathRector.php b/src/ContentRepository90/Rules/NodeGetPathRector.php index f5d69f8..0dc4263 100644 --- a/src/ContentRepository90/Rules/NodeGetPathRector.php +++ b/src/ContentRepository90/Rules/NodeGetPathRector.php @@ -1,26 +1,35 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node) { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getPath')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getPath' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(NodeLegacyStub::class))) { + $this->changed = true; + $this->nodeVar = $node->var; + + return $this->castToString( + $this->subgraph_findNodePath($node->var) + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($node->var)), - self::todoComment('Try to remove the (string) cast and make your code more type-safe.') - ], - $node - ); - - return $this->castToString( - $this->subgraph_findNodePath($node->var) - ); + $node->expr = $newExpr; + + return [ + self::assign( + 'subgraph', + $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar) + ), + self::withTodoComment( + 'Try to remove the (string) cast and make your code more type-safe.', + $node + ) + ]; } } diff --git a/src/ContentRepository90/Rules/NodeGetPropertyNamesRector.php b/src/ContentRepository90/Rules/NodeGetPropertyNamesRector.php index c7771a3..eb18ad5 100644 --- a/src/ContentRepository90/Rules/NodeGetPropertyNamesRector.php +++ b/src/ContentRepository90/Rules/NodeGetPropertyNamesRector.php @@ -4,24 +4,22 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypeName; -use Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub; +use Neos\ContentRepository\Domain\Model\Node as NodeLegacyStub; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\NodeType\NodeType; -use PhpParser\NodeDumper; -final class NodeGetPropertyNamesRector extends AbstractRector +final class NodeGetPropertyNamesRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -55,7 +53,7 @@ public function refactor(Node $node): ?Node return $this->nodeFactory->createFuncCall('array_keys', [ $this->nodeFactory->createFuncCall('iterator_to_array', [ - $this->nodeFactory->createPropertyFetch($node->var, 'properties'), + $this->nodeFactory->createPropertyFetch($node->var, 'properties'), ]), ]); } diff --git a/src/ContentRepository90/Rules/NodeIsAutoCreatedRector.php b/src/ContentRepository90/Rules/NodeIsAutoCreatedRector.php index cc5f482..6002771 100644 --- a/src/ContentRepository90/Rules/NodeIsAutoCreatedRector.php +++ b/src/ContentRepository90/Rules/NodeIsAutoCreatedRector.php @@ -7,17 +7,16 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeIsAutoCreatedRector extends AbstractRector +final class NodeIsAutoCreatedRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -40,7 +39,7 @@ public function refactor(Node $node): ?Node { assert($node instanceof Node\Expr\MethodCall); - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { return null; } if (!$this->isName($node->name, 'isAutoCreated')) { diff --git a/src/ContentRepository90/Rules/NodeIsHiddenInIndexRector.php b/src/ContentRepository90/Rules/NodeIsHiddenInIndexRector.php index e446af5..26862ea 100644 --- a/src/ContentRepository90/Rules/NodeIsHiddenInIndexRector.php +++ b/src/ContentRepository90/Rules/NodeIsHiddenInIndexRector.php @@ -8,21 +8,19 @@ use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeIsHiddenInIndexRector extends AbstractRector +final class NodeIsHiddenInIndexRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) + public function __construct() { } - public function getRuleDefinition() : RuleDefinition + public function getRuleDefinition(): RuleDefinition { return CodeSampleLoader::fromFile('"NodeInterface::isHiddenInIndex()" will be rewritten', __CLASS__); } @@ -30,18 +28,19 @@ public function getRuleDefinition() : RuleDefinition /** * @return array> */ - public function getNodeTypes() : array + public function getNodeTypes(): array { return [MethodCall::class]; } + /** * @param MethodCall $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?Node { assert($node instanceof MethodCall); - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { return null; } if (!$this->isName($node->name, 'isHiddenInIndex')) { diff --git a/src/ContentRepository90/Rules/NodeIsHiddenRector.php b/src/ContentRepository90/Rules/NodeIsHiddenRector.php index bb7adf8..a404882 100644 --- a/src/ContentRepository90/Rules/NodeIsHiddenRector.php +++ b/src/ContentRepository90/Rules/NodeIsHiddenRector.php @@ -1,26 +1,25 @@ > */ - public function getNodeTypes() : array + public function getNodeTypes(): array { return [\PhpParser\Node\Expr\MethodCall::class]; } + /** * @param \PhpParser\Node\Expr\MethodCall $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?Node { assert($node instanceof Node\Expr\MethodCall); - if (!$this->isObjectType($node->var, new ObjectType(\Neos\Rector\ContentRepository90\Legacy\NodeLegacyStub::class))) { + if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Model\Node::class))) { return null; } if (!$this->isName($node->name, 'isHidden')) { diff --git a/src/ContentRepository90/Rules/NodeLabelGeneratorRector.php b/src/ContentRepository90/Rules/NodeLabelGeneratorRector.php index d5e627c..095692f 100644 --- a/src/ContentRepository90/Rules/NodeLabelGeneratorRector.php +++ b/src/ContentRepository90/Rules/NodeLabelGeneratorRector.php @@ -1,27 +1,26 @@ getLabel()" will be rewritten.', __CLASS__); } @@ -29,14 +28,15 @@ public function getRuleDefinition() : RuleDefinition /** * @return array> */ - public function getNodeTypes() : array + public function getNodeTypes(): array { return [Node\Expr\MethodCall::class]; } + /** * @param Node\Expr\MethodCall $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?Node { assert($node instanceof Node\Expr\MethodCall); diff --git a/src/ContentRepository90/Rules/NodeSearchServiceRector.php b/src/ContentRepository90/Rules/NodeSearchServiceRector.php index ae00cbe..f31b659 100644 --- a/src/ContentRepository90/Rules/NodeSearchServiceRector.php +++ b/src/ContentRepository90/Rules/NodeSearchServiceRector.php @@ -6,18 +6,24 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeSearchServiceRector extends AbstractRector +final class NodeSearchServiceRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -30,95 +36,120 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if ( - !$this->isObjectType($node->var, new ObjectType(\Neos\Neos\Domain\Service\NodeSearchService::class)) - && !$this->isObjectType($node->var, new ObjectType(\Neos\Neos\Domain\Service\NodeSearchServiceInterface::class)) - ) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'findByProperties')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public bool $hasStartingPoint = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'findByProperties' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\Neos\Domain\Service\NodeSearchService::class)) + || $this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\Neos\Domain\Service\NodeSearchServiceInterface::class)) + ) { + $this->changed = true; + + if (isset($node->args[3])) { + $this->hasStartingPoint = true; + $this->nodeVar = $node->args[3]->value; + } else { + $this->nodeVar = new Node\Expr\Variable('node'); + } + + return $this->nodeFactory->createMethodCall( + 'subgraph', + 'findDescendantNodes', + [ + $this->nodeFactory->createPropertyFetch( + $this->nodeVar, + 'aggregateId' + ), + $this->nodeFactory->createStaticCall( + \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter::class, + 'create', + [ + 'nodeTypes' => $this->nodeFactory->createStaticCall( + \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria::class, + 'create', + [ + $this->nodeFactory->createStaticCall( + \Neos\ContentRepository\Core\NodeType\NodeTypeNames::class, + 'fromStringArray', + [ + $node->args[1]->value, + ] + ), + $this->nodeFactory->createStaticCall( + \Neos\ContentRepository\Core\NodeType\NodeTypeNames::class, + 'createEmpty', + ), + ] + ), + 'searchTerm' => $node->args[0]->value, + ] + ) + ] + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - if (!isset($node->args[3])) { - $nodeExpr = self::assign('node', new \PhpParser\Node\Scalar\String_('we-need-a-node-here')); - $nodeNode = $nodeExpr->expr->var; - - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment('The replacement needs a node as starting point for the search. Please provide a node, to make this replacement working.', $nodeExpr), - $subgraphNode = self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($nodeNode)), - ], - $node - ); - - } else { - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment('This could be a suitable replacement. Please check if all your requirements are still fulfilled.', - $subgraphNode = self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($node->args[3]->value)) - ) - ], - $node - - ); - $nodeNode = $node->args[3]->value; + $node->expr = $newExpr; + if (!$visitor->hasStartingPoint) { + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'The replacement needs a node as starting point for the search. Please provide a node, to make this replacement working.', + self::assign($visitor->nodeVar->name, new \PhpParser\Node\Scalar\String_('we-need-a-node-here')), + ), + self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar)), + $node, + ]; } - return $this->nodeFactory->createMethodCall( - $subgraphNode->expr->var, - 'findDescendantNodes', - [ - $this->nodeFactory->createPropertyFetch( - $nodeNode, - 'aggregateId' - ), - $this->nodeFactory->createStaticCall( - \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter::class, - 'create', - [ - 'nodeTypes' => $this->nodeFactory->createStaticCall( - \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria::class, - 'create', - [ - $this->nodeFactory->createStaticCall( - \Neos\ContentRepository\Core\NodeType\NodeTypeNames::class, - 'fromStringArray', - [ - $node->args[1]->value, - ] - ), - $this->nodeFactory->createStaticCall( - \Neos\ContentRepository\Core\NodeType\NodeTypeNames::class, - 'createEmpty', - ), - ] - ), - 'searchTerm' => $node->args[0]->value, - ] - ) - ] - ); + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'This could be a suitable replacement. Please check if all your requirements are still fulfilled.', + self::assign('subgraph', $this->this_contentRepositoryRegistry_subgraphForNode($visitor->nodeVar)), + ), + $node, + ]; } } -/** - * \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter::create( - * nodeTypes: \Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria::create( - * \Neos\ContentRepository\Core\NodeType\NodeTypeNames::fromStringArray($searchNodeTypes), - * \Neos\ContentRepository\Core\NodeType\NodeTypeNames::createEmpty() - * ), - * searchTerm: $term - * ) - */ diff --git a/src/ContentRepository90/Rules/NodeTypeAllowsGrandchildNodeTypeRector.php b/src/ContentRepository90/Rules/NodeTypeAllowsGrandchildNodeTypeRector.php index c16cfdb..5947b63 100644 --- a/src/ContentRepository90/Rules/NodeTypeAllowsGrandchildNodeTypeRector.php +++ b/src/ContentRepository90/Rules/NodeTypeAllowsGrandchildNodeTypeRector.php @@ -7,18 +7,24 @@ use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeTypeAllowsGrandchildNodeTypeRector extends AbstractRector +final class NodeTypeAllowsGrandchildNodeTypeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -31,54 +37,84 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(NodeType::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'allowsGrandchildNodeType')) { + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'allowsGrandchildNodeType' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(NodeType::class))) { + $this->changed = true; + + return $this->nodeFactory->createMethodCall( + $this->contentRepository_getNodeTypeManager(), + 'isNodeTypeAllowedAsChildToTetheredNode', + [ + $this->nodeFactory->createPropertyFetch( + $node->var, + 'name' + ), + $this->nodeFactory->createStaticCall( + \Neos\ContentRepository\Core\SharedModel\Node\NodeName::class, + 'fromString', + [ + $node->args[0] + ] + ), + $this->nodeFactory->createPropertyFetch( + $node->args[1]->value, + 'name' + ) + ] + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment( - 'Make this code aware of multiple Content Repositories.', - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - ) - ], + $node->expr = $newExpr; + + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'Make this code aware of multiple Content Repositories.', + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + ), $node - ); - - return $this->nodeFactory->createMethodCall( - $this->contentRepository_getNodeTypeManager(), - 'isNodeTypeAllowedAsChildToTetheredNode', - [ - $this->nodeFactory->createPropertyFetch( - $node->var, - 'name' - ), - $this->nodeFactory->createStaticCall( - \Neos\ContentRepository\Core\SharedModel\Node\NodeName::class, - 'fromString', - [ - $node->args[0] - ] - ), - $this->nodeFactory->createPropertyFetch( - $node->args[1]->value, - 'name' - ) - ] - ); + ]; } } diff --git a/src/ContentRepository90/Rules/NodeTypeGetAutoCreatedChildNodesRector.php b/src/ContentRepository90/Rules/NodeTypeGetAutoCreatedChildNodesRector.php index 84077d6..cefc546 100644 --- a/src/ContentRepository90/Rules/NodeTypeGetAutoCreatedChildNodesRector.php +++ b/src/ContentRepository90/Rules/NodeTypeGetAutoCreatedChildNodesRector.php @@ -1,27 +1,32 @@ getAutoCreatedChildNodes()" will be rewritten.', __CLASS__); } @@ -29,37 +34,66 @@ public function getRuleDefinition() : RuleDefinition /** * @return array> */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } + /** - * @param Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(NodeType::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getAutoCreatedChildNodes')) { + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getAutoCreatedChildNodes' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(NodeType::class))) { + $this->changed = true; + + return $this->nodeFactory->createPropertyFetch( + $node->var, + 'tetheredNodeTypeDefinitions' + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment( - 'NodeType::tetheredNodeTypeDefinitions() is not a 1:1 replacement of NodeType::getAutoCreatedChildNodes(). You need to change your code to work with new TetheredNodeTypeDefinition object.', - ) - ], - $node - ); - - return $this->nodeFactory->createPropertyFetch( - $node->var, - 'tetheredNodeTypeDefinitions' - ); + $node->expr = $newExpr; + + return + self::withTodoComment( + 'NodeType::tetheredNodeTypeDefinitions() is not a 1:1 replacement of NodeType::getAutoCreatedChildNodes(). You need to change your code to work with new TetheredNodeTypeDefinition object.', + $node + ); } } diff --git a/src/ContentRepository90/Rules/NodeTypeGetNameRector.php b/src/ContentRepository90/Rules/NodeTypeGetNameRector.php index 0deac54..c0e1728 100644 --- a/src/ContentRepository90/Rules/NodeTypeGetNameRector.php +++ b/src/ContentRepository90/Rules/NodeTypeGetNameRector.php @@ -7,17 +7,16 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeTypeGetNameRector extends AbstractRector +final class NodeTypeGetNameRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition diff --git a/src/ContentRepository90/Rules/NodeTypeGetTypeOfAutoCreatedChildNodeRector.php b/src/ContentRepository90/Rules/NodeTypeGetTypeOfAutoCreatedChildNodeRector.php index ce29ba4..0b2d083 100644 --- a/src/ContentRepository90/Rules/NodeTypeGetTypeOfAutoCreatedChildNodeRector.php +++ b/src/ContentRepository90/Rules/NodeTypeGetTypeOfAutoCreatedChildNodeRector.php @@ -7,18 +7,24 @@ use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class NodeTypeGetTypeOfAutoCreatedChildNodeRector extends AbstractRector +final class NodeTypeGetTypeOfAutoCreatedChildNodeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } public function getRuleDefinition(): RuleDefinition @@ -31,46 +37,75 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(NodeType::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getTypeOfAutoCreatedChildNode')) { + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getTypeOfAutoCreatedChildNode' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(NodeType::class))) { + $this->changed = true; + + return $this->nodeFactory->createMethodCall( + $this->contentRepository_getNodeTypeManager(), + 'getNodeType', + [$this->nodeFactory->createMethodCall( + $this->nodeFactory->createPropertyFetch( + $node->var, + 'tetheredNodeTypeDefinitions' + ), + 'get', + $node->args + )] + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment( - 'Make this code aware of multiple Content Repositories. If you have a Node object around you can use $node->contentRepositoryId.', - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - ) - ], - $node - ); - - return - $this->nodeFactory->createMethodCall( - $this->contentRepository_getNodeTypeManager(), - 'getNodeType', - [$this->nodeFactory->createMethodCall( - $this->nodeFactory->createPropertyFetch( - $node->var, - 'tetheredNodeTypeDefinitions' - ), - 'get', - $node->args - )] - ); + $node->expr = $newExpr; + + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'Make this code aware of multiple Content Repositories. If you have a Node object around you can use $node->contentRepositoryId.', + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + ), + $node, + ]; } } diff --git a/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php b/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php index b403b7b..d8d5f05 100644 --- a/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php +++ b/src/ContentRepository90/Rules/NodeTypeManagerAccessRector.php @@ -1,26 +1,30 @@ nodeTypeManager" will be rewritten.', __CLASS__); } @@ -28,31 +32,69 @@ public function getRuleDefinition() : RuleDefinition /** * @return array> */ - public function getNodeTypes() : array + public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\PropertyFetch::class]; + return [Node\Stmt::class]; } + /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node) : ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\PropertyFetch); - - if (!$this->isObjectType($node, new ObjectType(\Neos\ContentRepository\Domain\Service\NodeTypeManager::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof PropertyFetch + ) { + $this->nodeVar = $node; + if ($this->nodeTypeResolver->isObjectType($this->nodeVar, new ObjectType(\Neos\ContentRepository\Domain\Service\NodeTypeManager::class))) { + $this->changed = true; + + return $this->contentRepository_getNodeTypeManager(); + } + } + + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + + $contentRepository = self::assign( + 'contentRepository', + $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default')) + ); + + return [ self::withTodoComment( 'Make this code aware of multiple Content Repositories.', - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - ) - ], - $node - ); + new Node\Stmt\Nop(), + ), + $contentRepository, + $node + ]; + } - return $this->contentRepository_getNodeTypeManager(); + return null; } } diff --git a/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php b/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php index 46cda49..234e64a 100644 --- a/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php +++ b/src/ContentRepository90/Rules/Traits/ContentRepositoryTrait.php @@ -5,15 +5,11 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Identifier; -use PhpParser\Node\Name\FullyQualified; +use Rector\PhpParser\Node\NodeFactory; trait ContentRepositoryTrait { - /** - * @var \Rector\Core\PhpParser\Node\NodeFactory - */ - protected $nodeFactory; + protected NodeFactory $nodeFactory; private function contentRepository_findWorkspaceByName(Expr $workspaceName) { diff --git a/src/ContentRepository90/Rules/Traits/DimensionSpacePointsTrait.php b/src/ContentRepository90/Rules/Traits/DimensionSpacePointsTrait.php index d5f5153..f203ee4 100644 --- a/src/ContentRepository90/Rules/Traits/DimensionSpacePointsTrait.php +++ b/src/ContentRepository90/Rules/Traits/DimensionSpacePointsTrait.php @@ -8,19 +8,16 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Param; +use PhpParser\Node\Name\FullyQualified; +use Rector\PhpParser\Node\NodeFactory; trait DimensionSpacePointsTrait { use FunctionsTrait; - /** - * @var \Rector\Core\PhpParser\Node\NodeFactory - */ - protected $nodeFactory; + protected NodeFactory $nodeFactory; - - private function dimensionSpacePoints_toLegacyDimensionArray( - ): Expr + private function dimensionSpacePoints_toLegacyDimensionArray(): Expr { return $this->nodeFactory->createFuncCall( 'array_map', @@ -30,7 +27,7 @@ private function dimensionSpacePoints_toLegacyDimensionArray( new Param( new Variable('dimensionSpacePoint'), null, - '\\' . DimensionSpacePoint::class + new FullyQualified(DimensionSpacePoint::class) ) ], 'expr' => $this->nodeFactory->createMethodCall('dimensionSpacePoint', 'toLegacyDimensionArray') diff --git a/src/ContentRepository90/Rules/Traits/NodeTrait.php b/src/ContentRepository90/Rules/Traits/NodeTrait.php index a649f9e..0f88376 100644 --- a/src/ContentRepository90/Rules/Traits/NodeTrait.php +++ b/src/ContentRepository90/Rules/Traits/NodeTrait.php @@ -4,14 +4,11 @@ namespace Neos\Rector\ContentRepository90\Rules\Traits; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Variable; +use Rector\PhpParser\Node\NodeFactory; trait NodeTrait { - /** - * @var \Rector\Core\PhpParser\Node\NodeFactory - */ - protected $nodeFactory; + protected NodeFactory $nodeFactory; private function node_nodeAggregateId(Expr $nodeVariable): Expr { diff --git a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php index 96334c7..be9ff16 100644 --- a/src/ContentRepository90/Rules/Traits/SubgraphTrait.php +++ b/src/ContentRepository90/Rules/Traits/SubgraphTrait.php @@ -5,17 +5,13 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Variable; +use Rector\PhpParser\Node\NodeFactory; trait SubgraphTrait { use NodeTrait; - /** - * @var \Rector\Core\PhpParser\Node\NodeFactory - */ - protected $nodeFactory; - + protected NodeFactory $nodeFactory; private function subgraph_findChildNodes( Expr $nodeVariable, @@ -54,7 +50,7 @@ private function subgraph_findChildNodes( ); } - private function subgraph_findNodePath(Variable $nodeVariable): Expr + private function subgraph_findNodePath(Expr $nodeVariable): Expr { return $this->nodeFactory->createMethodCall( 'subgraph', @@ -65,7 +61,7 @@ private function subgraph_findNodePath(Variable $nodeVariable): Expr ); } - private function subgraph_findNodePath_getDepth(Variable $nodeVariable): Expr + private function subgraph_findNodePath_getDepth(Expr $nodeVariable): Expr { return $this->nodeFactory->createMethodCall( $this->subgraph_findNodePath($nodeVariable), @@ -84,7 +80,7 @@ private function subgraph_findNodeById(Expr $nodeAggregateIdentifier) ); } - private function subgraph_findParentNode(Variable $nodeVariable): Expr + private function subgraph_findParentNode(Expr $nodeVariable): Expr { return $this->nodeFactory->createMethodCall( 'subgraph', diff --git a/src/ContentRepository90/Rules/Traits/ThisTrait.php b/src/ContentRepository90/Rules/Traits/ThisTrait.php index 79c42f2..1a0572f 100644 --- a/src/ContentRepository90/Rules/Traits/ThisTrait.php +++ b/src/ContentRepository90/Rules/Traits/ThisTrait.php @@ -4,14 +4,11 @@ namespace Neos\Rector\ContentRepository90\Rules\Traits; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Variable; +use Rector\PhpParser\Node\NodeFactory; trait ThisTrait { - /** - * @var \Rector\Core\PhpParser\Node\NodeFactory - */ - protected $nodeFactory; + protected NodeFactory $nodeFactory; private function this_contentRepositoryRegistry_subgraphForNode(Expr $nodeVariable): Expr { diff --git a/src/ContentRepository90/Rules/Traits/ValueObjectTrait.php b/src/ContentRepository90/Rules/Traits/ValueObjectTrait.php index fb15381..fc3317b 100644 --- a/src/ContentRepository90/Rules/Traits/ValueObjectTrait.php +++ b/src/ContentRepository90/Rules/Traits/ValueObjectTrait.php @@ -9,14 +9,12 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use PhpParser\Node\Expr; use PhpParser\Node\Scalar\String_; +use Rector\PhpParser\Node\NodeFactory; trait ValueObjectTrait { - /** - * @var \Rector\Core\PhpParser\Node\NodeFactory - */ - protected $nodeFactory; + protected NodeFactory $nodeFactory; private function contentRepositoryId_fromString(string $contentRepositoryName) { diff --git a/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspaceRector.php b/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspaceRector.php index 59e51ca..d9d39ac 100644 --- a/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspaceRector.php +++ b/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspaceRector.php @@ -6,19 +6,24 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class WorkspaceGetBaseWorkspaceRector extends AbstractRector +final class WorkspaceGetBaseWorkspaceRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -32,38 +37,68 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Core\SharedModel\Workspace\Workspace::class))) { - return null; - } - if (!$this->isName($node->name, 'getBaseWorkspace')) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment('Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories.', + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getBaseWorkspace' + ) { + $this->nodeVar = $node->var; + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Core\SharedModel\Workspace\Workspace::class))) { + $this->changed = true; + + return $this->nodeFactory->createMethodCall( + new Variable('contentRepository'), + 'findWorkspaceByName', + [$this->nodeFactory->createPropertyFetch($node->var, 'baseWorkspaceName')] + ); + } + } + + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories.', self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - ) - ], - $node - ); - - - return - $this->nodeFactory->createMethodCall( - new Variable('contentRepository'), - 'findWorkspaceByName', - [$this->nodeFactory->createPropertyFetch($node->var, 'baseWorkspaceName')] - ); + ), + $node, + ]; + } + + return null; } } diff --git a/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspacesRector.php b/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspacesRector.php index b2fe107..1532cb2 100644 --- a/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspacesRector.php +++ b/src/ContentRepository90/Rules/WorkspaceGetBaseWorkspacesRector.php @@ -4,21 +4,27 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class WorkspaceGetBaseWorkspacesRector extends AbstractRector +final class WorkspaceGetBaseWorkspacesRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -32,41 +38,71 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Core\SharedModel\Workspace\Workspace::class))) { - return null; - } - if (!$this->isName($node->name, 'getBaseWorkspaces')) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment('Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories.', - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), - ) - ], - $node - ); + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getBaseWorkspaces' + ) { + $this->nodeVar = $node->var; + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + return $this->nodeFactory->createMethodCall( + $this->nodeFactory->createMethodCall( + new Variable('contentRepository'), + 'findWorkspaces' + ), + 'getBaseWorkspaces', + [$this->nodeFactory->createPropertyFetch($node->var, 'workspaceName')] + ); + } + } - return - $this->nodeFactory->createMethodCall( - $this->nodeFactory->createMethodCall( - new Variable('contentRepository'), - 'findWorkspaces' + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'Check if you could change your code to work with the WorkspaceName value object instead and make this code aware of multiple Content Repositories.', + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), ), - 'getBaseWorkspaces', - [$this->nodeFactory->createPropertyFetch($node->var, 'workspaceName')] - ); + $node, + ]; + } + + return null; } } diff --git a/src/ContentRepository90/Rules/WorkspaceGetDescriptionRector.php b/src/ContentRepository90/Rules/WorkspaceGetDescriptionRector.php index 0db925c..d19a4c0 100644 --- a/src/ContentRepository90/Rules/WorkspaceGetDescriptionRector.php +++ b/src/ContentRepository90/Rules/WorkspaceGetDescriptionRector.php @@ -4,22 +4,25 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; -final class WorkspaceGetDescriptionRector extends AbstractRector +final class WorkspaceGetDescriptionRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -33,39 +36,68 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getDescription')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getDescription' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + return $this->nodeFactory->createPropertyFetch( + $this->nodeFactory->createPropertyFetch( + $this->this_workspaceService_getWorkspaceMetadata( + $this->contentRepositoryId_fromString('default'), + $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName') + ), + 'description', + ), 'value' + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Make this code aware of multiple Content Repositories.') - ], + $node->expr = $newExpr; + + return self::withTodoComment( + 'Make this code aware of multiple Content Repositories.', $node ); - - return - $this->nodeFactory->createPropertyFetch( - $this->nodeFactory->createPropertyFetch( - $this->this_workspaceService_getWorkspaceMetadata( - $this->contentRepositoryId_fromString('default'), - $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName') - ), - 'description', - ), 'value' - ); } } diff --git a/src/ContentRepository90/Rules/WorkspaceGetNameRector.php b/src/ContentRepository90/Rules/WorkspaceGetNameRector.php index 3df578b..40f5c8a 100644 --- a/src/ContentRepository90/Rules/WorkspaceGetNameRector.php +++ b/src/ContentRepository90/Rules/WorkspaceGetNameRector.php @@ -4,21 +4,25 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; -final class WorkspaceGetNameRector extends AbstractRector +final class WorkspaceGetNameRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -32,31 +36,61 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getName')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getName' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + $propertyFetchAggregateId = $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'); + return $this->nodeFactory->createPropertyFetch($propertyFetchAggregateId, 'value'); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Check if you could change your code to work with the WorkspaceName value object instead.') - ], + $node->expr = $newExpr; + + return self::withTodoComment( + '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/src/ContentRepository90/Rules/WorkspaceGetTitleRector.php b/src/ContentRepository90/Rules/WorkspaceGetTitleRector.php index 642bec2..c63af74 100644 --- a/src/ContentRepository90/Rules/WorkspaceGetTitleRector.php +++ b/src/ContentRepository90/Rules/WorkspaceGetTitleRector.php @@ -4,22 +4,25 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; -final class WorkspaceGetTitleRector extends AbstractRector +final class WorkspaceGetTitleRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -33,39 +36,68 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'getTitle')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'getTitle' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + return $this->nodeFactory->createPropertyFetch( + $this->nodeFactory->createPropertyFetch( + $this->this_workspaceService_getWorkspaceMetadata( + $this->contentRepositoryId_fromString('default'), + $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName') + ), + 'title', + ), 'value' + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Make this code aware of multiple Content Repositories.') - ], + $node->expr = $newExpr; + + return self::withTodoComment( + 'Make this code aware of multiple Content Repositories.', $node ); - - return - $this->nodeFactory->createPropertyFetch( - $this->nodeFactory->createPropertyFetch( - $this->this_workspaceService_getWorkspaceMetadata( - $this->contentRepositoryId_fromString('default'), - $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName') - ), - 'title', - ), 'value' - ); } } diff --git a/src/ContentRepository90/Rules/WorkspacePublishNodeRector.php b/src/ContentRepository90/Rules/WorkspacePublishNodeRector.php index c34348d..b5c2011 100644 --- a/src/ContentRepository90/Rules/WorkspacePublishNodeRector.php +++ b/src/ContentRepository90/Rules/WorkspacePublishNodeRector.php @@ -4,22 +4,25 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; -final class WorkspacePublishNodeRector extends AbstractRector +final class WorkspacePublishNodeRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -33,39 +36,68 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'publishNode')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'publishNode' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + return $this->nodeFactory->createMethodCall( + $this->this_workspacePublishingService(), + 'publishChangesInDocument', + [ + $this->contentRepositoryId_fromString('default'), + $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'), + $node->args[0] + ] + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Check if this matches your requirements as this is not a 100% replacement. Make this code aware of multiple Content Repositories.') - ], + $node->expr = $newExpr; + + return self::withTodoComment( + 'Check if this matches your requirements as this is not a 100% replacement. Make this code aware of multiple Content Repositories.', $node ); - - return - $this->nodeFactory->createMethodCall( - $this->this_workspacePublishingService(), - 'publishChangesInDocument', - [ - $this->contentRepositoryId_fromString('default'), - $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'), - $node->args[0] - ] - ); } } diff --git a/src/ContentRepository90/Rules/WorkspacePublishRector.php b/src/ContentRepository90/Rules/WorkspacePublishRector.php index a613364..dd10170 100644 --- a/src/ContentRepository90/Rules/WorkspacePublishRector.php +++ b/src/ContentRepository90/Rules/WorkspacePublishRector.php @@ -4,22 +4,25 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; -final class WorkspacePublishRector extends AbstractRector +final class WorkspacePublishRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) + public function __construct() { } @@ -33,38 +36,67 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'publish')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'publish' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + return $this->nodeFactory->createMethodCall( + $this->this_workspacePublishingService(), + 'publishWorkspace', + [ + $this->contentRepositoryId_fromString('default'), + $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName') + ] + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Make this code aware of multiple Content Repositories.') - ], + $node->expr = $newExpr; + + return self::withTodoComment( + 'Make this code aware of multiple Content Repositories.', $node ); - - return - $this->nodeFactory->createMethodCall( - $this->this_workspacePublishingService(), - 'publishWorkspace', - [ - $this->contentRepositoryId_fromString('default'), - $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName') - ] - ); } } diff --git a/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php b/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php index ae0c29c..9ef72fc 100644 --- a/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php +++ b/src/ContentRepository90/Rules/WorkspaceRepositoryCountByNameRector.php @@ -7,18 +7,23 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class WorkspaceRepositoryCountByNameRector extends AbstractRector +final class WorkspaceRepositoryCountByNameRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } @@ -32,39 +37,70 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'countByName')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'countByName' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + $this->changed = true; + + return new Node\Expr\Ternary( + new Node\Expr\BinaryOp\NotIdentical( + $this->contentRepository_findWorkspaceByName($this->workspaceName_fromString($node->args[0]->value)), + new Expr\ConstFetch(new Node\Name('null')) + ), + new Node\Scalar\LNumber(1), + new Node\Scalar\LNumber(0) + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } + $node->expr = $newExpr; - $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_findWorkspaceByName($this->workspaceName_fromString($node->args[0]->value)), - new Expr\ConstFetch(new Node\Name('null')) + return [ + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), + self::withTodoComment( + 'remove ternary operator (...? 1 : 0 ) - unnecessary complexity', + $node ), - new Node\Scalar\LNumber(1), - new Node\Scalar\LNumber(0) - ); + ]; } } diff --git a/src/ContentRepository90/Rules/WorkspaceRepositoryFindByBaseWorkspaceRector.php b/src/ContentRepository90/Rules/WorkspaceRepositoryFindByBaseWorkspaceRector.php index 49741a4..27043a4 100644 --- a/src/ContentRepository90/Rules/WorkspaceRepositoryFindByBaseWorkspaceRector.php +++ b/src/ContentRepository90/Rules/WorkspaceRepositoryFindByBaseWorkspaceRector.php @@ -6,19 +6,24 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class WorkspaceRepositoryFindByBaseWorkspaceRector extends AbstractRector +final class WorkspaceRepositoryFindByBaseWorkspaceRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) + public function __construct() { } @@ -33,43 +38,74 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'findByBaseWorkspace')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'findByBaseWorkspace' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + $this->changed = true; + + return $this->nodeFactory->createMethodCall( + $this->nodeFactory->createMethodCall( + new Variable('contentRepository'), + 'findWorkspaces', + [] + ), + 'getDependantWorkspaces', + [ + $this->workspaceName_fromString($node->args[0]->value) + ] + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } + $node->expr = $newExpr; - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment( - 'Make this code aware of multiple Content Repositories.', - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default')))), - ], - $node - ); - - return $this->nodeFactory->createMethodCall( - $this->nodeFactory->createMethodCall( - new Variable('contentRepository'), - 'findWorkspaces', - [] + return [ + new Nop(), // Needed, to render the comment below + self::withTodoComment( + 'Make this code aware of multiple Content Repositories.', + self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default'))), ), - 'getDependantWorkspaces', - [ - $this->workspaceName_fromString($node->args[0]->value) - ] - ); + $node, + ]; } } diff --git a/src/ContentRepository90/Rules/WorkspaceRepositoryFindByIdentifierRector.php b/src/ContentRepository90/Rules/WorkspaceRepositoryFindByIdentifierRector.php index 139d36d..67bb2b6 100644 --- a/src/ContentRepository90/Rules/WorkspaceRepositoryFindByIdentifierRector.php +++ b/src/ContentRepository90/Rules/WorkspaceRepositoryFindByIdentifierRector.php @@ -6,19 +6,24 @@ use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\Node\Expr; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -final class WorkspaceRepositoryFindByIdentifierRector extends AbstractRector +final class WorkspaceRepositoryFindByIdentifierRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector - ) { + public function __construct() + { } @@ -32,34 +37,66 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ - public function refactor(Node $node): ?Node + public function refactor(Node $node): ?array { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'findByIdentifier')) { + + $traverser = new NodeTraverser(); + $traverser->addVisitor( + $visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + use AllTraits; + + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + protected NodeFactory $nodeFactory, + public bool $changed = false, + public ?Node\Expr $nodeVar = null, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'findByIdentifier' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(\Neos\ContentRepository\Domain\Repository\WorkspaceRepository::class))) { + $this->changed = true; + + return $this->contentRepository_findWorkspaceByName( + $this->workspaceName_fromString($node->args[0]->value) + ); + } + } + return null; + } + }); + + /** @var Node\Expr $newExpr */ + $newExpr = $traverser->traverse([$node->expr])[0]; + + if (!$visitor->changed) { return null; } + $node->expr = $newExpr; - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::withTodoComment( - 'Make this code aware of multiple Content Repositories.', - self::assign('contentRepository', $this->this_contentRepositoryRegistry_get($this->contentRepositoryId_fromString('default')))), - ], - $node - ); - - return - $this->contentRepository_findWorkspaceByName($this->workspaceName_fromString($node->args[0]->value)); + return [ + new Nop(), // Needed, to render the comment below + 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/ContentRepository90/Rules/WorkspaceSetDescriptionRector.php b/src/ContentRepository90/Rules/WorkspaceSetDescriptionRector.php index c7b5bf5..faab7c7 100644 --- a/src/ContentRepository90/Rules/WorkspaceSetDescriptionRector.php +++ b/src/ContentRepository90/Rules/WorkspaceSetDescriptionRector.php @@ -4,25 +4,26 @@ namespace Neos\Rector\ContentRepository90\Rules; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Rector\Utility\CodeSampleLoader; use PhpParser\Node; -use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Identifier; +use PhpParser\Node\Scalar\String_; +use PhpParser\NodeTraverser; +use PhpParser\NodeVisitorAbstract; use PHPStan\Type\ObjectType; -use Rector\Core\Rector\AbstractRector; -use Rector\PostRector\Collector\NodesToAddCollector; +use Rector\NodeTypeResolver\NodeTypeResolver; +use Rector\PhpParser\Node\NodeFactory; +use Rector\Rector\AbstractRector; +use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; -final class WorkspaceSetDescriptionRector extends AbstractRector +final class WorkspaceSetDescriptionRector extends AbstractRector implements DocumentedRuleInterface { use AllTraits; - public function __construct( - private readonly NodesToAddCollector $nodesToAddCollector, - ) - { - } - public function getRuleDefinition(): RuleDefinition { return CodeSampleLoader::fromFile('"Workspace::setDescription()" will be rewritten', __CLASS__); @@ -33,37 +34,61 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'setDescription')) { - return null; + + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + private readonly NodeFactory $nodeFactory, + public bool $changed = false, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'setDescription' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + $serviceCall = $this->nodeFactory->createMethodCall( + $this->nodeFactory->createPropertyFetch('this', 'workspaceService'), + 'setWorkspaceDescription', + [ + $this->nodeFactory->createStaticCall(ContentRepositoryId::class, 'fromString', [new String_('default')]), + $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'), + $this->nodeFactory->createStaticCall(\Neos\Neos\Domain\Model\WorkspaceDescription::class, 'fromString', [$node->args[0]]) + ] + ); + return $serviceCall; + } + } + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + self::withTodoComment('Make this code aware of multiple Content Repositories.', $node); + return $node; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Make this code aware of multiple Content Repositories.') - ], - $node - ); - return - $this->nodeFactory->createMethodCall( - $this->this_workspaceService(), - 'setWorkspaceDescription', - [$this->contentRepositoryId_fromString('default'), - $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'), - $this->nodeFactory->createStaticCall(\Neos\Neos\Domain\Model\WorkspaceDescription::class, 'fromString', [$node->args[0]]) - ] - ); + return null; } } diff --git a/src/ContentRepository90/Rules/WorkspaceSetTitleRector.php b/src/ContentRepository90/Rules/WorkspaceSetTitleRector.php index 172e156..af93856 100644 --- a/src/ContentRepository90/Rules/WorkspaceSetTitleRector.php +++ b/src/ContentRepository90/Rules/WorkspaceSetTitleRector.php @@ -1,69 +1,91 @@ > - */ public function getNodeTypes(): array { - return [\PhpParser\Node\Expr\MethodCall::class]; + return [Node\Stmt::class]; } /** - * @param \PhpParser\Node\Expr\MethodCall $node + * @param Node\Stmt $node */ public function refactor(Node $node): ?Node { - assert($node instanceof Node\Expr\MethodCall); - - if (!$this->isObjectType($node->var, new ObjectType(Workspace::class))) { + if (!in_array('expr', $node->getSubNodeNames())) { return null; } - if (!$this->isName($node->name, 'setTitle')) { - return null; + + $traverser = new NodeTraverser(); + $traverser->addVisitor($visitor = new class($this->nodeTypeResolver, $this->nodeFactory) extends NodeVisitorAbstract { + public function __construct( + private readonly NodeTypeResolver $nodeTypeResolver, + private readonly NodeFactory $nodeFactory, + public bool $changed = false, + ) { + } + + public function leaveNode(Node $node) + { + if ( + $node instanceof MethodCall && + $node->name instanceof Identifier && + $node->name->toString() === 'setTitle' + ) { + if ($this->nodeTypeResolver->isObjectType($node->var, new ObjectType(Workspace::class))) { + $this->changed = true; + + $serviceCall = $this->nodeFactory->createMethodCall( + $this->nodeFactory->createPropertyFetch('this', 'workspaceService'), + 'setWorkspaceTitle', + [ + $this->nodeFactory->createStaticCall(ContentRepositoryId::class, 'fromString', [new String_('default')]), + $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'), + $this->nodeFactory->createStaticCall(\Neos\Neos\Domain\Model\WorkspaceTitle::class, 'fromString', [$node->args[0]]) + ] + ); + return $serviceCall; + } + } + return null; + } + }); + + $newExpr = $traverser->traverse([$node->expr])[0]; + + if ($visitor->changed) { + $node->expr = $newExpr; + self::withTodoComment('Make this code aware of multiple Content Repositories.', $node); + return $node; } - $this->nodesToAddCollector->addNodesBeforeNode( - [ - self::todoComment('Make this code aware of multiple Content Repositories.') - ], - $node - ); - return - $this->nodeFactory->createMethodCall( - $this->this_workspaceService(), - 'setWorkspaceTitle', - [$this->contentRepositoryId_fromString('default'), - $this->nodeFactory->createPropertyFetch($node->var, 'workspaceName'), - $this->nodeFactory->createStaticCall(\Neos\Neos\Domain\Model\WorkspaceTitle::class, 'fromString', [$node->args[0]]) - ] - ); + return null; } -} +} \ No newline at end of file diff --git a/src/ContentRepository90/Rules/YamlDimensionConfigRector.php b/src/ContentRepository90/Rules/YamlDimensionConfigRector.php deleted file mode 100644 index 0998c75..0000000 --- a/src/ContentRepository90/Rules/YamlDimensionConfigRector.php +++ /dev/null @@ -1,115 +0,0 @@ - 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/ContentRepository90/Rules/YamlRoutePartHandlerRector.php b/src/ContentRepository90/Rules/YamlRoutePartHandlerRector.php deleted file mode 100644 index a7b33a0..0000000 --- a/src/ContentRepository90/Rules/YamlRoutePartHandlerRector.php +++ /dev/null @@ -1,49 +0,0 @@ - $routeConfig) { - if (!is_array($routeConfig)) { - continue; - } - if (!isset($routeConfig['routeParts']) || !is_array($routeConfig['routeParts'])) { - continue; - } - - $handlerToReplace = [ - \Neos\Neos\Routing\FrontendNodeRoutePartHandler::class, - \Neos\Neos\Routing\FrontendNodeRoutePartHandlerInterface::class, - ]; - - foreach ($routeConfig['routeParts'] as $routePartKey => $routePart) { - if (isset($routePart['handler']) && in_array($routePart['handler'], $handlerToReplace)) { - $parsed[$routeConfigKey]['routeParts'][$routePartKey]['handler'] = \Neos\Neos\FrontendRouting\FrontendNodeRoutePartHandlerInterface::class; - } - } - } - - return YamlWithComments::dump($parsed); - } -} diff --git a/src/Core/FusionProcessing/AfxParser/AfxParserException.php b/src/Core/FusionProcessing/AfxParser/AfxParserException.php deleted file mode 100644 index 8bf5f48..0000000 --- a/src/Core/FusionProcessing/AfxParser/AfxParserException.php +++ /dev/null @@ -1,22 +0,0 @@ -isOpeningBracket()) { - $lexer->consume(); - } - - $identifier = Identifier::parse($lexer); - - try { - $attributes = []; - $children = []; - - if ($lexer->isWhitespace()) { - while ($lexer->isWhitespace()) { - $lexer->consume(); - } - - while (!$lexer->isForwardSlash() && !$lexer->isClosingBracket()) { - if ($lexer->isOpeningBrace()) { - $attributes[] = [ - 'type' => 'spread', - 'payload' => Spread::parse($lexer) - ]; - } else { - $attributes[] = [ - 'type' => 'prop', - 'payload' => Prop::parse($lexer) - ]; - } - while ($lexer->isWhitespace()) { - $lexer->consume(); - } - } - } - - if ($lexer->isForwardSlash()) { - $lexer->consume(); - - if ($lexer->isClosingBracket()) { - $lexer->consume(); - - return [ - 'identifier' => $identifier, - 'attributes' => $attributes, - 'children' => $children, - 'selfClosing' => true - ]; - } else { - throw new AfxParserException(sprintf('Self closing tag "%s" misses closing bracket.', $identifier), 1557860567); - } - } - - if ($lexer->isClosingBracket()) { - $lexer->consume(); - } else { - throw new AfxParserException(sprintf('Tag "%s" did not end with closing bracket.', $identifier), 1557860573); - } - - $children = AfxNodeList::parse($lexer); - - if ($lexer->isOpeningBracket()) { - $lexer->consume(); - - if ($lexer->isForwardSlash()) { - $lexer->consume(); - } else { - throw new AfxParserException(sprintf( - 'Opening-bracket for closing of tag "%s" was not followed by slash.', - $identifier - ), 1557860584); - } - } else { - throw new AfxParserException(sprintf( - 'Opening-bracket for closing of tag "%s" expected.', - $identifier - ), 1557860587); - } - - $closingIdentifier = Identifier::parse($lexer); - - if ($closingIdentifier !== $identifier) { - throw new AfxParserException(sprintf( - 'Closing-tag identifier "%s" did not match opening-tag identifier "%s".', - $closingIdentifier, - $identifier - ), 1557860595); - } - - if ($lexer->isClosingBracket()) { - $lexer->consume(); - return [ - 'identifier' => $identifier, - 'attributes' => $attributes, - 'children' => $children, - 'selfClosing' => false - ]; - } else { - throw new AfxParserException(sprintf('Closing tag "%s" did not end with closing-bracket.', $identifier), 1557860618); - } - - if ($lexer->isEnd()) { - throw new AfxParserException(sprintf('Tag was %s is not closed.', $identifier), 1557860622); - } - } catch (AfxParserException $e) { - throw new AfxParserException(sprintf('<%s> %s', $identifier, $e->getMessage()), 1557860627); - } - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/AfxNodeList.php b/src/Core/FusionProcessing/AfxParser/Expression/AfxNodeList.php deleted file mode 100644 index 8dfc6ef..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/AfxNodeList.php +++ /dev/null @@ -1,90 +0,0 @@ -isEnd()) { - if ($lexer->isOpeningBracket()) { - $lexer->consume(); - if ($currentText !== '') { - $contents[] = [ - 'type' => 'text', - 'payload' => $currentText - ]; - } - if ($lexer->isForwardSlash()) { - $lexer->rewind(); - return $contents; - } - if ($lexer->isExclamationMark()) { - $lexer->rewind(); - $contents[] = [ - 'type' => 'comment', - 'payload' => Comment::parse($lexer) - ]; - $currentText = ''; - continue; - } else { - $lexer->rewind(); - $contents[] = [ - 'type' => 'node', - 'payload' => AfxNode::parse($lexer) - ]; - $currentText = ''; - continue; - } - } - - if ($lexer->isOpeningBrace()) { - if ($currentText) { - $contents[] = [ - 'type' => 'text', - 'payload' => $currentText - ]; - } - - $contents[] = [ - 'type' => 'expression', - 'payload' => Expression::parse($lexer) - ]; - $currentText = ''; - continue; - } - - $currentText .= $lexer->consume(); - } - - if ($lexer->isEnd() && $currentText !== '') { - $contents[] = [ - 'type' => 'text', - 'payload' => $currentText - ]; - } - - return $contents; - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/Comment.php b/src/Core/FusionProcessing/AfxParser/Expression/Comment.php deleted file mode 100644 index 7811bad..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/Comment.php +++ /dev/null @@ -1,54 +0,0 @@ -isOpeningBracket() && $lexer->peek(4) === '') { - $lexer->consume(); - $lexer->consume(); - $lexer->consume(); - return $currentComment; - } - if ($lexer->isEnd()) { - throw new AfxParserException(sprintf('Comment not closed.')); - } - $currentComment .= $lexer->consume(); - } - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/Expression.php b/src/Core/FusionProcessing/AfxParser/Expression/Expression.php deleted file mode 100644 index 7fa001c..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/Expression.php +++ /dev/null @@ -1,67 +0,0 @@ -isOpeningBrace()) { - $lexer->consume(); - } else { - throw new AfxParserException('Expression without braces', 1557860467); - } - $fromOffset = $lexer->characterPosition; - while (true) { - if ($lexer->isEnd()) { - throw new AfxParserException(sprintf('Unfinished Expression "%s"', $contents), 1557860496); - } - - if ($lexer->isOpeningBrace()) { - $braceCount++; - } - - if ($lexer->isClosingBrace()) { - if ($braceCount === 0) { - $toOffset = $lexer->characterPosition; - $lexer->consume(); - return [ - 'from' => $fromOffset, - 'to' => $toOffset, - 'contents' => $contents - ]; - } - - $braceCount--; - } - - $contents .= $lexer->consume(); - } - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/Identifier.php b/src/Core/FusionProcessing/AfxParser/Expression/Identifier.php deleted file mode 100644 index 7473e4e..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/Identifier.php +++ /dev/null @@ -1,64 +0,0 @@ -isAlphaNumeric(): - case $lexer->isDot(): - case $lexer->isColon(): - case $lexer->isMinus(): - case $lexer->isUnderscore(): - case $lexer->isAt(): - case $lexer->isDoubleQuote(): - case $lexer->isSingleQuote(): - case $lexer->isBackSlash() && $lexer->peek(2) === '\"': - case $lexer->isBackSlash() && $lexer->peek(2) === '\\\'': - $identifier .= $lexer->consume(); - break; - case $lexer->isEqualSign(): - case $lexer->isWhiteSpace(): - case $lexer->isClosingBracket(): - case $lexer->isForwardSlash(): - return $identifier; - break; - default: - $unexpected_character = $lexer->consume(); - throw new AfxParserException(sprintf( - 'Unexpected character "%s" in identifier "%s"', - $unexpected_character, - $identifier - ), 1557860650); - } - } - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/Prop.php b/src/Core/FusionProcessing/AfxParser/Expression/Prop.php deleted file mode 100644 index c95c525..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/Prop.php +++ /dev/null @@ -1,71 +0,0 @@ -isEqualSign()) { - $lexer->consume(); - switch (true) { - case $lexer->isSingleQuote(): - case $lexer->isDoubleQuote(): - $value = [ - 'type' => 'string', - 'payload' => StringLiteral::parse($lexer), - 'identifier' => $identifier - ]; - break; - - case $lexer->isOpeningBrace(): - $value = [ - 'type' => 'expression', - 'payload' => Expression::parse($lexer), - 'identifier' => $identifier - ]; - break; - default: - throw new AfxParserException(sprintf( - 'Prop-assignment "%s" was not followed by quotes or braces', - $identifier - ), 1557860545); - } - } elseif ($lexer->isWhiteSpace() || $lexer->isForwardSlash() || $lexer->isClosingBracket()) { - $value = [ - 'type' => 'boolean', - 'payload' => true, - 'identifier' => $identifier - ]; - } else { - throw new AfxParserException(sprintf('Prop identifier "%s" is neither assignment nor boolean', $identifier), 1557860552); - } - - return $value; - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/Spread.php b/src/Core/FusionProcessing/AfxParser/Expression/Spread.php deleted file mode 100644 index 268784a..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/Spread.php +++ /dev/null @@ -1,74 +0,0 @@ -isOpeningBrace() && $lexer->peek(4) === '{...') { - $lexer->consume(); - $lexer->consume(); - $lexer->consume(); - $lexer->consume(); - } else { - throw new AfxParserException('Spread without braces', 1557860522); - } - - $fromOffset = $lexer->characterPosition; - while (true) { - if ($lexer->isEnd()) { - throw new AfxParserException(sprintf('Unfinished Spread "%s"', $contents), 1557860526); - } - - if ($lexer->isOpeningBrace()) { - $braceCount++; - } - - if ($lexer->isClosingBrace()) { - if ($braceCount === 0) { - $toOffset = $lexer->characterPosition; - $lexer->consume(); - return [ - 'type' => 'expression', - 'payload' => [ - 'from' => $fromOffset, - 'to' => $toOffset, - 'contents' => $contents - ] - ]; - } - - $braceCount--; - } - - $contents .= $lexer->consume(); - } - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Expression/StringLiteral.php b/src/Core/FusionProcessing/AfxParser/Expression/StringLiteral.php deleted file mode 100644 index dbbfe0b..0000000 --- a/src/Core/FusionProcessing/AfxParser/Expression/StringLiteral.php +++ /dev/null @@ -1,67 +0,0 @@ -isSingleQuote() || $lexer->isDoubleQuote()) { - $openingQuoteSign = $lexer->consume(); - } else { - throw new AfxParserException('Unquoted String literal', 1557860514); - } - - while (true) { - if ($lexer->isEnd()) { - throw new AfxParserException(sprintf('Unfinished string literal "%s"', $contents), 1557860504); - } - - if ($lexer->isBackSlash() && !$willBeEscaped) { - $willBeEscaped = true; - $lexer->consume(); - continue; - } - - if ($lexer->isSingleQuote() || $lexer->isDoubleQuote()) { - $closingQuoteSign = $lexer->consume(); - if (!$willBeEscaped && $openingQuoteSign === $closingQuoteSign) { - return $contents; - } - - $contents .= $closingQuoteSign; - $willBeEscaped = false; - continue; - } - - $contents .= $lexer->consume(); - $willBeEscaped = false; - } - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Lexer.php b/src/Core/FusionProcessing/AfxParser/Lexer.php deleted file mode 100644 index 42c18fc..0000000 --- a/src/Core/FusionProcessing/AfxParser/Lexer.php +++ /dev/null @@ -1,287 +0,0 @@ -string = $string; - $this->currentCharacter = ($string !== '') ? $string[0] : null; - $this->characterPosition = 0; - } - - /** - * Checks if the current character is whitespace - * - * @return boolean - */ - public function isWhiteSpace(): bool - { - return ctype_space($this->currentCharacter); - } - - /** - * Checks if the current character is a letter - * - * @return boolean - */ - public function isAlpha(): bool - { - return ctype_alpha($this->currentCharacter); - } - - /** - * Checks if the current character is alpha-numeric - * - * @return boolean - */ - public function isAlphaNumeric(): bool - { - return ctype_alnum($this->currentCharacter); - } - - /** - * Checks if the current character is a colon - * - * @return boolean - */ - public function isColon(): bool - { - return $this->currentCharacter === ':'; - } - - /** - * Checks if the current character is a dot - * - * @return boolean - */ - public function isDot(): bool - { - return $this->currentCharacter === '.'; - } - - /** - * Checks if the current character is a @ - * - * @return boolean - */ - public function isAt(): bool - { - return $this->currentCharacter === '@'; - } - - /** - * Checks if the current character is a minus - * - * @return boolean - */ - public function isMinus(): bool - { - return $this->currentCharacter === '-'; - } - - /** - * Checks if the current character is an underscore - * - * @return boolean - */ - public function isUnderscore(): bool - { - return $this->currentCharacter === '_'; - } - - /** - * Checks if the current character is an equal sign - * - * @return boolean - */ - public function isEqualSign(): bool - { - return $this->currentCharacter === '='; - } - - /** - * Checks if the current character is an opening bracket - * - * @return boolean - */ - public function isOpeningBracket(): bool - { - return $this->currentCharacter === '<'; - } - - /** - * Checks if the current character is a closing bracket - * - * @return boolean - */ - public function isClosingBracket(): bool - { - return $this->currentCharacter === '>'; - } - - /** - * Checks if the current character is an opening curly brace - * - * @return boolean - */ - public function isOpeningBrace(): bool - { - return $this->currentCharacter === '{'; - } - - /** - * Checks if the current character is a closing curly brace - * - * @return boolean - */ - public function isClosingBrace(): bool - { - return $this->currentCharacter === '}'; - } - - /** - * Checks if the current character is a forward slash - * - * @return boolean - */ - public function isForwardSlash(): bool - { - return $this->currentCharacter === '/'; - } - - /** - * Checks if the current character is a back slash - * - * @return boolean - */ - public function isBackSlash(): bool - { - return $this->currentCharacter === '\\'; - } - - /** - * Checks if the current character is a single quote - * - * @return boolean - */ - public function isSingleQuote(): bool - { - return $this->currentCharacter === '\''; - } - - /** - * Checks if the current character is a double quote - * - * @return boolean - */ - public function isDoubleQuote(): bool - { - return $this->currentCharacter === '"'; - } - - /** - * Checks if the current character is an exclamation mark - * - * @return boolean - */ - public function isExclamationMark(): bool - { - return $this->currentCharacter === '!'; - } - - /** - * Checks if the iteration has ended - * - * @return boolean - */ - public function isEnd(): bool - { - return $this->currentCharacter === null; - } - - /** - * Rewinds the iteration by one step - * - * @return void - */ - public function rewind(): void - { - $this->currentCharacter = $this->string[--$this->characterPosition]; - } - - /** - * Peek several characters in advance and return the next n characters - * - * @param int $characterNumber - * @return string|null - */ - public function peek($characterNumber = 1): ?string - { - if ($this->characterPosition < strlen($this->string) - 1) { - return substr($this->string, $this->characterPosition, $characterNumber); - } else { - return null; - } - } - - /** - * Returns the current character and moves one step forward - * - * @return string|null - */ - public function consume(): ?string - { - $c = $this->currentCharacter; - if ($this->characterPosition < strlen($this->string) - 1) { - $this->currentCharacter = $this->string[++$this->characterPosition]; - } else { - $this->currentCharacter = null; - } - - return $c; - } -} diff --git a/src/Core/FusionProcessing/AfxParser/Parser.php b/src/Core/FusionProcessing/AfxParser/Parser.php deleted file mode 100644 index 01b69ec..0000000 --- a/src/Core/FusionProcessing/AfxParser/Parser.php +++ /dev/null @@ -1,44 +0,0 @@ -lexer = new Lexer($string); - } - - /** - * @return array - * @throws AfxParserException - */ - public function parse(): array - { - return Expression\AfxNodeList::parse($this->lexer); - } -} diff --git a/src/Core/FusionProcessing/EelExpressionTransformer.php b/src/Core/FusionProcessing/EelExpressionTransformer.php deleted file mode 100644 index 3a6bcce..0000000 --- a/src/Core/FusionProcessing/EelExpressionTransformer.php +++ /dev/null @@ -1,230 +0,0 @@ -findAllEelExpressions(); - - // apply processing function on Eel expressions - $eelExpressions = $eelExpressions->map( - fn(EelExpressionPosition $expressionPosition) => $expressionPosition->withEelExpression( - $processingFunction($expressionPosition->eelExpression, $expressionPosition->fusionPath) - ) - ); - - return new self($this->render($eelExpressions)); - } - - /** - * @throws ParserException - * @throws AfxParserException - */ - public function addCommentsIfRegexMatches(string $regex, string $comment): self - { - $regexCommentTemplatePair = new RegexCommentTemplatePair($regex, $comment); - return $this->addCommentsIfRegexesMatch([$regexCommentTemplatePair]); - } - - /** - * @param RegexCommentTemplatePair[] $regexCommentTemplatePairs - * @throws AfxParserException - * @throws ParserException - */ - public function addCommentsIfRegexesMatch(array $regexCommentTemplatePairs): self - { - $eelExpressions = $this->findAllEelExpressions(); - - $comments = []; - // fill $comments - foreach ($regexCommentTemplatePairs as $regexCommentTemplatePair) { - $eelExpressions->map( - function (EelExpressionPosition $expressionPosition) use ($regexCommentTemplatePair, &$comments) { - $comments = array_merge( - $comments, - $this->getPrecedingCommentsForEelExpressionPosition($expressionPosition, $regexCommentTemplatePair->regex, $regexCommentTemplatePair->template) - ); - return $expressionPosition; - } - ); - } - - $comments = $this->replaceLinePlaceholderWithinCommentTemplates($comments); - - if (count($comments)) { - $precedingComments = array_map(fn($comment) => $comment->text, $comments); - return new EelExpressionTransformer(implode("\n", $precedingComments) . "\n" . $this->fileContent); - } else { - return $this; - } - } - - /** @return PrecedingFusionFileComment[] */ - private function getPrecedingCommentsForEelExpressionPosition(EelExpressionPosition $expressionPosition, string $regex, string $template): array - { - $comments = []; - - $matches = []; - if (preg_match_all($regex, $expressionPosition->eelExpression, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { - foreach ($matches as $match) { - // $match[0][0] => the fully matched string - // $match[0][1] => the start offset of the string - $offsetOfFullMatch = $match[0][1]; - $lineNumberOfMatch = substr_count($this->fileContent, "\n", 0, $expressionPosition->fromOffset + $offsetOfFullMatch); - // we add one because the number of counted new line characters will be one less than the actual line number - $lineNumberOfMatch += 1; - // avoid adding the same comment multiple times per line by using an explicit array key - $commentKey = sha1($lineNumberOfMatch . $regex . $template); - $comments[$commentKey] = new PrecedingFusionFileComment($lineNumberOfMatch, $template); - } - } - - return $comments; - } - - /** - * @param PrecedingFusionFileComment[] $comments - * @return PrecedingFusionFileComment[] - */ - private function replaceLinePlaceholderWithinCommentTemplates(array $comments): array - { - foreach ($comments as $comment) { - $finalLineNumber = $comment->lineNumberOfMatch + count($comments); - $comment->text = str_replace('%LINE', (string)$finalLineNumber, $comment->template); - } - return $comments; - } - - /** - * @throws ParserException - * @throws AfxParserException - */ - public function findAllEelExpressions(): EelExpressionPositions - { - $eelExpressions = CustomObjectTreeParser::findEelExpressions($this->fileContent); - $afxExpressions = CustomObjectTreeParser::findAfxExpressions($this->fileContent); - foreach ($afxExpressions as $afxExpression) { - $parser = new AfxParser\Parser($afxExpression->code); - $ast = $parser->parse(); - $eelExpressionsInAfx = self::findEelExpressionsInAfxAst($ast); - $eelExpressionsInAfx = $eelExpressionsInAfx->withOffset($afxExpression->fromOffset); - - $eelExpressions = $eelExpressions->addAndSort($eelExpressionsInAfx); - } - return $eelExpressions; - } - - /** - * @param array $afxAst - * @return EelExpressionPositions - */ - private static function findEelExpressionsInAfxAst(array $afxAst): EelExpressionPositions - { - $result = []; - foreach ($afxAst as $afxElement) { - self::findEelExpressionsInAfxAstElement($afxElement, $result); - } - return EelExpressionPositions::fromArray($result); - } - - /** - * @param array $afxElement - * @param EelExpressionPosition[] $result - * @return void - */ - private static function findEelExpressionsInAfxAstElement(array $afxElement, array &$result): void - { - if ($afxElement['type'] === 'text') { - return; - } - if ($afxElement['type'] === 'string') { - return; - } - if ($afxElement['type'] === 'node') { - foreach ($afxElement['payload']['attributes'] as $attribute) { - self::findEelExpressionsInAfxAstElement($attribute, $result); - } - foreach ($afxElement['payload']['children'] as $child) { - self::findEelExpressionsInAfxAstElement($child, $result); - } - } - if ($afxElement['type'] === 'prop') { - self::findEelExpressionsInAfxAstElement($afxElement['payload'], $result); - } - if ($afxElement['type'] === 'spread') { - // We found an Eel expression in a spread operation - $result[] = new EelExpressionPosition( - $afxElement['payload']['payload']['contents'], - $afxElement['payload']['payload']['from'] + 1, - $afxElement['payload']['payload']['to'] + 1, - null - ); - return; - } - - if ($afxElement['type'] === 'expression') { - // We found an Eel expression - $result[] = new EelExpressionPosition( - $afxElement['payload']['contents'], - $afxElement['payload']['from'] + 1, - $afxElement['payload']['to'] + 1, - null - ); - } - } - - private function render(EelExpressionPositions $eelExpressions): string - { - if ($eelExpressions->isEmpty()) { - return $this->fileContent; - } - - // Render [fusion] [1st eel expression] - $processedFusionString = substr($this->fileContent, 0, $eelExpressions->first()->fromOffset); - $processedFusionString .= $eelExpressions->first()->eelExpression; - $toOffsetOfLastSeenEelExpression = $eelExpressions->first()->toOffset; - - // Render all Eel expressions (2...n) - foreach ($eelExpressions->withoutFirst() as $eelExpression) { - $processedFusionString .= substr($this->fileContent, $toOffsetOfLastSeenEelExpression, $eelExpression->fromOffset - $toOffsetOfLastSeenEelExpression); - $processedFusionString .= $eelExpression->eelExpression; - $toOffsetOfLastSeenEelExpression = $eelExpression->toOffset; - } - // render remaining Fusion in File - $processedFusionString .= substr($this->fileContent, $toOffsetOfLastSeenEelExpression); - - return $processedFusionString; - } - - public function getProcessedContent(): string - { - return $this->fileContent; - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/CacheSegmentParser.php b/src/Core/FusionProcessing/Fusion/Cache/CacheSegmentParser.php deleted file mode 100644 index 61bac1b..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/CacheSegmentParser.php +++ /dev/null @@ -1,314 +0,0 @@ -randomCacheMarker = $randomCacheMarker; - $this->randomCacheMarkerLength = strlen($randomCacheMarker); - $this->content = $content; - $this->outerSegmentContent = ''; - $currentPosition = 0; - $nextStartPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_START_TOKEN); - while ($nextStartPosition !== false) { - $part = $this->extractContent($currentPosition, $nextStartPosition); - $this->output .= $part; - $this->outerSegmentContent .= $part; - $result = $this->parseSegment($nextStartPosition); - $this->output .= $result['cleanContent']; - $this->outerSegmentContent .= $result['embed']; - $currentPosition = $this->calculateCurrentPosition($result['endPosition']); - $nextStartPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_START_TOKEN); - } - - $nextEndPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_END_TOKEN); - if ($nextEndPosition !== false) { - throw new Exception(sprintf('Exceeding segment end token after position %d', $currentPosition), 1391853689); - } - - $currentPosition = isset($result['endPosition']) ? $this->calculateCurrentPosition($result['endPosition']) : $currentPosition; - $part = $this->extractContent($currentPosition); - $this->output .= $part; - $this->outerSegmentContent .= $part; - // we no longer need the content - unset($this->content); - } - - /** - * Parses a segment at current position diving down into nested segments. - * - * The returned segmentData array has the following keys: - * - identifier -> The identifier of this entry for cached segments (or the eval expression for everything else) - * - type -> The type of segment (one of the ContentCache::SEGMENT_TYPE_* constants) - * - context -> eventual context information saved for a cached segment (optional) - * - metadata -> cache entry metadata like tags - * - content -> the content of this segment including embed code for sub segments - * - cleanContent -> the raw content without any cache references for this segment and all sub segments - * - embed -> the placeholder content for this segment to be used in "content" of parent segments - * - * @param integer $currentPosition - * @return array - * @throws Exception - */ - protected function parseSegment($currentPosition) - { - $nextStartPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_START_TOKEN); - if ($nextStartPosition !== $currentPosition) { - throw new Exception(sprintf('The current position (%d) is not the start of a segment, next start position %d', $currentPosition, $nextStartPosition), 1472464124); - } - - $segmentData = [ - 'identifier' => '', - 'type' => '', - 'context' => '', - 'metadata' => '', - 'content' => '', - 'cleanContent' => '', - 'embed' => '' - ]; - - $nextEndPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_END_TOKEN); - $currentPosition = $this->calculateCurrentPosition($nextStartPosition); - $nextStartPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_START_TOKEN); - - $nextIdentifierSeparatorPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_SEPARATOR_TOKEN); - $nextSecondIdentifierSeparatorPosition = $this->calculateNextTokenPosition($nextIdentifierSeparatorPosition + 1, ContentCache::CACHE_SEGMENT_SEPARATOR_TOKEN); - - if ($nextIdentifierSeparatorPosition === false || $nextSecondIdentifierSeparatorPosition === false - || $nextStartPosition !== false && $nextStartPosition < $nextSecondIdentifierSeparatorPosition - || $nextEndPosition !== false && $nextEndPosition < $nextSecondIdentifierSeparatorPosition - ) { - throw new Exception(sprintf('Missing segment separator token after position %d', $currentPosition), 1391855139); - } - - $identifier = $this->extractContent($currentPosition, $nextIdentifierSeparatorPosition); - $contextOrMetadata = $this->extractContent($this->calculateCurrentPosition($nextIdentifierSeparatorPosition), $nextSecondIdentifierSeparatorPosition); - - $segmentData['identifier'] = $identifier; - $segmentData['type'] = ContentCache::SEGMENT_TYPE_CACHED; - $segmentData['metadata'] = $contextOrMetadata; - $segmentData['context'] = $contextOrMetadata; - - if (strpos($identifier, 'eval=') === 0) { - $segmentData['type'] = ContentCache::SEGMENT_TYPE_UNCACHED; - unset($segmentData['metadata']); - $this->uncachedPartCount++; - } - - if (strpos($identifier, 'evalCached=') === 0) { - $segmentData['type'] = ContentCache::SEGMENT_TYPE_DYNAMICCACHED; - $segmentData['identifier'] = substr($identifier, 11); - $additionalData = json_decode($contextOrMetadata, true); - $segmentData['metadata'] = $additionalData['metadata']; - $this->uncachedPartCount++; - } - - $currentPosition = $this->calculateCurrentPosition($nextSecondIdentifierSeparatorPosition); - $segmentData = $this->extractContentAndSubSegments($currentPosition, $segmentData); - - if ($segmentData['type'] === ContentCache::SEGMENT_TYPE_CACHED || $segmentData['type'] === ContentCache::SEGMENT_TYPE_DYNAMICCACHED) { - $this->cacheSegments[$identifier] = $this->reduceSegmentDataToCacheRelevantInformation($segmentData); - } - - return $segmentData; - } - - /** - * @param integer $currentPosition - * @param array $segmentData - * @return array - */ - protected function extractContentAndSubSegments($currentPosition, array $segmentData) - { - $nextStartPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_START_TOKEN); - $nextEndPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_END_TOKEN); - - $segmentData['content'] = ''; - $segmentData['cleanContent'] = ''; - while ($nextStartPosition !== false && $nextStartPosition < $nextEndPosition) { - $segmentContent = $this->extractContent($currentPosition, $nextStartPosition); - $segmentData['content'] .= $segmentContent; - $segmentData['cleanContent'] .= $segmentContent; - - $nextLevelData = $this->parseSegment($nextStartPosition); - $segmentData['content'] .= $nextLevelData['embed']; - $segmentData['cleanContent'] .= $nextLevelData['cleanContent']; - - $currentPosition = $this->calculateCurrentPosition($nextLevelData['endPosition']); - $nextStartPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_START_TOKEN); - $nextEndPosition = $this->calculateNextTokenPosition($currentPosition, ContentCache::CACHE_SEGMENT_END_TOKEN); - } - - /** @phpstan-ignore-next-line: $toPosition expects int|null but int|false given. Is this a real case? */ - $remainingContent = $this->extractContent($currentPosition, $nextEndPosition); - $segmentData['content'] .= $remainingContent; - $segmentData['cleanContent'] .= $remainingContent; - $segmentData['endPosition'] = $nextEndPosition; - - if ($segmentData['type'] === ContentCache::SEGMENT_TYPE_UNCACHED) { - $segmentData['embed'] = ContentCache::CACHE_SEGMENT_START_TOKEN . ContentCache::CACHE_SEGMENT_MARKER . $segmentData['identifier'] . ContentCache::CACHE_SEGMENT_SEPARATOR_TOKEN . ContentCache::CACHE_SEGMENT_MARKER . $segmentData['context'] . ContentCache::CACHE_SEGMENT_END_TOKEN . ContentCache::CACHE_SEGMENT_MARKER; - } elseif ($segmentData['type'] === ContentCache::SEGMENT_TYPE_DYNAMICCACHED) { - $segmentData['embed'] = ContentCache::CACHE_SEGMENT_START_TOKEN . ContentCache::CACHE_SEGMENT_MARKER . 'evalCached=' . $segmentData['identifier'] . ContentCache::CACHE_SEGMENT_SEPARATOR_TOKEN . ContentCache::CACHE_SEGMENT_MARKER . $segmentData['context'] . ContentCache::CACHE_SEGMENT_END_TOKEN . ContentCache::CACHE_SEGMENT_MARKER; - } else { - $segmentData['embed'] = ContentCache::CACHE_SEGMENT_START_TOKEN . ContentCache::CACHE_SEGMENT_MARKER . $segmentData['identifier'] . ContentCache::CACHE_SEGMENT_END_TOKEN . ContentCache::CACHE_SEGMENT_MARKER; - } - - return $segmentData; - } - - /** - * Make sure that we keep only necessary information for caching and strip all internal segment data. - * - * @param array $segmentData - * @return array - */ - protected function reduceSegmentDataToCacheRelevantInformation(array $segmentData) - { - return [ - 'identifier' => $segmentData['identifier'], - 'type' => $segmentData['type'], - 'content' => $segmentData['content'], - 'metadata' => $segmentData['metadata'] - ]; - } - - /** - * @param integer $fromPosition - * @param integer|null $toPosition - * @return string - */ - protected function extractContent($fromPosition, $toPosition = null) - { - // substr behaves differently if the third parameter is not given or if it's null, so we need to take this detour - if ($toPosition === null) { - return substr($this->content, $fromPosition); - } - - return substr($this->content, $fromPosition, ($toPosition - $fromPosition)); - } - - /** - * Calculates a position assuming that the given position is a token followed by the random cache marker - * - * @param int $position - * @return int - */ - protected function calculateCurrentPosition($position) - { - return $position + 1 + $this->randomCacheMarkerLength; - } - - /** - * Find the next position of the given token (one of the ContentCache::CACHE_SEGMENT_*_TOKEN constants) in the parsed content. - * - * @param integer $currentPosition The position to start searching from - * @param string $token the token to search for (will internally be appeneded by the randomCacheMarker) - * @return integer|false Position of the token or false if the token was not found - */ - protected function calculateNextTokenPosition($currentPosition, $token) - { - return strpos($this->content, $token . $this->randomCacheMarker, $currentPosition); - } - - /** - * Returns the fully intact content as originally given to extractRenderedSegments() but without the markers. This - * content is suitable for being used as output for the user. - * - * @return string - */ - public function getOutput() - { - return $this->output; - } - - /** - * Returns an array with extracted content segments, including the type (if they can be cached or not) and tags to - * be used for their entries when the segments are stored in a persistent cache. - * - * @return array - */ - public function getCacheSegments() - { - return $this->cacheSegments; - } - - /** - * @return integer - */ - public function getUncachedPartCount() - { - return $this->uncachedPartCount; - } - - /** - * @return string - */ - public function getOuterSegmentContent() - { - return $this->outerSegmentContent; - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/ContentCache.php b/src/Core/FusionProcessing/Fusion/Cache/ContentCache.php deleted file mode 100644 index e11272f..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/ContentCache.php +++ /dev/null @@ -1,384 +0,0 @@ -[a-f0-9]+)\x03CONTENT_CACHE/"; - public const EVAL_PLACEHOLDER_REGEX = "/\x02CONTENT_CACHE(?P[^\x02\x1f\x03]+)\x1fCONTENT_CACHE(?P[^\x02\x1f\x03]+)\x03CONTENT_CACHE/"; - - public const MAXIMUM_NESTING_LEVEL = 32; - - /** - * A cache entry tag that will be used by default to flush an entry on "every" change - whatever that means to - * the application. - */ - public const TAG_EVERYTHING = 'Everything'; - - public const SEGMENT_TYPE_CACHED = 'cached'; - public const SEGMENT_TYPE_UNCACHED = 'uncached'; - public const SEGMENT_TYPE_DYNAMICCACHED = 'dynamiccached'; - - /** - * @var StringFrontend - * @Flow\Inject - */ - protected $cache; - - /** - * @var Context - * @Flow\Inject - */ - protected $securityContext; - - /** - * @var string - */ - protected $randomCacheMarker; - - /** - * ContentCache constructor - */ - public function __construct() - { - $this->randomCacheMarker = Algorithms::generateRandomString(13); - } - - /** - * Takes the given content and adds markers for later use as a cached content segment. - * - * This function will add a start and an end token to the beginning and end of the content and generate a cache - * identifier based on the current Fusion path and additional values which were defined in the Fusion - * configuration by the site integrator. - * - * The whole cache segment (START TOKEN + IDENTIFIER + SEPARATOR TOKEN + original content + END TOKEN) is returned - * as a string. - * - * This method is called by the Fusion Runtime while rendering a Fusion object. - * - * @param string $content The (partial) content which should potentially be cached later on - * @param string $fusionPath The Fusion path that rendered the content, for example "page/body/parts/breadcrumbMenu" - * @param array $cacheIdentifierValues The values (simple type or implementing CacheAwareInterface) that should be used to create a cache identifier, will be sorted by keys for consistent ordering - * @param array $tags Tags to add to the cache entry - * @param integer $lifetime Lifetime of the cache segment in seconds. NULL for the default lifetime and 0 for unlimited lifetime. - * @return string The original content, but with additional markers and a cache identifier added - */ - public function createCacheSegment($content, $fusionPath, array $cacheIdentifierValues, array $tags = [], $lifetime = null) - { - $cacheIdentifier = $this->renderContentCacheEntryIdentifier($fusionPath, $cacheIdentifierValues); - $metadata = implode(',', $tags); - if ($lifetime !== null) { - $metadata .= ';' . $lifetime; - } - return self::CACHE_SEGMENT_START_TOKEN . $this->randomCacheMarker . $cacheIdentifier . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . $metadata . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . $content . self::CACHE_SEGMENT_END_TOKEN . $this->randomCacheMarker; - } - - /** - * Similar to createCacheSegment() creates a content segment with markers added, but in contrast to that function - * this method is used for rendering a segment which is not supposed to be cached. - * - * This method is called by the Fusion Runtime while rendering a Fusion object. - * - * @param string $content The content rendered by the Fusion Runtime - * @param string $fusionPath The Fusion path that rendered the content, for example "page/body/parts/breadcrumbMenu" - * @param array $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object - * @return string The original content, but with additional markers added - */ - public function createUncachedSegment($content, $fusionPath, array $serializedContext) - { - return self::CACHE_SEGMENT_START_TOKEN . $this->randomCacheMarker . 'eval=' . $fusionPath . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . json_encode(['context' => $serializedContext]) . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . $content . self::CACHE_SEGMENT_END_TOKEN . $this->randomCacheMarker; - } - - /** - * Similar to createUncachedSegment() creates a content segment with markers added, but in contrast to that function - * this method is used for rendering a segment which will be evaluated at runtime but can still be cached. - * - * This method is called by the Fusion Runtime while rendering a Fusion object. - * - * @param string $content The content rendered by the Fusion Runtime - * @param string $fusionPath The Fusion path that rendered the content, for example "page/body/parts/breadcrumbMenu" - * @param array $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object - * @param array $cacheIdentifierValues - * @param array $tags Tags to add to the cache entry - * @param integer $lifetime Lifetime of the cache segment in seconds. NULL for the default lifetime and 0 for unlimited lifetime. - * @param string $cacheDiscriminator The evaluated cache discriminator value - * @return string The original content, but with additional markers added - */ - public function createDynamicCachedSegment($content, $fusionPath, array $serializedContext, array $cacheIdentifierValues, array $tags, $lifetime, $cacheDiscriminator) - { - $metadata = implode(',', $tags); - if ($lifetime !== null) { - $metadata .= ';' . $lifetime; - } - $cacheDiscriminator = md5($cacheDiscriminator); - $identifier = $this->renderContentCacheEntryIdentifier($fusionPath, $cacheIdentifierValues) . '_' . $cacheDiscriminator; - $segmentData = [ - 'path' => $fusionPath, - 'metadata' => $metadata, - 'context' => $serializedContext, - ]; - - return self::CACHE_SEGMENT_START_TOKEN . $this->randomCacheMarker . 'evalCached=' . $identifier . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . json_encode($segmentData) . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . $content . self::CACHE_SEGMENT_END_TOKEN . $this->randomCacheMarker; - } - - /** - * Renders an identifier for a content cache entry - * - * @param string $fusionPath - * @param array $cacheIdentifierValues - * @return string An MD5 hash built from the fusionPath and certain elements of the given identifier values - * @throws CacheException If an invalid entry identifier value is given - */ - protected function renderContentCacheEntryIdentifier($fusionPath, array $cacheIdentifierValues) - { - ksort($cacheIdentifierValues); - - $identifierSource = ''; - foreach ($cacheIdentifierValues as $key => $value) { - if ($value instanceof CacheAwareInterface) { - $identifierSource .= $key . '=' . $value->getCacheEntryIdentifier() . '&'; - } elseif (is_string($value) || is_bool($value) || is_integer($value)) { - $identifierSource .= $key . '=' . $value . '&'; - } elseif ($value !== null) { - throw new CacheException(sprintf('Invalid cache entry identifier @cache.entryIdentifier.%s for path "%s". A entry identifier value must be a string or implement CacheAwareInterface.', $key, $fusionPath), 1395846615); - } - } - $identifierSource .= 'securityContextHash=' . $this->securityContext->getContextHash(); - - return md5($fusionPath . '@' . $identifierSource); - } - - /** - * Takes a string of content which includes cache segment markers, extracts the marked segments, writes those - * segments which can be cached to the actual cache and returns the cleaned up original content without markers. - * - * This method is called by the Fusion Runtime while rendering a Fusion object. - * - * @param string $content The content with an outer cache segment - * @param boolean $storeCacheEntries Whether to store extracted cache segments in the cache - * @return string The (pure) content without cache segment markers - */ - public function processCacheSegments($content, $storeCacheEntries = true) - { - $parser = new CacheSegmentParser($content, $this->randomCacheMarker); - - if ($storeCacheEntries) { - $segments = $parser->getCacheSegments(); - - foreach ($segments as $segment) { - $metadata = explode(';', $segment['metadata']); - $tagsValue = $metadata[0] === '' ? [] : ($metadata[0] === '*' ? false : explode(',', $metadata[0])); - // false means we do not need to store the cache entry again (because it was previously fetched) - if ($tagsValue !== false) { - $lifetime = isset($metadata[1]) ? (integer)$metadata[1] : null; - $this->cache->set($segment['identifier'], $segment['content'], $this->sanitizeTags($tagsValue), $lifetime); - } - } - } - - return $parser->getOutput(); - } - - /** - * Tries to retrieve the specified content segment from the cache – further nested inline segments are retrieved - * as well and segments which were not cacheable are rendered. - * - * @param \Closure $uncachedCommandCallback A callback to process commands in uncached segments - * @param string $fusionPath Fusion path identifying the Fusion object to retrieve from the content cache - * @param array $cacheIdentifierValues Further values which play into the cache identifier hash, must be the same as the ones specified while the cache entry was written - * @param boolean $addCacheSegmentMarkersToPlaceholders If cache segment markers should be added – this makes sense if the cached segment is about to be included in a not-yet-cached segment - * @param string|false $cacheDiscriminator The evaluated cache discriminator value, if any and false if the cache discriminator is disabled for the current context - * @return string|false The segment with replaced cache placeholders, or false if a segment was missing in the cache - * @throws Exception - */ - public function getCachedSegment($uncachedCommandCallback, $fusionPath, $cacheIdentifierValues, $addCacheSegmentMarkersToPlaceholders = false, $cacheDiscriminator = null) - { - // If $addCacheSegmentMarkersToPlaceholders was set, the outer segment was a cache miss and we need to re-evaluate dynamic cached segments. - if ($cacheDiscriminator === false || ($addCacheSegmentMarkersToPlaceholders && $cacheDiscriminator !== null)) { - return false; - } - $cacheIdentifier = $this->renderContentCacheEntryIdentifier($fusionPath, $cacheIdentifierValues); - if ($cacheDiscriminator !== null) { - $cacheIdentifier .= '_' . md5($cacheDiscriminator); - } - $content = $this->cache->get($cacheIdentifier); - - if ($content === false) { - return false; - } - - $i = 0; - do { - $replaced = $this->replaceCachePlaceholders($content, $addCacheSegmentMarkersToPlaceholders); - if ($replaced === false) { - return false; - } - $replaced += $this->replaceUncachedPlaceholders($uncachedCommandCallback, $content); - if ($i > self::MAXIMUM_NESTING_LEVEL) { - throw new Exception('Maximum cache segment level reached', 1391873620); - } - $i++; - } while ($replaced > 0); - - if ($addCacheSegmentMarkersToPlaceholders) { - return self::CACHE_SEGMENT_START_TOKEN . $this->randomCacheMarker . $cacheIdentifier . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . '*' . self::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . $content . self::CACHE_SEGMENT_END_TOKEN . $this->randomCacheMarker; - } else { - return $content; - } - } - - /** - * Find cache placeholders in a cached segment and return the identifiers - * - * @param string $content - * @param boolean $addCacheSegmentMarkersToPlaceholders - * @return integer|boolean Number of replaced placeholders or false if a placeholder couldn't be found - */ - protected function replaceCachePlaceholders(&$content, $addCacheSegmentMarkersToPlaceholders) - { - $cache = $this->cache; - $foundMissingIdentifier = false; - $content = preg_replace_callback(self::CACHE_PLACEHOLDER_REGEX, function ($match) use ($cache, &$foundMissingIdentifier, $addCacheSegmentMarkersToPlaceholders) { - $identifier = $match['identifier']; - $entry = $cache->get($identifier); - if ($entry !== false) { - if ($addCacheSegmentMarkersToPlaceholders) { - return ContentCache::CACHE_SEGMENT_START_TOKEN . $this->randomCacheMarker . $identifier . ContentCache::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . '*' . ContentCache::CACHE_SEGMENT_SEPARATOR_TOKEN . $this->randomCacheMarker . $entry . ContentCache::CACHE_SEGMENT_END_TOKEN . $this->randomCacheMarker; - } else { - return $entry; - } - } else { - $foundMissingIdentifier = true; - return ''; - } - }, $content, -1, $count); - if ($foundMissingIdentifier) { - return false; - } - return $count; - } - - /** - * Replace segments which are marked as not-cacheable by their actual content by invoking the Fusion Runtime. - * - * @param \Closure $uncachedCommandCallback - * @param string $content The content potentially containing not cacheable segments marked by the respective tokens - * @return integer Number of replaced placeholders - */ - protected function replaceUncachedPlaceholders(\Closure $uncachedCommandCallback, &$content) - { - $cache = $this->cache; - $content = preg_replace_callback(self::EVAL_PLACEHOLDER_REGEX, function ($match) use ($uncachedCommandCallback, $cache) { - $command = $match['command']; - $additionalData = json_decode($match['data'], true); - - return $uncachedCommandCallback($command, $additionalData, $cache); - }, $content, -1, $count); - return $count; - } - - /** - * Flush content cache entries by tag - * - * @param string $tag A tag value that was assigned to a cache entry in Fusion, for example "Everything", "Node_[…]", "NodeType_[…]", "DescendantOf_[…]" whereas "…" is the node identifier or node type respectively - * @return integer The number of cache entries which actually have been flushed - */ - public function flushByTag($tag) - { - return $this->cache->flushByTag($this->sanitizeTag($tag)); - } - - /** - * Flush content cache entries by tags - * - * @param array $tags values that were assigned to a cache entry in Fusion, for example "Everything", "Node_[…]", "NodeType_[…]", "DescendantOf_[…]" whereas "…" is the node identifier or node type respectively - * @return integer The number of cache entries which actually have been flushed - */ - public function flushByTags(array $tags): int - { - return $this->cache->flushByTags($this->sanitizeTags($tags)); - } - - /** - * Flush all content cache entries - * - * @return void - */ - public function flush() - { - $this->cache->flush(); - } - - /** - * Sanitizes the given tag for use with the cache framework - * - * @param string $tag A tag which possibly contains non-allowed characters, for example "NodeType_Acme.Com:Page" - * @return string A cleaned up tag, for example "NodeType_Acme_Com-Page" - */ - protected function sanitizeTag($tag) - { - return strtr($tag, '.:', '_-'); - } - - /** - * Sanitizes multiple tags with sanitizeTag() - * - * @param array $tags Multiple tags - * @return array The sanitized tags - */ - protected function sanitizeTags(array $tags) - { - foreach ($tags as $key => $value) { - $tags[$key] = $this->sanitizeTag($value); - } - return $tags; - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/FileMonitorListener.php b/src/Core/FusionProcessing/Fusion/Cache/FileMonitorListener.php deleted file mode 100644 index 6107fa3..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/FileMonitorListener.php +++ /dev/null @@ -1,62 +0,0 @@ -flowCacheManager = $flowCacheManager; - } - - /** - * @param string $fileMonitorIdentifier - * @param array $changedFiles - * @return void - */ - public function flushContentCacheOnFileChanges($fileMonitorIdentifier, array $changedFiles) - { - $fileMonitorsThatTriggerContentCacheFlush = [ - 'ContentRepository_NodeTypesConfiguration', - 'Fusion_Files', - 'Fluid_TemplateFiles', - 'Flow_ClassFiles', - 'Flow_ConfigurationFiles', - 'Flow_TranslationFiles' - ]; - - if (in_array($fileMonitorIdentifier, $fileMonitorsThatTriggerContentCacheFlush)) { - $this->flowCacheManager->getCache('Neos_Fusion_Content')->flush(); - } - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/FusionContextSerializer.php b/src/Core/FusionProcessing/Fusion/Cache/FusionContextSerializer.php deleted file mode 100644 index a0a35e7..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/FusionContextSerializer.php +++ /dev/null @@ -1,55 +0,0 @@ - $context - */ - public function denormalize(mixed $data, string $type, string $format = null, array $context = []) - { - return $this->propertyMapper->convert($data, $type); - } - - /** - * @param array $context - * @return array - */ - public function normalize(mixed $object, string $format = null, array $context = []) - { - return $this->propertyMapper->convert($object, 'string'); - } - - public function supportsDenormalization(mixed $data, string $type, string $format = null) - { - return true; - } - - public function supportsNormalization(mixed $data, string $format = null) - { - return true; - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/ParserCache.php b/src/Core/FusionProcessing/Fusion/Cache/ParserCache.php deleted file mode 100644 index 5102018..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/ParserCache.php +++ /dev/null @@ -1,148 +0,0 @@ -enableCache === false) { - return $generateValueToCache(); - } - if ($contextPathAndFilename === null) { - return $generateValueToCache(); - } - if (str_contains($contextPathAndFilename, 'resource://')) { - $contextPathAndFilename = $this->getAbsolutePathForPackageRessourceUri($contextPathAndFilename); - } - if (str_contains($contextPathAndFilename, 'nodetypes://')) { - $contextPathAndFilename = $this->getAbsolutePathForNodeTypesUri($contextPathAndFilename); - } - $fusionFileRealPath = realpath($contextPathAndFilename); - if ($fusionFileRealPath === false) { - // should not happen as the file would not been able to be read in the first place. - throw new \RuntimeException("Couldn't resolve realpath for: '$contextPathAndFilename'", 1705409467); - } - $identifier = $this->getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal($fusionFileRealPath); - return $this->cacheForIdentifier($identifier, $generateValueToCache); - } - - public function cacheForDsl(string $identifier, string $code, \Closure $generateValueToCache): mixed - { - if ($this->enableCache === false) { - return $generateValueToCache(); - } - $identifier = $this->getCacheIdentifierForDslCode($identifier, $code); - return $this->cacheForIdentifier($identifier, $generateValueToCache); - } - - private function cacheForIdentifier(string $identifier, \Closure $generateValueToCache): mixed - { - $value = $this->parsePartialsCache->get($identifier); - if ($value !== false) { - return $value; - } - $value = $generateValueToCache(); - if ($value !== false) { - // in the rare edge-case of a fusion dsl returning `false` we cannot cache it, - // as the above get would be ignored. This is an acceptable compromise. - $this->parsePartialsCache->set($identifier, $value); - } - return $value; - } - - /** - * Uses the same technique to resolve a package resource URI like Flow. - * - * resource://My.Site/Private/Fusion/Foo/Bar.fusion - * -> - * FLOW_PATH_ROOT/Packages/Sites/My.Package/Resources/Private/Fusion/Foo/Bar.fusion - * - * {@see \Neos\Flow\ResourceManagement\Streams\ResourceStreamWrapper::evaluateResourcePath()} - * {@link https://github.com/neos/flow-development-collection/issues/2687} - * - * @throws \InvalidArgumentException - */ - private function getAbsolutePathForPackageRessourceUri(string $requestedPath): string - { - $resourceUriParts = UnicodeFunctions::parse_url($requestedPath); - - if ((isset($resourceUriParts['scheme']) === false - || $resourceUriParts['scheme'] !== 'resource')) { - throw new \InvalidArgumentException("Unsupported stream wrapper: '$requestedPath'"); - } - - /** @var FlowPackageInterface $package */ - $package = $this->packageManager->getPackage($resourceUriParts['host']); - return Files::concatenatePaths([$package->getResourcesPath(), $resourceUriParts['path']]); - } - - /** - * Uses the same technique to resolve a package nodetypes URI like Neos. - * - * nodetypes://My.Site/Foo/Bar.fusion - * -> - * FLOW_PATH_ROOT/Packages/Sites/My.Package/NodeTypes/Foo/Bar.fusion - * - * {@see \Neos\Neos\ResourceManagement\NodeTypesStreamWrapper::evaluateNodeTypesPath} - * - * @throws \InvalidArgumentException - */ - private function getAbsolutePathForNodeTypesUri(string $requestedPath): string - { - $nodeTypeUriParts = UnicodeFunctions::parse_url($requestedPath); - - if ((isset($nodeTypeUriParts['scheme']) === false - || $nodeTypeUriParts['scheme'] !== 'nodetypes')) { - throw new \InvalidArgumentException("Unsupported stream wrapper: '$requestedPath'"); - } - - $package = $this->packageManager->getPackage($nodeTypeUriParts['host']); - return Files::concatenatePaths([$package->getPackagePath(), 'NodeTypes', $nodeTypeUriParts['path']]); - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/ParserCacheFlusher.php b/src/Core/FusionProcessing/Fusion/Cache/ParserCacheFlusher.php deleted file mode 100644 index 7a98478..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/ParserCacheFlusher.php +++ /dev/null @@ -1,80 +0,0 @@ -flowCacheManager = $flowCacheManager; - } - - /** - * @param string $fileMonitorIdentifier - * @param array $changedFiles - * @return void - */ - public function flushPartialCacheOnFileChanges($fileMonitorIdentifier, array $changedFiles) - { - if ($fileMonitorIdentifier !== 'Fusion_Files') { - return; - } - - $identifiersToFlush = []; - foreach ($changedFiles as $changedFile => $status) { - // flow returns linux style file paths without directory traversal from the file monitor. - // As discovered via https://github.com/neos/neos-development-collection/issues/4915 the paths will point to symlinks instead of the actual file. - // Thus, we still need to invoke `realpath` as the cache invalidation otherwise would not work (due to a different hash) - // But attempting to use realpath on removed/moved files fails because it surely cannot be resolved via file system. - if ($status === ChangeDetectionStrategyInterface::STATUS_DELETED) { - // Ignoring removed files means we cannot flush removed files, but this is a compromise for now. - // See https://github.com/neos/neos-development-collection/issues/4415 as reminder that flushing is disabled for deleted files - continue; - } - $fusionFileRealPath = realpath($changedFile); - if ($fusionFileRealPath === false) { - // should not happen as we ignored deleted files beforehand. - throw new \RuntimeException("Couldn't resolve realpath for: '$changedFile'", 1709122619); - } - $identifiersToFlush[] = $this->getCacheIdentifierForAbsoluteUnixStyleFilePathWithoutDirectoryTraversal($fusionFileRealPath); - } - - if ($identifiersToFlush !== []) { - $partialsCache = $this->flowCacheManager->getCache('Neos_Fusion_ParsePartials'); - foreach ($identifiersToFlush as $identifierToFlush) { - if ($partialsCache->has($identifierToFlush)) { - $partialsCache->remove($identifierToFlush); - } - } - } - } -} diff --git a/src/Core/FusionProcessing/Fusion/Cache/ParserCacheIdentifierTrait.php b/src/Core/FusionProcessing/Fusion/Cache/ParserCacheIdentifierTrait.php deleted file mode 100644 index e9b564c..0000000 --- a/src/Core/FusionProcessing/Fusion/Cache/ParserCacheIdentifierTrait.php +++ /dev/null @@ -1,51 +0,0 @@ -runtime = $runtime; - } - - public function injectSerializer(NormalizerInterface&DenormalizerInterface $serializer): void - { - $this->serializer = $serializer; - } - - /** - * Adds a tag built from the given key and value. - * - * @throws Exception - */ - public function addTag(string $tag): void - { - $tag = trim($tag); - if ($tag === '') { - throw new Exception('Tag Value must not be empty', 1448264367); - } - $this->tags[$tag] = true; - } - - /** - * Resets the assigned tags, returning the previously set tags. - * - * @return array - */ - protected function flushTags() - { - $tags = array_keys($this->tags); - $this->tags = []; - return $tags; - } - - /** - * Enter an evaluation - * - * Needs to be called right before evaluation of a path starts to check the cache mode and set internal state - * like the cache entry point. - * - * @param array $configuration - * @param string $fusionPath - * @return array An evaluate context array that needs to be passed to subsequent calls to pass the current state - * @throws Exception - */ - public function enter(array $configuration, $fusionPath) - { - $cacheForPathEnabled = isset($configuration['mode']) && ($configuration['mode'] === 'cached' || $configuration['mode'] === 'dynamic'); - $cacheForPathDisabled = isset($configuration['mode']) && ($configuration['mode'] === 'uncached' || $configuration['mode'] === 'dynamic'); - - if ($cacheForPathDisabled && (!isset($configuration['context']) || $configuration['context'] === [])) { - throw new Exception(sprintf('Missing @cache.context configuration for path "%s". An uncached segment must have one or more context variable names configured.', $fusionPath), 1395922119); - } - - $currentPathIsEntryPoint = false; - if ($this->enableContentCache && $cacheForPathEnabled) { - if ($this->inCacheEntryPoint === null) { - $this->inCacheEntryPoint = true; - $currentPathIsEntryPoint = true; - } - } - - return [ - 'configuration' => $configuration, - 'fusionPath' => $fusionPath, - 'cacheForPathEnabled' => $cacheForPathEnabled, - 'cacheForPathDisabled' => $cacheForPathDisabled, - 'currentPathIsEntryPoint' => $currentPathIsEntryPoint - ]; - } - - /** - * Check for cached evaluation and or collect metadata for evaluation - * - * Try to get a cached segment for the current path and return that with all uncached segments evaluated if it - * exists. Otherwise metadata for the cache lifetime is collected (if configured) for nested evaluations (to find the - * minimum maximumLifetime). - * - * @param array $evaluateContext The current evaluation context - * @param object $fusionObject The current Fusion object (for "this" in evaluations) - * @return array Cache hit state as boolean and value as mixed - */ - public function preEvaluate(array &$evaluateContext, $fusionObject) - { - if ($this->enableContentCache) { - if ($evaluateContext['cacheForPathEnabled'] && $evaluateContext['cacheForPathDisabled']) { - $evaluateContext['cacheDiscriminator'] = $this->runtime->evaluate($evaluateContext['fusionPath'] . '/__meta/cache/entryDiscriminator'); - } - if ($evaluateContext['cacheForPathEnabled']) { - $evaluateContext['cacheIdentifierValues'] = $this->buildCacheIdentifierValues($evaluateContext['configuration'], $evaluateContext['fusionPath'], $fusionObject); - $cacheDiscriminator = isset($evaluateContext['cacheDiscriminator']) ? $evaluateContext['cacheDiscriminator'] : null; - $segment = $this->contentCache->getCachedSegment(function ($command, $additionalData, $cache) { - if (strpos($command, 'eval=') === 0) { - $unserializedContext = $this->unserializeContext($additionalData['context']); - $path = substr($command, 5); - $result = $this->evaluateUncached($path, $unserializedContext); - return $result; - } elseif (strpos($command, 'evalCached=') === 0) { - /* - * Why do we need the following line: - * - in "enter" the cache context is decided upon which contains "currentPathIsEntryPoint". - * - This can not happen in nested segments as the topmost entry point should be the only one active - * - the result of a "currentPathIsEntryPoint" is that on postProcess cache segments are parsed from the content. - * - To get "currentPathIsEntryPoint" only on topmost segments, the state "$this->inCacheEntryPoint" is used. - * This state can have two values "true" and "null", in case it's true a topmost segment existed and "currentPathIsEntryPoint" will not be set - * - A dynamic cache segment that we resolve here is to be seen independently from the parent cached entry as it is a forking point for content - * It must create cache segment tokens in order to properly cache, but those also need to be removed from the result. - * Therefore a dynamic cache entry must always have "currentPathIsEntryPoint" to make sure the markers are parsed regardless of the caching status of the upper levels - * To make that happen the state "$this->inCacheEntryPoint" must be reset to null. - */ - $previouslyInCacheEntryPoint = $this->inCacheEntryPoint; - $this->inCacheEntryPoint = null; - - $unserializedContext = $this->unserializeContext($additionalData['context']); - $this->runtime->pushContextArray($unserializedContext); - $result = $this->runtime->evaluate($additionalData['path']); - $this->runtime->popContext(); - $this->inCacheEntryPoint = $previouslyInCacheEntryPoint; - return $result; - } else { - throw new Exception(sprintf('Unknown uncached command "%s"', $command), 1392837596); - } - }, $evaluateContext['fusionPath'], $evaluateContext['cacheIdentifierValues'], $this->addCacheSegmentMarkersToPlaceholders, $cacheDiscriminator); - if ($segment !== false) { - return [true, $segment]; - } else { - $this->addCacheSegmentMarkersToPlaceholders = true; - } - - $this->cacheMetadata[] = ['lifetime' => null]; - } - - - if (isset($evaluateContext['configuration']['maximumLifetime'])) { - $maximumLifetime = $this->runtime->evaluate($evaluateContext['fusionPath'] . '/__meta/cache/maximumLifetime', $fusionObject); - - if ($maximumLifetime !== null && $this->cacheMetadata !== []) { - $parentCacheMetadata = &$this->cacheMetadata[count($this->cacheMetadata) - 1]; - - if ($parentCacheMetadata['lifetime'] === null) { - $parentCacheMetadata['lifetime'] = (int)$maximumLifetime; - } elseif ($maximumLifetime > 0) { - $parentCacheMetadata['lifetime'] = min((int)$parentCacheMetadata['lifetime'], (int)$maximumLifetime); - } - } - } - } - return [false, null]; - } - - /** - * Post process output for caching information - * - * The content cache stores cache segments with markers inside the generated content. This method creates cache - * segments and will process the final outer result (currentPathIsEntryPoint) to remove all cache markers and - * store cache entries. - * - * @param array $evaluateContext The current evaluation context - * @param object $fusionObject The current Fusion object (for "this" in evaluations) - * @param mixed $output The generated output after caching information was removed - * @return mixed The post-processed output with cache segment markers or cleaned for the entry point - */ - public function postProcess(array $evaluateContext, $fusionObject, $output) - { - if ($this->enableContentCache && $evaluateContext['cacheForPathEnabled'] && $evaluateContext['cacheForPathDisabled']) { - $contextArray = $this->runtime->getCurrentContext(); - if (isset($evaluateContext['configuration']['context'])) { - $contextVariables = []; - foreach ($evaluateContext['configuration']['context'] as $contextVariableName) { - $contextVariables[$contextVariableName] = $contextArray[$contextVariableName]; - } - } else { - $contextVariables = $contextArray; - } - $cacheTags = $this->buildCacheTags($evaluateContext['configuration'], $evaluateContext['fusionPath'], $fusionObject); - $cacheMetadata = array_pop($this->cacheMetadata); - $output = $this->contentCache->createDynamicCachedSegment($output, $evaluateContext['fusionPath'], $this->serializeContext($contextVariables), $evaluateContext['cacheIdentifierValues'], $cacheTags, $cacheMetadata['lifetime'], (string)$evaluateContext['cacheDiscriminator']); - } elseif ($this->enableContentCache && $evaluateContext['cacheForPathEnabled']) { - $cacheTags = $this->buildCacheTags($evaluateContext['configuration'], $evaluateContext['fusionPath'], $fusionObject); - $cacheMetadata = array_pop($this->cacheMetadata); - $output = $this->contentCache->createCacheSegment($output, $evaluateContext['fusionPath'], $evaluateContext['cacheIdentifierValues'], $cacheTags, $cacheMetadata['lifetime']); - } elseif ($this->enableContentCache && $evaluateContext['cacheForPathDisabled'] && $this->inCacheEntryPoint) { - $contextArray = $this->runtime->getCurrentContext(); - if (isset($evaluateContext['configuration']['context'])) { - $contextVariables = []; - foreach ($evaluateContext['configuration']['context'] as $contextVariableName) { - if (isset($contextArray[$contextVariableName])) { - $contextVariables[$contextVariableName] = $contextArray[$contextVariableName]; - } else { - $contextVariables[$contextVariableName] = null; - } - } - } else { - $contextVariables = $contextArray; - } - $output = $this->contentCache->createUncachedSegment($output, $evaluateContext['fusionPath'], $this->serializeContext($contextVariables)); - } - - if ($evaluateContext['cacheForPathEnabled'] && $evaluateContext['currentPathIsEntryPoint']) { - $output = $this->contentCache->processCacheSegments($output, $this->enableContentCache); - $this->inCacheEntryPoint = null; - $this->addCacheSegmentMarkersToPlaceholders = false; - } - - return $output; - } - - /** - * Leave the evaluation of a path - * - * Has to be called in the same function calling enter() for every return path. - * - * @param array $evaluateContext The current evaluation context - * @return void - */ - public function leave(array $evaluateContext) - { - if ($evaluateContext['currentPathIsEntryPoint']) { - $this->inCacheEntryPoint = null; - } - } - - /** - * Evaluate a Fusion path with a given context without content caching - * - * This is used to render uncached segments "out of band" in getCachedSegment of ContentCache. - * - * @param string $path - * @param array $contextArray - * @return mixed - * - * TODO Find another way of disabling the cache (especially to allow cached content inside uncached content) - */ - public function evaluateUncached($path, array $contextArray) - { - $previousEnableContentCache = $this->enableContentCache; - $this->enableContentCache = false; - $this->runtime->pushContextArray($contextArray); - $result = $this->runtime->evaluate($path); - $this->runtime->popContext(); - $this->enableContentCache = $previousEnableContentCache; - return $result; - } - - /** - * Builds an array of additional key / values which must go into the calculation of the cache entry identifier for - * a cached content segment. - * - * @param array $configuration - * @param string $fusionPath - * @param object $fusionObject The actual Fusion object - * @return array - */ - protected function buildCacheIdentifierValues(array $configuration, $fusionPath, $fusionObject) - { - $objectType = ''; - if (isset($configuration['entryIdentifier']['__objectType'])) { - $objectType = '<' . $configuration['entryIdentifier']['__objectType'] . '>'; - } - return $this->runtime->evaluate($fusionPath . '/__meta/cache/entryIdentifier' . $objectType, $fusionObject); - } - - /** - * Builds an array of string which must be used as tags for the cache entry identifier of a specific cached content segment. - * - * @param array $configuration - * @param string $fusionPath - * @param object $fusionObject The actual Fusion object - * @return array - */ - protected function buildCacheTags(array $configuration, $fusionPath, $fusionObject) - { - $cacheTags = []; - if (isset($configuration['entryTags'])) { - foreach ($configuration['entryTags'] as $tagKey => $tagValue) { - $tagValue = $this->runtime->evaluate($fusionPath . '/__meta/cache/entryTags/' . $tagKey, $fusionObject); - if (is_array($tagValue)) { - $cacheTags = array_merge($cacheTags, $tagValue); - } elseif ((string)$tagValue !== '') { - $cacheTags[] = $tagValue; - } - } - foreach ($this->flushTags() as $tagKey => $tagValue) { - $cacheTags[] = $tagValue; - } - } else { - $cacheTags = [ContentCache::TAG_EVERYTHING]; - } - return array_unique($cacheTags); - } - - /** - * Encodes an array of context variables to its serialized representation - * {@see self::unserializeContext()} - * - * @param array $contextVariables - * @return array - */ - protected function serializeContext(array $contextVariables): array - { - $serializedContextArray = []; - foreach ($contextVariables as $variableName => $contextValue) { - if ($contextValue !== null) { - $serializedContextArray[$variableName]['type'] = TypeHandling::getTypeForValue($contextValue); - $serializedContextArray[$variableName]['value'] = $this->serializer->normalize($contextValue); - } - } - - return $serializedContextArray; - } - - /** - * Decodes and serialized array of context variables to its original values - * {@see self::serializeContext()} - * - * @param array $contextArray - * @return array - */ - protected function unserializeContext(array $contextArray): array - { - $unserializedContext = []; - foreach ($contextArray as $variableName => $typeAndValue) { - $value = $this->serializer->denormalize($typeAndValue['value'], $typeAndValue['type']); - $unserializedContext[$variableName] = $value; - } - - return $unserializedContext; - } - - /** - * @param boolean $enableContentCache - * @return void - */ - public function setEnableContentCache($enableContentCache) - { - $this->enableContentCache = $enableContentCache; - } - - /** - * @return boolean - */ - public function getEnableContentCache() - { - return $this->enableContentCache; - } -} diff --git a/src/Core/FusionProcessing/Fusion/DslFactory.php b/src/Core/FusionProcessing/Fusion/DslFactory.php deleted file mode 100644 index a270879..0000000 --- a/src/Core/FusionProcessing/Fusion/DslFactory.php +++ /dev/null @@ -1,53 +0,0 @@ ->|null - */ - protected $dslSettings; - - /** - * @param string $identifier - * @return DslInterface - * @throws Fusion\Exception - */ - public function create(string $identifier): DslInterface - { - if (is_array($this->dslSettings) && isset($this->dslSettings[$identifier])) { - $dslObjectName = $this->dslSettings[$identifier]; - if (!class_exists($dslObjectName)) { - throw new Fusion\Exception(sprintf('The fusion dsl-object %s was not found.', $dslObjectName), 1490776462); - } - $dslObject = new $dslObjectName(); - if (!$dslObject instanceof DslInterface) { - throw new Fusion\Exception(sprintf('The fusion dsl-object was of type %s but was supposed to be of type %s', get_class($dslObject), DslInterface::class), 1490776470); - } - return new $dslObject(); - } - throw new Fusion\Exception(sprintf('The fusion dsl-object for the key %s was not configured', $identifier), 1490776550); - } -} diff --git a/src/Core/FusionProcessing/Fusion/DslInterface.php b/src/Core/FusionProcessing/Fusion/DslInterface.php deleted file mode 100644 index 09037d9..0000000 --- a/src/Core/FusionProcessing/Fusion/DslInterface.php +++ /dev/null @@ -1,54 +0,0 @@ -Hello World - * ` - * The returned string must be a valid fusion string, - * which is parsed again: - * - * Neos.Fusion:Tag { - * tagName = 'div' - * content = 'Hello World' - * } - * - * @api - */ -interface DslInterface -{ - /** - * Transpile the given dsl-code to fusion-code - * - * @param string $code - * @return string - * @throws Fusion\Exception - */ - public function transpile(string $code): string; -} diff --git a/src/Core/FusionProcessing/Fusion/EelNeosDeprecationTracer.php b/src/Core/FusionProcessing/Fusion/EelNeosDeprecationTracer.php deleted file mode 100644 index 2231294..0000000 --- a/src/Core/FusionProcessing/Fusion/EelNeosDeprecationTracer.php +++ /dev/null @@ -1,96 +0,0 @@ - true, - 'accessroles' => true, - 'accessible' => true, - 'autocreated' => true, - 'cacheentryidentifier' => true, - 'childnodes' => true, - 'contentobject' => true, - 'context' => true, - 'contextpath' => true, - 'creationdatetime' => true, - 'depth' => true, - 'dimensions' => true, - 'hidden' => true, - 'hiddenafterdatetime' => true, - 'hiddenbeforedatetime' => true, - 'hiddeninindex' => true, - 'identifier' => true, - 'index' => true, - 'label' => true, - 'lastmodificationdatetime' => true, - 'lastpublicationdatetime' => true, - 'nodeaggregateidentifier' => true, - 'nodedata' => true, - 'nodename' => true, - 'nodetype' => true, - 'numberofchildnodes' => true, - 'othernodevariants' => true, - 'parent' => true, - 'parentpath' => true, - 'path' => true, - 'primarychildnode' => true, - 'propertynames' => true, - 'removed' => true, - 'root' => true, - 'tethered' => true, - 'visible' => true, - 'workspace' => true, - ]; - - public function __construct( - private readonly string $eelExpression, - private readonly bool $throwExceptions, - ) { - } - - public function recordPropertyAccess(object $object, string $propertyName): void - { - if ( - // deliberate cross package reference from Neos.Fusion to simplify the wiring of this temporary migration helper - $object instanceof Node - && array_key_exists(strtolower($propertyName), self::LEGACY_NODE_FIELDS) - ) { - $this->logDeprecationOrThrowException( - sprintf('The node field "%s" is removed in "%s"', $propertyName, $this->eelExpression) - ); - } - } - - public function recordMethodCall(object $object, string $methodName, array $arguments): void - { - } - - public function recordFunctionCall(callable $function, string $functionName, array $arguments): void - { - } - - private function logDeprecationOrThrowException(string $message): void - { - if ($this->throwExceptions) { - throw new \RuntimeException($message); - } else { - $this->logger->debug($message); - } - } -} diff --git a/src/Core/FusionProcessing/Fusion/ExceptionHandlers/AbsorbingHandler.php b/src/Core/FusionProcessing/Fusion/ExceptionHandlers/AbsorbingHandler.php deleted file mode 100644 index 641d7b0..0000000 --- a/src/Core/FusionProcessing/Fusion/ExceptionHandlers/AbsorbingHandler.php +++ /dev/null @@ -1,65 +0,0 @@ -systemLogger->debug('Absorbed Exception: ' . $exception->getMessage(), ['fusionPath' => $fusionPath, 'referenceCode' => $referenceCode, 'FLOW_LOG_ENVIRONMENT' => ['packageKey' => 'Neos.Fusion', 'className' => self::class, 'methodName' => 'handle']]); - $this->throwableStorage->logThrowable($exception); - return ''; - } - - /** - * The absorbing handler is meant to catch loose evaluation errors (like missing assets) in a useful way, - * therefor caching is desired. - * - * @param string $fusionPath - * @param \Exception $exception - * @return boolean - */ - protected function exceptionDisablesCache($fusionPath, \Exception $exception) - { - return false; - } -} diff --git a/src/Core/FusionProcessing/Fusion/ExceptionHandlers/AbstractRenderingExceptionHandler.php b/src/Core/FusionProcessing/Fusion/ExceptionHandlers/AbstractRenderingExceptionHandler.php deleted file mode 100644 index bcd12ad..0000000 --- a/src/Core/FusionProcessing/Fusion/ExceptionHandlers/AbstractRenderingExceptionHandler.php +++ /dev/null @@ -1,130 +0,0 @@ -runtime = $runtime; - } - - /** - * Returns current Fusion runtime - * - * @return Runtime - */ - protected function getRuntime() - { - return $this->runtime; - } - - /** - * Handle an Exception thrown while rendering Fusion - * - * @param string $fusionPath - * @param \Exception $exception - * @return string - * @throws StopActionException|SecurityException - */ - public function handleRenderingException($fusionPath, \Exception $exception) - { - if ($exception instanceof StopActionException || $exception instanceof SecurityException) { - throw $exception; - } - if ($exception instanceof Exceptions\RuntimeException) { - $fusionPath = $exception->getFusionPath(); - /** @var \Exception $exception */ - $exception = $exception->getPrevious(); - } - if ($this->exceptionDisablesCache($fusionPath, $exception)) { - $this->runtime->setEnableContentCache(false); - } - $referenceCode = ($exception instanceof \Neos\Flow\Exception) ? $exception->getReferenceCode() : null; - return $this->handle($fusionPath, $exception, $referenceCode); - } - - /** - * Handles an Exception thrown while rendering Fusion - * - * @param string $fusionPath path causing the exception - * @param \Exception $exception exception to handle - * @param string|null $referenceCode - * @return string - */ - abstract protected function handle($fusionPath, \Exception $exception, $referenceCode); - - /** - * breaks the given path to multiple line to allow a nicer formatted logging - * - * example: - * formatScriptPath('page/body