[Node] add StmtsIterable interface to mark nodes that contain iterable stmts to improve hooking in node visitors
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.
Assignand otherExprcan 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?
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?
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 :/
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;
}
};
}
}