Skip to content

Commit

Permalink
Optimized memory consumption
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Aug 26, 2020
1 parent d7f9e90 commit c9678cd
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 90 deletions.
96 changes: 6 additions & 90 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
use PHPStan\Node\ClassConstantsNode;
use PHPStan\Node\ClassMethodsNode;
use PHPStan\Node\ClassPropertiesNode;
use PHPStan\Node\ClassStatementsGatherer;
use PHPStan\Node\ClosureReturnStatementsNode;
use PHPStan\Node\Constant\ClassConstantFetch;
use PHPStan\Node\ExecutionEndNode;
use PHPStan\Node\FunctionReturnStatementsNode;
use PHPStan\Node\InArrowFunctionNode;
Expand All @@ -64,8 +64,6 @@
use PHPStan\Node\LiteralArrayItem;
use PHPStan\Node\LiteralArrayNode;
use PHPStan\Node\MethodReturnStatementsNode;
use PHPStan\Node\Property\PropertyRead;
use PHPStan\Node\Property\PropertyWrite;
use PHPStan\Node\ReturnStatement;
use PHPStan\Node\UnreachableStatementNode;
use PHPStan\Parser\Parser;
Expand All @@ -74,7 +72,6 @@
use PHPStan\PhpDoc\Tag\ParamTag;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\FunctionReflection;
use PHPStan\Reflection\MethodReflectionWithNode;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Reflection\PassedByReference;
Expand Down Expand Up @@ -574,92 +571,11 @@ private function processStmtNode(
throw new \PHPStan\ShouldNotHappenException();
}

$properties = [];
$methods = [];
$methodCalls = [];
$propertyUsages = [];
$constants = [];
$constantFetches = [];

