Skip to content

Comments

Faster MutatingScope->getNodeKey()#5014

Merged
staabm merged 4 commits intophpstan:2.1.xfrom
staabm:node-key
Feb 24, 2026
Merged

Faster MutatingScope->getNodeKey()#5014
staabm merged 4 commits intophpstan:2.1.xfrom
staabm:node-key

Conversation

@staabm
Copy link
Contributor

@staabm staabm commented Feb 21, 2026

saves 0,3-0,4 seconds on php bin/phpstan analyse src/Analyser/ src/Rules/ -v --debug.
also reduces memory consumption by a few megabytes (because not every variable needs to hold a expr-printed attribute).

getNodeKey is invoked by-far most often with Variable, see numer of invocations per node-type:

array(67) {
  ["PHPStan\Node\Expr\IntertwinedVariableByReferenceWithExpr"]=>
  int(4)
  ["PhpParser\Node\Expr\BinaryOp\LogicalXor"]=>
  int(5)
  ["PhpParser\Node\Expr\PostDec"]=>
  int(8)
  ["PhpParser\Node\Expr\AssignOp\Minus"]=>
  int(8)
  ["PHPStan\Node\IssetExpr"]=>
  int(12)
  ["PhpParser\Node\Expr\Throw_"]=>
  int(18)
  ["PhpParser\Node\Expr\ErrorSuppress"]=>
  int(25)
  ["PhpParser\Node\Expr\BinaryOp\Mod"]=>
  int(34)
  ["PhpParser\Node\Expr\BitwiseNot"]=>
  int(35)
  ["PhpParser\Node\Expr\BinaryOp\BitwiseOr"]=>
  int(38)
  ["PhpParser\Node\Expr\BinaryOp\Mul"]=>
  int(41)
  ["PhpParser\Node\Expr\BinaryOp\Spaceship"]=>
  int(46)
  ["PhpParser\Node\Expr\PreDec"]=>
  int(46)
  ["PhpParser\Node\Expr\Cast\Int_"]=>
  int(56)
  ["PhpParser\Node\Expr\Clone_"]=>
  int(60)
  ["PhpParser\Node\Expr\Cast\Bool_"]=>
  int(64)
  ["PhpParser\Node\Expr\AssignOp\BitwiseOr"]=>
  int(79)
  ["PhpParser\Node\Expr\AssignOp\Plus"]=>
  int(85)
  ["PhpParser\Node\Expr\List_"]=>
  int(98)
  ["PhpParser\Node\Expr\BinaryOp\SmallerOrEqual"]=>
  int(115)
  ["PhpParser\Node\Expr\BinaryOp\Equal"]=>
  int(121)
  ["PhpParser\Node\Expr\AssignOp\Concat"]=>
  int(148)
  ["PhpParser\Node\Expr\StaticPropertyFetch"]=>
  int(163)
  ["PhpParser\Node\Expr\PostInc"]=>
  int(230)
  ["PhpParser\Node\Expr\Include_"]=>
  int(235)
  ["PhpParser\Node\Expr\AssignOp\Coalesce"]=>
  int(275)
  ["PhpParser\Node\Expr\UnaryMinus"]=>
  int(311)
  ["PhpParser\Node\Expr\Closure"]=>
  int(408)
  ["PhpParser\Node\Expr\BinaryOp\BitwiseAnd"]=>
  int(436)
  ["PhpParser\Node\Expr\BinaryOp\GreaterOrEqual"]=>
  int(478)
  ["PhpParser\Node\Expr\PreInc"]=>
  int(784)
  ["PhpParser\Node\Expr\BinaryOp\Smaller"]=>
  int(793)
  ["PhpParser\Node\Expr\BinaryOp\Minus"]=>
  int(1031)
  ["PHPStan\Node\Expr\AlwaysRememberedExpr"]=>
  int(1161)
  ["PhpParser\Node\Expr\BinaryOp\Plus"]=>
  int(1199)
  ["PhpParser\Node\Expr\ArrowFunction"]=>
  int(1211)
  ["PhpParser\Node\Expr\Cast\String_"]=>
  int(1710)
  ["PhpParser\Node\Expr\BinaryOp\Greater"]=>
  int(1941)
  ["PhpParser\Node\Scalar\Float_"]=>
  int(2085)
  ["PHPStan\Node\Expr\OriginalForeachKeyExpr"]=>
  int(2157)
  ["PhpParser\Node\Expr\BinaryOp\Concat"]=>
  int(2303)
  ["PHPStan\Node\Expr\TypeExpr"]=>
  int(2689)
  ["PhpParser\Node\Expr\BinaryOp\Coalesce"]=>
  int(3159)
  ["PhpParser\Node\Expr\Ternary"]=>
  int(3611)
  ["PHPStan\Node\Expr\ParameterVariableOriginalValueExpr"]=>
  int(3730)
  ["PhpParser\Node\Expr\Isset_"]=>
  int(3837)
  ["PHPStan\Node\Expr\PropertyInitializationExpr"]=>
  int(4632)
  ["PhpParser\Node\Expr\BinaryOp\BooleanOr"]=>
  int(9840)
  ["PHPStan\Node\Expr\ForeachValueByRefExpr"]=>
  int(10580)
  ["PhpParser\Node\Expr\BinaryOp\NotIdentical"]=>
  int(11218)
  ["PhpParser\Node\Expr\New_"]=>
  int(12485)
  ["PhpParser\Node\Expr\BinaryOp\BooleanAnd"]=>
  int(23353)
  ["PhpParser\Node\Scalar\Int_"]=>
  int(24669)
  ["PhpParser\Node\Expr\BooleanNot"]=>
  int(24972)
  ["PhpParser\Node\Expr\Instanceof_"]=>
  int(25872)
  ["PhpParser\Node\Expr\Array_"]=>
  int(29885)
  ["PhpParser\Node\Expr\ClassConstFetch"]=>
  int(29916)
  ["PhpParser\Node\Expr\Assign"]=>
  int(33054)
  ["PhpParser\Node\Expr\BinaryOp\Identical"]=>
  int(46624)
  ["PhpParser\Node\Scalar\String_"]=>
  int(62483)
  ["PhpParser\Node\Expr\StaticCall"]=>
  int(67364)
  ["PhpParser\Node\Expr\FuncCall"]=>
  int(68771)
  ["PhpParser\Node\Expr\ArrayDimFetch"]=>
  int(76027)
  ["PhpParser\Node\Expr\ConstFetch"]=>
  int(76787)
  ["PhpParser\Node\Expr\PropertyFetch"]=>
  int(170030)
  ["PhpParser\Node\Expr\MethodCall"]=>
  int(475115)
  ["PhpParser\Node\Expr\Variable"]=>
  int(1037687)
}

