PHPStan\Analyser\NodeScopeResolver::processNode PHP Method

processNode() private method

private processNode ( PhpParser\Node $node, Scope $scope, Closure $nodeCallback )
$node PhpParser\Node
$scope Scope
$nodeCallback Closure
    private function processNode(\PhpParser\Node $node, Scope $scope, \Closure $nodeCallback)
    {
        $nodeCallback($node, $scope);
        if ($node instanceof \PhpParser\Node\Stmt\ClassLike) {
            if ($node instanceof Node\Stmt\Trait_) {
                return;
            }
            if (isset($node->namespacedName)) {
                $scope = $scope->enterClass((string) $node->namespacedName);
            } else {
                $scope = $scope->enterAnonymousClass($this->anonymousClassReflection);
            }
        } elseif ($node instanceof Node\Stmt\TraitUse) {
            $this->processTraitUse($node, $scope, $nodeCallback);
        } elseif ($node instanceof \PhpParser\Node\Stmt\Function_) {
            $scope = $scope->enterFunction($this->broker->getFunction($node->namespacedName, $scope));
        } elseif ($node instanceof \PhpParser\Node\Stmt\ClassMethod) {
            $scope = $this->enterClassMethod($scope, $node);
        } elseif ($node instanceof \PhpParser\Node\Stmt\Namespace_) {
            $scope = $scope->enterNamespace((string) $node->name);
        } elseif ($node instanceof \PhpParser\Node\Expr\StaticCall && (is_string($node->class) || $node->class instanceof \PhpParser\Node\Name) && is_string($node->name) && (string) $node->class === 'Closure' && $node->name === 'bind') {
            $thisType = null;
            if (isset($node->args[1])) {
                $argValue = $node->args[1]->value;
                if ($argValue instanceof Expr\ConstFetch && (string) $argValue->name === 'null') {
                    $thisType = null;
                } else {
                    $thisType = $scope->getType($argValue);
                }
            }
            $scopeClass = 'static';
            if (isset($node->args[2])) {
                $argValue = $node->args[2]->value;
                $argValueType = $scope->getType($argValue);
                if ($argValueType->getClass() !== null) {
                    $scopeClass = $argValueType->getClass();
                } elseif ($argValue instanceof Expr\ClassConstFetch && $argValue->name === 'class' && $argValue->class instanceof Name) {
                    $resolvedName = $scope->resolveName($argValue->class);
                    if ($resolvedName !== null) {
                        $scopeClass = $resolvedName;
                    }
                } elseif ($argValue instanceof Node\Scalar\String_) {
                    $scopeClass = $argValue->value;
                }
            }
            $closureBindScope = $scope->enterClosureBind($thisType, $scopeClass);
        } elseif ($node instanceof \PhpParser\Node\Expr\Closure) {
            $scope = $scope->enterAnonymousFunction($node->params, $node->uses, $node->returnType);
        } elseif ($node instanceof Foreach_) {
            if ($node->valueVar instanceof Variable) {
                $scope = $scope->enterForeach($node->expr, $node->valueVar->name, $node->keyVar !== null && $node->keyVar instanceof Variable ? $node->keyVar->name : null);
            } else {
                if ($node->keyVar !== null && $node->keyVar instanceof Variable) {
                    $scope = $scope->assignVariable($node->keyVar->name);
                }
                if ($node->valueVar instanceof Array_) {
                    $scope = $this->lookForArrayDestructuringArray($scope, $node->valueVar);
                } else {
                    $scope = $this->lookForAssigns($scope, $node->valueVar);
                }
            }
        } elseif ($node instanceof Catch_) {
            if (isset($node->types)) {
                $nodeTypes = $node->types;
            } elseif (isset($node->type)) {
                $nodeTypes = [$node->type];
            } else {
                throw new \PHPStan\ShouldNotHappenException();
            }
            $scope = $scope->enterCatch($nodeTypes, $node->var);
        } elseif ($node instanceof For_) {
            foreach ($node->init as $initExpr) {
                $scope = $this->lookForAssigns($scope, $initExpr);
            }
            foreach ($node->cond as $condExpr) {
                $scope = $this->lookForAssigns($scope, $condExpr);
            }
            foreach ($node->loop as $loopExpr) {
                $scope = $this->lookForAssigns($scope, $loopExpr);
            }
        } elseif ($node instanceof If_) {
            $scope = $this->lookForAssigns($scope, $node->cond);
            $ifScope = $scope;
            $scope = $this->lookForTypeSpecifications($scope, $node->cond);
            $this->processNode($node->cond, $scope, $nodeCallback);
            $this->processNodes($node->stmts, $scope, $nodeCallback);
            foreach ($node->elseifs as $elseif) {
                $this->processNode($elseif, $ifScope, $nodeCallback);
                $ifScope = $this->lookForAssigns($ifScope, $elseif->cond);
            }
            if ($node->else !== null) {
                $this->processNode($node->else, $ifScope, $nodeCallback);
            }
            return;
        } elseif ($node instanceof Switch_) {
            $scope = $this->lookForAssigns($scope, $node->cond);
            $switchScope = $scope;
            $switchConditionIsTrue = $node->cond instanceof Expr\ConstFetch && strtolower((string) $node->cond->name) === 'true';
            foreach ($node->cases as $caseNode) {
                if ($caseNode->cond !== null) {
                    $switchScope = $this->lookForAssigns($switchScope, $caseNode->cond);
                    if ($switchConditionIsTrue) {
                        $switchScope = $this->lookForTypeSpecifications($switchScope, $caseNode->cond);
                    }
                }
                $this->processNode($caseNode, $switchScope, $nodeCallback);
                if ($this->hasEarlyTermination($caseNode->stmts, $switchScope)) {
                    $switchScope = $scope;
                }
            }
            return;
        } elseif ($node instanceof ElseIf_) {
            $scope = $this->lookForAssigns($scope, $node->cond);
            $scope = $this->lookForTypeSpecifications($scope, $node->cond);
        } elseif ($node instanceof While_) {
            $scope = $this->lookForAssigns($scope, $node->cond);
            $scope = $this->lookForTypeSpecifications($scope, $node->cond);
        } elseif ($this->polluteCatchScopeWithTryAssignments && $node instanceof TryCatch) {
            foreach ($node->stmts as $statement) {
                $scope = $this->lookForAssigns($scope, $statement);
            }
        } elseif ($node instanceof Ternary) {
            $scope = $this->lookForAssigns($scope, $node->cond);
        } elseif ($node instanceof Do_) {
            foreach ($node->stmts as $statement) {
                $scope = $this->lookForAssigns($scope, $statement);
            }
        } elseif ($node instanceof FuncCall) {
            $scope = $scope->enterFunctionCall($node);
        } elseif ($node instanceof Expr\StaticCall) {
            $scope = $scope->enterFunctionCall($node);
        } elseif ($node instanceof MethodCall) {
            if ($scope->getType($node->var)->getClass() === 'Closure' && $node->name === 'call' && isset($node->args[0])) {
                $closureCallScope = $scope->enterClosureBind($scope->getType($node->args[0]->value), 'static');
            }
            $scope = $scope->enterFunctionCall($node);
        } elseif ($node instanceof Array_) {
            foreach ($node->items as $item) {
                $scope = $this->lookForAssigns($scope, $item->value);
            }
        } elseif ($node instanceof New_ && $node->class instanceof Class_) {
            $node->args = [];
            foreach ($node->class->stmts as $i => $statement) {
                if ($statement instanceof Node\Stmt\ClassMethod && $statement->name === '__construct') {
                    unset($node->class->stmts[$i]);
                    $node->class->stmts = array_values($node->class->stmts);
                    break;
                }
            }
            $code = $this->printer->prettyPrint([$node]);
            $classReflection = new \ReflectionClass(eval(sprintf('return %s', $code)));
            $this->anonymousClassReflection = $this->broker->getClassFromReflection($classReflection);
        } elseif ($node instanceof BooleanNot) {
            $scope = $scope->enterNegation();
        }
        $originalScope = $scope;
        foreach ($node->getSubNodeNames() as $subNodeName) {
            $scope = $originalScope;
            $subNode = $node->{$subNodeName};
            if (is_array($subNode)) {
                $argClosureBindScope = null;
                if (isset($closureBindScope) && $subNodeName === 'args') {
                    $argClosureBindScope = $closureBindScope;
                }
                $this->processNodes($subNode, $scope, $nodeCallback, $argClosureBindScope);
            } elseif ($subNode instanceof \PhpParser\Node) {
                if ($node instanceof Coalesce && $subNodeName === 'left') {
                    $scope = $this->assignVariable($scope, $subNode);
                }
                if ($node instanceof Ternary && $subNodeName === 'if') {
                    $scope = $this->lookForTypeSpecifications($scope, $node->cond);
                }
                if ($node instanceof BooleanAnd && $subNodeName === 'right') {
                    $scope = $this->lookForTypeSpecifications($scope, $node->left);
                }
                if (($node instanceof Assign || $node instanceof AssignRef) && $subNodeName === 'var') {
                    $scope = $this->lookForEnterVariableAssign($scope, $node->var);
                }
                $nodeScope = $scope;
                if ($node instanceof MethodCall && $subNodeName === 'var' && isset($closureCallScope)) {
                    $nodeScope = $closureCallScope;
                }
                $this->processNode($subNode, $nodeScope, $nodeCallback);
            }
        }
    }