public function visitStaticCall(Node $node) : Context
{
// Get the name of the method being called
$method_name = $node->children['method'];
// Give up on things like Class::$var
if (!is_string($method_name)) {
return $this->context;
}
// Get the name of the static class being referenced
$static_class = '';
if ($node->children['class']->kind == \ast\AST_NAME) {
$static_class = $node->children['class']->children['name'];
}
// Short circuit on a constructor being called statically
// on something other than 'parent'
if ($method_name === '__construct') {
if ($static_class !== 'parent') {
$this->emitIssue(Issue::UndeclaredStaticMethod, $node->lineno ?? 0, "{$static_class}::{$method_name}()");
}
}
try {
// Get a reference to the method being called
$method = (new ContextNode($this->code_base, $this->context, $node))->getMethod($method_name, true);
// Get the method thats calling the static method
$calling_method = null;
if ($this->context->isInMethodScope()) {
$calling_function_like = $this->context->getFunctionLikeInScope($this->code_base);
if ($calling_function_like instanceof Method) {
$calling_method = $calling_function_like;
}
}
// If the method being called isn't actually static and it's
// not a call to parent::f from f, we may be in trouble.
if (!$method->isStatic() && !(('parent' === $static_class || 'self' === $static_class || 'static' === $static_class) && $this->context->isInMethodScope() && ($this->context->getFunctionLikeFQSEN()->getName() == $method->getFQSEN()->getName() || $calling_method && !$calling_method->isStatic())) && !($this->context->isInClassScope() && $this->context->isInFunctionLikeScope() && ($calling_method && !$calling_method->isStatic())) && !($this->context->isInClassScope() && $this->context->isInFunctionLikeScope() && $this->context->getFunctionLikeFQSEN()->isClosure())) {
$class_list = (new ContextNode($this->code_base, $this->context, $node->children['class']))->getClassList();
if (!empty($class_list)) {
$class = array_values($class_list)[0];
$this->emitIssue(Issue::StaticCallToNonStatic, $node->lineno ?? 0, "{$class->getFQSEN()}::{$method_name}()", $method->getFileRef()->getFile(), (string) $method->getFileRef()->getLineNumberStart());
}
}
// Make sure the parameters look good
$this->analyzeCallToMethod($this->code_base, $method, $node);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
} catch (\Exception $exception) {
// If we can't figure out the class for this method
// call, cry YOLO and mark every method with that
// name with a reference.
if (Config::get()->dead_code_detection && Config::get()->dead_code_detection_prefer_false_negative) {
foreach ($this->code_base->getMethodSetByName($method_name) as $method) {
$method->addReference($this->context);
}
}
// If we can't figure out what kind of a call
// this is, don't worry about it
return $this->context;
}
return $this->context;
}