grafik

@staabm staabm marked this pull request as ready for review February 21, 2026 09:21
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@staabm staabm closed this Feb 21, 2026
@staabm staabm deleted the node-key branch February 21, 2026 09:27
@staabm staabm reopened this Feb 21, 2026
Copy link
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this one.

Is it the right place to do this ? I mean, this early if seems to avoid

  • calling printExpr
  • two if conditions which seems really quick

So if the printExpr is the slow method to skip, we could have the early return inside to improve the situation more often ? Cause their is multiple other call to printExpr.

@staabm staabm marked this pull request as draft February 23, 2026 14:19
Comment on lines +920 to +923
// perf optimize for the most common path
if ($node instanceof Variable && !$node->name instanceof Expr) {
return '$' . $node->name;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I duplicated the logic to here now, as getNodeKey is by far the most common caller of exprPrinter->printExpr:

grafik

and with this check here, we also prevent unnecessary getAttributes calls below

@staabm staabm marked this pull request as ready for review February 24, 2026 09:23
@phpstan-bot
Copy link
Collaborator

This pull request has been marked as ready for review.

@staabm
Copy link
Contributor Author

staabm commented Feb 24, 2026

running this change on

blackfire run --ignore-exit-status php bin/phpstan analyze src/Analyser --debug -v --level=5

leads the following difference

grafik

we see the memory reduction because exprPrinter->printExpr puts a cache into every AstNode attributes property.

@staabm staabm merged commit b8492b8 into phpstan:2.1.x Feb 24, 2026
639 of 642 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants