PHP-Parser icon indicating copy to clipboard operation
PHP-Parser copied to clipboard

[Node] add StmtsIterable interface to mark nodes that contain iterable stmts to improve hooking in node visitors

Open TomasVotruba opened this issue 3 years ago • 3 comments

At the moment, there are nodes in php-parser, that include list of other stmts. While iterating over can be easy with property_exists($node, 'stmts') (feels like poor coding), the hooking to them via node visitors/Rector/PHPStan not so.

At the moment hooking into them requries special constant and careful listing of all such nodes: https://github.com/rectorphp/rector-src/blob/975fdf113fab99b6120383211e997da2c820bd0a/rules/CodeQuality/NodeTypeGroup.php It happened we forgot some and then Rector rules were not applied.

These rules often work with previously used stmt, e.g. to remove already assigned value:

 function setDefault()
 {
-    $value = 100;
     $value = 150;
     // ...
 }

This could be somehow achieved with next/previous/parent attributes. Yet PHPStan and Rector are moving away from next/previous/parent nodes for 2 reasons:

  • it's heavy for memory
  • it can take time to find firs previous stmts, as e.g. Assign and other Expr can be deeply nested:
if ($number === 1000 && ($value = 100)++ > --$breakpoint) {
    
}

Here removing $value = 100 itself is ilegal and requires quite complex check to all parent nodes. Iterating only over all directly available stmts would make this no-brainer.


Where would be this interface be useful?

  • Rector could hook into all those nodes via single type
  • PHPStan that can hook only into single node (see getNodeType()) would be able to hook into those too
  • not just these 2 projects, but any php-parser based traverser could detect such node type easily

This marker will dramatically simplify integration of new rules to work with stmts.


At first I thought we could have class StmtsAware extends Stmt {}, but sometime Expr contains $stmts too:

https://github.com/nikic/PHP-Parser/blob/678ccbe0720549c51d30b76d094a117eac819e9e/lib/PhpParser/Node/Expr/Closure.php#L21-L22

Thus interface marker choice. We're already using such marker interface in Rector via patching of php-parser nodes: https://github.com/rectorphp/rector/blob/d08b83c4264ba5b265890a049285518f79684644/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php#L8

And it works great :+1:


What do you think?

TomasVotruba avatar May 15 '22 22:05 TomasVotruba

I feel like this got stuck but I don't know why. I'd love to get it before version 5 gets released. What needs to be done here to finish it?

TomasVotruba avatar Aug 09 '22 19:08 TomasVotruba

I feel like this got stuck but I don't know why. I'd love to get it before version 5 gets released. What needs to be done here to finish it?

This is blocked on the nullable $stmts in ClassMethod. I'm not sure what to do about it :/

nikic avatar Sep 21 '22 18:09 nikic

I've removed the interface from ClassMethod, as the use case is really different. I also resolved the rest of the comments.


The main goal of this interface is to catch all the nested structures with single node, and it does well now :+1:

if (...) {
    foreach (...) {
        $closure = function() {
            $item = 1;

            if (...) {
                return true;
            }
        };
    }
}

TomasVotruba avatar Sep 25 '22 14:09 TomasVotruba