PHPStan\Analyser\NodeScopeResolver::processNodes PHP Method

processNodes() public method

public processNodes ( array $nodes, Scope $scope, Closure $nodeCallback, Scope $closureBindScope = null )
$nodes array
$scope Scope
$nodeCallback Closure
$closureBindScope Scope
    public function processNodes(array $nodes, Scope $scope, \Closure $nodeCallback, Scope $closureBindScope = null)
    {
        foreach ($nodes as $i => $node) {
            if (!$node instanceof \PhpParser\Node) {
                continue;
            }
            if ($scope->getInFunctionCall() !== null && $node instanceof Arg) {
                $functionCall = $scope->getInFunctionCall();
                $value = $node->value;
                $parametersAcceptor = $this->findParametersAcceptorInFunctionCall($functionCall, $scope);
                if ($parametersAcceptor !== null) {
                    $parameters = $parametersAcceptor->getParameters();
                    $assignByReference = false;
                    if (isset($parameters[$i])) {
                        $assignByReference = $parameters[$i]->isPassedByReference();
                    } elseif (count($parameters) > 0 && $parametersAcceptor->isVariadic()) {
                        $lastParameter = $parameters[count($parameters) - 1];
                        $assignByReference = $lastParameter->isPassedByReference();
                    }
                    if ($assignByReference && $value instanceof Variable) {
                        $scope = $scope->assignVariable($value->name, new MixedType(true));
                    }
                }
            }
            $nodeScope = $scope;
            if ($i === 0 && $closureBindScope !== null) {
                $nodeScope = $closureBindScope;
            }
            $this->processNode($node, $nodeScope, $nodeCallback);
            $scope = $this->lookForAssigns($scope, $node);
            if ($node instanceof If_) {
                if ($this->hasEarlyTermination($node->stmts, $scope)) {
                    $scope = $this->lookForTypeSpecificationsInEarlyTermination($scope, $node->cond);
                    $this->processNode($node->cond, $scope, function (Node $node, Scope $inScope) use(&$scope) {
                        if ($inScope->isNegated()) {
                            if ($node instanceof Isset_) {
                                foreach ($node->vars as $var) {
                                    if ($var instanceof PropertyFetch) {
                                        $scope = $scope->specifyFetchedPropertyFromIsset($var);
                                    }
                                }
                            }
                        }
                    });
                }
            } elseif ($node instanceof Node\Stmt\Declare_) {
                foreach ($node->declares as $declare) {
                    if ($declare instanceof Node\Stmt\DeclareDeclare && $declare->key === 'strict_types' && $declare->value instanceof Node\Scalar\LNumber && $declare->value->value === 1) {
                        $scope = $scope->enterDeclareStrictTypes();
                        break;
                    }
                }
            } elseif ($node instanceof FuncCall && $node->name instanceof Name && (string) $node->name === 'assert' && isset($node->args[0])) {
                $scope = $this->lookForTypeSpecifications($scope, $node->args[0]->value);
            } elseif ($node instanceof Assign && $node->var instanceof Array_) {
                $scope = $this->lookForArrayDestructuringArray($scope, $node->var);
            }
        }
    }

Usage Example

Beispiel #1
0
 /**
  * @param string[] $files
  * @param \Closure|null $progressCallback
  * @return string[]|\PHPStan\Analyser\Error[] errors
  */
 public function analyse(array $files, \Closure $progressCallback = null) : array
 {
     $errors = [];
     if ($this->bootstrapFile !== null) {
         if (!is_file($this->bootstrapFile)) {
             return [sprintf('Bootstrap file %s does not exist.', $this->bootstrapFile)];
         }
         try {
             require_once $this->bootstrapFile;
         } catch (\Throwable $e) {
             return [$e->getMessage()];
         }
     }
     foreach ($this->ignoreErrors as $ignoreError) {
         try {
             \Nette\Utils\Strings::match('', $ignoreError);
         } catch (\Nette\Utils\RegexpException $e) {
             $errors[] = $e->getMessage();
         }
     }
     if (count($errors) > 0) {
         return $errors;
     }
     foreach ($files as $file) {
         try {
             if ($this->isExcludedFromAnalysing($file)) {
                 if ($progressCallback !== null) {
                     $progressCallback($file);
                 }
                 continue;
             }
             $fileErrors = [];
             $this->nodeScopeResolver->processNodes($this->parser->parseFile($file), new Scope($this->broker, $this->printer, $file), function (\PhpParser\Node $node, Scope $scope) use(&$fileErrors) {
                 if ($node instanceof \PhpParser\Node\Stmt\Trait_) {
                     return;
                 }
                 $classes = array_merge([get_class($node)], class_parents($node));
                 foreach ($this->registry->getRules($classes) as $rule) {
                     $ruleErrors = $this->createErrors($node, $scope->getAnalysedContextFile(), $rule->processNode($node, $scope));
                     $fileErrors = array_merge($fileErrors, $ruleErrors);
                 }
             });
             if ($progressCallback !== null) {
                 $progressCallback($file);
             }
             $errors = array_merge($errors, $fileErrors);
         } catch (\PhpParser\Error $e) {
             $errors[] = new Error($e->getMessage(), $file);
         } catch (\PHPStan\AnalysedCodeException $e) {
             $errors[] = new Error($e->getMessage(), $file);
         } catch (\Throwable $t) {
             $errors[] = new Error(sprintf('Internal error: %s', $t->getMessage()), $file);
         }
     }
     $unmatchedIgnoredErrors = $this->ignoreErrors;
     $errors = array_values(array_filter($errors, function (string $error) use(&$unmatchedIgnoredErrors) : bool {
         foreach ($this->ignoreErrors as $i => $ignore) {
             if (\Nette\Utils\Strings::match($error, $ignore) !== null) {
                 unset($unmatchedIgnoredErrors[$i]);
                 return false;
             }
         }
         return true;
     }));
     foreach ($unmatchedIgnoredErrors as $unmatchedIgnoredError) {
         $errors[] = sprintf('Ignored error pattern %s was not matched in reported errors.', $unmatchedIgnoredError);
     }
     return $errors;
 }