diff --git a/composer.json b/composer.json index 80c92b017..0017d1195 100644 --- a/composer.json +++ b/composer.json @@ -24,12 +24,12 @@ "nette/robot-loader": "^4.1", "phpecs/phpecs": "^2.2", "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^2.1.32", + "phpstan/phpstan": "^2.1.33", "phpstan/phpstan-webmozart-assert": "^2.0", - "phpunit/phpunit": "^12.4", + "phpunit/phpunit": "^12.5", "rector/jack": "^0.4", "rector/swiss-knife": "^2.3.3", - "tomasvotruba/class-leak": "^2.0.5" + "tomasvotruba/class-leak": "^2.1" }, "autoload": { "psr-4": { diff --git a/composer.lock b/composer.lock index ea1625aa5..5f1412b47 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b87d1cb1266b6be20605dd3ec29d8151", + "content-hash": "a967275501c77c1e7789c741c9c25676", "packages": [ { "name": "brick/math", @@ -633,31 +633,31 @@ }, { "name": "fruitcake/php-cors", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/fruitcake/php-cors.git", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6|^7" + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" }, "require-dev": { - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^2", "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -688,7 +688,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" }, "funding": [ { @@ -700,7 +700,7 @@ "type": "github" } ], - "time": "2023-10-12T05:21:21+00:00" + "time": "2025-12-03T09:33:47+00:00" }, { "name": "graham-campbell/result-type", @@ -1288,16 +1288,16 @@ }, { "name": "laravel/framework", - "version": "v12.40.2", + "version": "v12.41.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "1ccd99220b474500e672b373f32bd709ec38de50" + "reference": "3e229b05935fd0300c632fb1f718c73046d664fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/1ccd99220b474500e672b373f32bd709ec38de50", - "reference": "1ccd99220b474500e672b373f32bd709ec38de50", + "url": "https://api.github.com/repos/laravel/framework/zipball/3e229b05935fd0300c632fb1f718c73046d664fc", + "reference": "3e229b05935fd0300c632fb1f718c73046d664fc", "shasum": "" }, "require": { @@ -1503,7 +1503,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-11-26T19:24:25+00:00" + "time": "2025-12-03T01:02:13+00:00" }, { "name": "laravel/prompts", @@ -2186,16 +2186,16 @@ }, { "name": "livewire/livewire", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "f5f9efe6d5a7059116bd695a89d95ceedf33f3cb" + "reference": "214da8f3a1199a88b56ab2fe901d4a607f784805" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/f5f9efe6d5a7059116bd695a89d95ceedf33f3cb", - "reference": "f5f9efe6d5a7059116bd695a89d95ceedf33f3cb", + "url": "https://api.github.com/repos/livewire/livewire/zipball/214da8f3a1199a88b56ab2fe901d4a607f784805", + "reference": "214da8f3a1199a88b56ab2fe901d4a607f784805", "shasum": "" }, "require": { @@ -2250,7 +2250,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.7.0" + "source": "https://github.com/livewire/livewire/tree/v3.7.1" }, "funding": [ { @@ -2258,7 +2258,7 @@ "type": "github" } ], - "time": "2025-11-12T17:58:16+00:00" + "time": "2025-12-03T22:41:13+00:00" }, { "name": "monolog/monolog", @@ -2365,16 +2365,16 @@ }, { "name": "nesbot/carbon", - "version": "3.10.3", + "version": "3.11.0", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f" + "reference": "bdb375400dcd162624531666db4799b36b64e4a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", - "reference": "8e3643dcd149ae0fe1d2ff4f2c8e4bbfad7c165f", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", + "reference": "bdb375400dcd162624531666db4799b36b64e4a1", "shasum": "" }, "require": { @@ -2382,9 +2382,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3.12 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -2466,7 +2466,7 @@ "type": "tidelift" } ], - "time": "2025-09-06T13:39:36+00:00" + "time": "2025-12-02T21:04:28+00:00" }, { "name": "nette/schema", @@ -2535,20 +2535,20 @@ }, { "name": "nette/utils", - "version": "v4.0.9", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "505a30ad386daa5211f08a318e47015b501cad30" + "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/505a30ad386daa5211f08a318e47015b501cad30", - "reference": "505a30ad386daa5211f08a318e47015b501cad30", + "url": "https://api.github.com/repos/nette/utils/zipball/fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", + "reference": "fa1f0b8261ed150447979eb22e373b7b7ad5a8e0", "shasum": "" }, "require": { - "php": "8.0 - 8.5" + "php": "8.2 - 8.5" }, "conflict": { "nette/finder": "<3", @@ -2571,7 +2571,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -2618,9 +2618,9 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.9" + "source": "https://github.com/nette/utils/tree/v4.1.0" }, - "time": "2025-10-31T00:45:47+00:00" + "time": "2025-12-01T17:49:23+00:00" }, { "name": "nikic/php-parser", @@ -3568,16 +3568,16 @@ }, { "name": "samsonasik/array-lookup", - "version": "2.0.3", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/samsonasik/ArrayLookup.git", - "reference": "3b815db0c4d1847e900ca2548b1e9566c5f808f5" + "reference": "e61c61c732eeb4607768ecc8e212ecf830e0f401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/samsonasik/ArrayLookup/zipball/3b815db0c4d1847e900ca2548b1e9566c5f808f5", - "reference": "3b815db0c4d1847e900ca2548b1e9566c5f808f5", + "url": "https://api.github.com/repos/samsonasik/ArrayLookup/zipball/e61c61c732eeb4607768ecc8e212ecf830e0f401", + "reference": "e61c61c732eeb4607768ecc8e212ecf830e0f401", "shasum": "" }, "require": { @@ -3622,7 +3622,7 @@ ], "support": { "issues": "https://github.com/samsonasik/ArrayLookup/issues", - "source": "https://github.com/samsonasik/ArrayLookup/tree/2.0.3" + "source": "https://github.com/samsonasik/ArrayLookup/tree/2.1.0" }, "funding": [ { @@ -3630,26 +3630,25 @@ "type": "github" } ], - "time": "2025-06-25T02:38:52+00:00" + "time": "2025-12-04T22:51:39+00:00" }, { "name": "symfony/clock", - "version": "v7.4.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "9169f24776edde469914c1e7a1442a50f7a4e110" + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/9169f24776edde469914c1e7a1442a50f7a4e110", - "reference": "9169f24776edde469914c1e7a1442a50f7a4e110", + "url": "https://api.github.com/repos/symfony/clock/zipball/832119f9b8dbc6c8e6f65f30c5969eca1e88764f", + "reference": "832119f9b8dbc6c8e6f65f30c5969eca1e88764f", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -3688,7 +3687,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.4.0" + "source": "https://github.com/symfony/clock/tree/v8.0.0" }, "funding": [ { @@ -3708,7 +3707,7 @@ "type": "tidelift" } ], - "time": "2025-11-12T15:39:26+00:00" + "time": "2025-11-12T15:46:48+00:00" }, { "name": "symfony/console", @@ -5857,34 +5856,27 @@ }, { "name": "symfony/translation", - "version": "v7.4.0", + "version": "v8.0.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68" + "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", - "reference": "2d01ca0da3f092f91eeedb46f24aa30d2fca8f68", + "url": "https://api.github.com/repos/symfony/translation/zipball/82ab368a6fca6358d995b6dd5c41590fb42c03e6", + "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5.3|^3.3" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { "nikic/php-parser": "<5.0", - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" @@ -5892,17 +5884,17 @@ "require-dev": { "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/console": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0|^8.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5933,7 +5925,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.4.0" + "source": "https://github.com/symfony/translation/tree/v8.0.0" }, "funding": [ { @@ -5953,7 +5945,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2025-11-27T08:09:45+00:00" }, { "name": "symfony/translation-contracts", @@ -7300,23 +7292,23 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.4.0", + "version": "12.5.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c" + "reference": "bca180c050dd3ae15f87c26d25cabb34fe1a0a5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", - "reference": "67e8aed88f93d0e6e1cb7effe1a2dfc2fee6022c", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/bca180c050dd3ae15f87c26d25cabb34fe1a0a5a", + "reference": "bca180c050dd3ae15f87c26d25cabb34fe1a0a5a", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.6.1", + "nikic/php-parser": "^5.6.2", "php": ">=8.3", "phpunit/php-file-iterator": "^6.0", "phpunit/php-text-template": "^5.0", @@ -7324,10 +7316,10 @@ "sebastian/environment": "^8.0.3", "sebastian/lines-of-code": "^4.0", "sebastian/version": "^6.0", - "theseer/tokenizer": "^1.2.3" + "theseer/tokenizer": "^1.3.1" }, "require-dev": { - "phpunit/phpunit": "^12.3.7" + "phpunit/phpunit": "^12.4.4" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -7336,7 +7328,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.4.x-dev" + "dev-main": "12.5.x-dev" } }, "autoload": { @@ -7365,7 +7357,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.4.0" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.0" }, "funding": [ { @@ -7385,7 +7377,7 @@ "type": "tidelift" } ], - "time": "2025-09-24T13:44:41+00:00" + "time": "2025-11-29T07:15:54+00:00" }, { "name": "phpunit/php-file-iterator", @@ -7634,16 +7626,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.4.4", + "version": "12.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9253ec75a672e39fcc9d85bdb61448215b8162c7" + "reference": "e33a5132ea24119400f6ce5bce6665922e968bad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9253ec75a672e39fcc9d85bdb61448215b8162c7", - "reference": "9253ec75a672e39fcc9d85bdb61448215b8162c7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e33a5132ea24119400f6ce5bce6665922e968bad", + "reference": "e33a5132ea24119400f6ce5bce6665922e968bad", "shasum": "" }, "require": { @@ -7657,7 +7649,7 @@ "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.3", - "phpunit/php-code-coverage": "^12.4.0", + "phpunit/php-code-coverage": "^12.5.0", "phpunit/php-file-iterator": "^6.0.0", "phpunit/php-invoker": "^6.0.0", "phpunit/php-text-template": "^5.0.0", @@ -7679,7 +7671,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.4-dev" + "dev-main": "12.5-dev" } }, "autoload": { @@ -7711,7 +7703,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.4.4" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.1" }, "funding": [ { @@ -7735,7 +7727,7 @@ "type": "tidelift" } ], - "time": "2025-11-21T07:39:11+00:00" + "time": "2025-12-06T12:19:17+00:00" }, { "name": "rector/jack", @@ -8958,5 +8950,5 @@ "php": "^8.4" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/resources/docs/creating-a-node-visitor.md b/resources/docs/creating-a-node-visitor.md index 5465af60d..c012a32bf 100644 --- a/resources/docs/creating-a-node-visitor.md +++ b/resources/docs/creating-a-node-visitor.md @@ -1,17 +1,10 @@ -You can add new NodeVisitors to process nodes in the `rector.php` config, make sure you tag it correctly so dependency injection knows where to look: +You can add new NodeVisitors to decorate nodes with attributes before being used by one or more rules. This is useful if you need to add metadata to nodes, that are known only in higher nodes. E.g. if `Expr` is part of assign: ```php -registerService(HelloVisitor::class, null, ScopeResolverNodeVisitorInterface::class); +$value = 100; ``` -## A simple node visitor +First we create a node visitor, that implements `Rector\Contract\PhpParser\DecoratingNodeVisitorInterface`: ```php setAttribute(self::HELLO_ATTRIBUTE, 'i was here'); - return $node; + $node->var->setAttribute(self::IS_PART_OF_ASSIGN, true); + $node->expr->setAttribute(self::IS_PART_OF_ASSIGN, true); + + return null; + } +} +``` + +Never modify any nodes in the visitor as it might break node tree traversal. Only add attributes to nodes. + +
+ +### Register in Rector + +Then, register in the `rector.php` config: + +```php +registerDecoratingNodeVisitor(ScopeResolverNodeVisitorInterface::class); +``` + +
+ +If you're adding similar node visitor to Rector codebase, add class to `Rector\DependencyInjection\LazyContainerFactory::DECORATING_NODE_VISITOR_CLASSES` to register it. + +
+ +Then, you can check the attribute in any Rector rule: + +```php +fianl class SomeRector extends AbstractRector +{ + public function refactor(Node $node): ?Node + { + if ($node->getAttribute(AssignAwareNodeVisitor::IS_PART_OF_ASSIGN)) { + // we know node is part of assign + } + + // ... + + return null; } } ``` + +
+ +### Use Existing Attribute Keys + +Rector provides couple such node visitors out of the box. You can use these attributes already. + +To find out list of attributes you can use, see `IS_*` constants in +[`AttributeKey` class](https://github.com/rectorphp/rector-src/blob/main/src/NodeTypeResolver/Node/AttributeKey.php). diff --git a/src/Documentation/DocumentationMenuFactory.php b/src/Documentation/DocumentationMenuFactory.php index d83b822c3..34031c33c 100644 --- a/src/Documentation/DocumentationMenuFactory.php +++ b/src/Documentation/DocumentationMenuFactory.php @@ -72,7 +72,11 @@ public function create(): array 'writing-tests-for-custom-rule', 'Writing Tests For Custom Rule' ), - $this->documentationMenuItemFactory->createSection('creating-a-node-visitor', 'Creating Node Visitor'), + $this->documentationMenuItemFactory->createSection( + 'creating-a-node-visitor', + 'Creating Node Visitor', + true + ), $this->documentationMenuItemFactory->createSection('how-to-run-on-php-53', 'Run on PHP 5.3'), $this->documentationMenuItemFactory->createSection( 'https://leanpub.com/rector-the-power-of-automated-refactoring',