public function visitCall(Node $node) : Context
{
$expression = $node->children['expr'];
(new ContextNode($this->code_base, $this->context, $node))->analyzeBackwardCompatibility();
foreach ($node->children['args']->children ?? [] as $arg_node) {
if ($arg_node instanceof Node) {
(new ContextNode($this->code_base, $this->context, $arg_node))->analyzeBackwardCompatibility();
}
}
if ($expression->kind == \ast\AST_VAR) {
$variable_name = (new ContextNode($this->code_base, $this->context, $expression))->getVariableName();
if (empty($variable_name)) {
return $this->context;
}
// $var() - hopefully a closure, otherwise we don't know
if ($this->context->getScope()->hasVariableWithName($variable_name)) {
$variable = $this->context->getScope()->getVariableByName($variable_name);
$union_type = $variable->getUnionType();
if ($union_type->isEmpty()) {
return $this->context;
}
foreach ($union_type->getTypeSet() as $type) {
if (!$type instanceof CallableType) {
continue;
}
$closure_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString((string) $type->asFQSEN());
if ($this->code_base->hasFunctionWithFQSEN($closure_fqsen)) {
// Get the closure
$function = $this->code_base->getFunctionByFQSEN($closure_fqsen);
// Check the call for paraemter and argument types
$this->analyzeCallToMethod($this->code_base, $function, $node);
}
}
}
} elseif ($expression->kind == \ast\AST_NAME) {
try {
$method = (new ContextNode($this->code_base, $this->context, $expression))->getFunction($expression->children['name'] ?? $expression->children['method']);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
return $this->context;
}
// Check the call for paraemter and argument types
$this->analyzeCallToMethod($this->code_base, $method, $node);
} elseif ($expression->kind == \ast\AST_CALL || $expression->kind == \ast\AST_STATIC_CALL || $expression->kind == \ast\AST_NEW || $expression->kind == \ast\AST_METHOD_CALL) {
$class_list = (new ContextNode($this->code_base, $this->context, $expression))->getClassList();
foreach ($class_list as $class) {
if (!$class->hasMethodWithName($this->code_base, '__invoke')) {
continue;
}
$method = $class->getMethodByNameInContext($this->code_base, '__invoke', $this->context);
// Check the call for paraemter and argument types
$this->analyzeCallToMethod($this->code_base, $method, $node);
}
}
return $this->context;
}