/** @var \Closure(Node, Scope): void $customCallback2 */
$customCallback2 = null;
$customCallback1 = function (Node $node, Scope $scope) use ($classReflection, &$properties, &$methods, &$methodCalls, &$propertyUsages, &$constants, &$constantFetches, &$customCallback1, &$customCallback2, $classScope): void {
if (!$scope->isInClass()) {
throw new \PHPStan\ShouldNotHappenException();
}
if ($scope->getClassReflection()->getName() !== $classReflection->getName()) {
return;
}
if ($node instanceof Node\Stmt\Property && !$scope->isInTrait()) {
$properties[] = $node;
return;
}
if ($node instanceof Node\Stmt\ClassMethod && !$scope->isInTrait()) {
$methods[] = $node;
return;
}
if ($node instanceof Node\Stmt\ClassConst) {
$constants[] = $node;
return;
}
if ($node instanceof MethodCall || $node instanceof StaticCall) {
$methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
if ($node instanceof MethodCall && $node->name instanceof Node\Identifier) {
$calleeType = $scope->getType($node->var);
$methodName = $node->name->toString();
if ($calleeType->hasMethod($methodName)->yes()) {
$methodReflection = $calleeType->getMethod($methodName, $scope);
if (
$methodReflection instanceof MethodReflectionWithNode
&& $methodReflection->getDeclaringClass()->getName() === $classReflection->getName()
&& $methodReflection->getNode() !== null
) {
$this->processNodes([$methodReflection->getNode()], $classScope, $customCallback2);
}
}
}
return;
}
if ($node instanceof Array_ && count($node->items) === 2) {
$methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
return;
}
if ($node instanceof Expr\ClassConstFetch) {
$constantFetches[] = new ClassConstantFetch($node, $scope);
return;
}
if (!$node instanceof Expr) {
return;
}
if ($node instanceof Expr\AssignOp\Coalesce) {
$customCallback1($node->var, $scope);
return;
}
if ($node instanceof Node\Scalar\EncapsedStringPart) {
return;
}
$inAssign = $scope->isInExpressionAssign($node);
while ($node instanceof ArrayDimFetch) {
$node = $node->var;
}
if (!$node instanceof PropertyFetch && !$node instanceof StaticPropertyFetch) {
return;
}

if ($inAssign) {
$propertyUsages[] = new PropertyWrite($node, $scope);
} else {
$propertyUsages[] = new PropertyRead($node, $scope);
}
};
$customCallback2 = static function (Node $node, Scope $scope) use ($nodeCallback, $customCallback1): void {
$nodeCallback($node, $scope);
$customCallback1($node, $scope);
};
$this->processStmtNodes($stmt, $stmt->stmts, $classScope, $customCallback2);
$nodeCallback(new ClassPropertiesNode($stmt, $properties, $propertyUsages, $methodCalls), $classScope);
$nodeCallback(new ClassMethodsNode($stmt, $methods, $methodCalls), $classScope);
$nodeCallback(new ClassConstantsNode($stmt, $constants, $constantFetches), $classScope);
$classStatementsGatherer = new ClassStatementsGatherer($classReflection, $classScope, $nodeCallback);
$this->processStmtNodes($stmt, $stmt->stmts, $classScope, $classStatementsGatherer);
$nodeCallback(new ClassPropertiesNode($stmt, $classStatementsGatherer->getProperties(), $classStatementsGatherer->getPropertyUsages(), $classStatementsGatherer->getMethodCalls()), $classScope);
$nodeCallback(new ClassMethodsNode($stmt, $classStatementsGatherer->getMethods(), $classStatementsGatherer->getMethodCalls()), $classScope);
$nodeCallback(new ClassConstantsNode($stmt, $classStatementsGatherer->getConstants(), $classStatementsGatherer->getConstantFetches()), $classScope);
} elseif ($stmt instanceof Node\Stmt\Property) {
$hasYield = false;
foreach ($stmt->props as $prop) {
Expand Down
189 changes: 189 additions & 0 deletions src/Node/ClassStatementsGatherer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php declare(strict_types = 1);

namespace PHPStan\Node;

use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PHPStan\Analyser\Scope;
use PHPStan\Node\Constant\ClassConstantFetch;
use PHPStan\Node\Property\PropertyRead;
use PHPStan\Node\Property\PropertyWrite;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflectionWithNode;

class ClassStatementsGatherer
{

private ClassReflection $classReflection;

private Scope $classScope;

/** @var callable(\PhpParser\Node $node, Scope $scope): void */
private $nodeCallback;

/** @var \PhpParser\Node\Stmt\Property[] */
private array $properties = [];

/** @var \PhpParser\Node\Stmt\ClassMethod[] */
private array $methods = [];

/** @var \PHPStan\Node\Method\MethodCall[] */
private array $methodCalls = [];

/** @var array<int, PropertyWrite|PropertyRead> */
private array $propertyUsages = [];

/** @var \PhpParser\Node\Stmt\ClassConst[] */
private array $constants = [];

/** @var ClassConstantFetch[] */
private array $constantFetches = [];

/**
* @param ClassReflection $classReflection
* @param Scope $classScope
* @param callable(\PhpParser\Node $node, Scope $scope): void $nodeCallback
*/
public function __construct(
ClassReflection $classReflection,
Scope $classScope,
callable $nodeCallback
)
{
$this->classReflection = $classReflection;
$this->classScope = $classScope;
$this->nodeCallback = $nodeCallback;
}

/**
* @return \PhpParser\Node\Stmt\Property[]
*/
public function getProperties(): array
{
return $this->properties;
}

/**
* @return \PhpParser\Node\Stmt\ClassMethod[]
*/
public function getMethods(): array
{
return $this->methods;
}

/**
* @return Method\MethodCall[]
*/
public function getMethodCalls(): array
{
return $this->methodCalls;
}

/**
* @return array<int, PropertyWrite|PropertyRead>
*/
public function getPropertyUsages(): array
{
return $this->propertyUsages;
}

/**
* @return \PhpParser\Node\Stmt\ClassConst[]
*/
public function getConstants(): array
{
return $this->constants;
}

/**
* @return ClassConstantFetch[]
*/
public function getConstantFetches(): array
{
return $this->constantFetches;
}

public function __invoke(\PhpParser\Node $node, Scope $scope): void
{
$nodeCallback = $this->nodeCallback;
$nodeCallback($node, $scope);
$this->gatherNodes($node, $scope);
}

private function gatherNodes(\PhpParser\Node $node, Scope $scope): void
{
if (!$scope->isInClass()) {
throw new \PHPStan\ShouldNotHappenException();
}
if ($scope->getClassReflection()->getName() !== $this->classReflection->getName()) {
return;
}
if ($node instanceof \PhpParser\Node\Stmt\Property && !$scope->isInTrait()) {
$this->properties[] = $node;
return;
}
if ($node instanceof \PhpParser\Node\Stmt\ClassMethod && !$scope->isInTrait()) {
$this->methods[] = $node;
return;
}
if ($node instanceof \PhpParser\Node\Stmt\ClassConst) {
$this->constants[] = $node;
return;
}
if ($node instanceof MethodCall || $node instanceof StaticCall) {
$this->methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
if ($node instanceof MethodCall && $node->name instanceof \PhpParser\Node\Identifier) {
$calleeType = $scope->getType($node->var);
$methodName = $node->name->toString();
if ($calleeType->hasMethod($methodName)->yes()) {
$methodReflection = $calleeType->getMethod($methodName, $scope);
if (
$methodReflection instanceof MethodReflectionWithNode
&& $methodReflection->getDeclaringClass()->getName() === $this->classReflection->getName()
&& $methodReflection->getNode() !== null
) {
//$this->processNodes([$methodReflection->getNode()], $this->classScope, $this);
}
}
}
return;
}
if ($node instanceof Array_ && count($node->items) === 2) {
$this->methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
return;
}
if ($node instanceof Expr\ClassConstFetch) {
$this->constantFetches[] = new ClassConstantFetch($node, $scope);
return;
}
if (!$node instanceof Expr) {
return;
}
if ($node instanceof Expr\AssignOp\Coalesce) {
$this->gatherNodes($node->var, $scope);
return;
}
if ($node instanceof \PhpParser\Node\Scalar\EncapsedStringPart) {
return;
}
$inAssign = $scope->isInExpressionAssign($node);
while ($node instanceof ArrayDimFetch) {
$node = $node->var;
}
if (!$node instanceof PropertyFetch && !$node instanceof StaticPropertyFetch) {
return;
}

if ($inAssign) {
$this->propertyUsages[] = new PropertyWrite($node, $scope);
} else {
$this->propertyUsages[] = new PropertyRead($node, $scope);
}
}

}

0 comments on commit c9678cd

Please sign in to comment.