public function visitProp(Node $node) : Context
{
$property_name = $node->children['prop'];
// Things like $foo->$bar
if (!is_string($property_name)) {
return $this->context;
}
assert(is_string($property_name), "Property must be string");
try {
$class_list = (new ContextNode($this->code_base, $this->context, $node->children['expr']))->getClassList();
} catch (CodeBaseException $exception) {
// This really shouldn't happen since the code
// parsed cleanly. This should fatal.
// throw $exception;
return $this->context;
} catch (\Exception $exception) {
// If we can't figure out what kind of a class
// this is, don't worry about it
return $this->context;
}
foreach ($class_list as $clazz) {
// Check to see if this class has the property or
// a setter
if (!$clazz->hasPropertyWithName($this->code_base, $property_name)) {
if (!$clazz->hasMethodWithName($this->code_base, '__set')) {
continue;
}
}
try {
$property = $clazz->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
} catch (IssueException $exception) {
Issue::maybeEmitInstance($this->code_base, $this->context, $exception->getIssueInstance());
return $this->context;
}
if (!$this->right_type->canCastToExpandedUnionType($property->getUnionType(), $this->code_base)) {
$this->emitIssue(Issue::TypeMismatchProperty, $node->lineno ?? 0, (string) $this->right_type, "{$clazz->getFQSEN()}::{$property->getName()}", (string) $property->getUnionType());
return $this->context;
} else {
// If we're assigning to an array element then we don't
// know what the constitutation of the parameter is
// outside of the scope of this assignment, so we add to
// its union type rather than replace it.
if ($this->is_dim_assignment) {
$property->getUnionType()->addUnionType($this->right_type);
}
}
// After having checked it, add this type to it
$property->getUnionType()->addUnionType($this->right_type);
return $this->context;
}
$std_class_fqsen = FullyQualifiedClassName::getStdClassFQSEN();
if (Config::get()->allow_missing_properties || !empty($class_list) && $class_list[0]->getFQSEN() == $std_class_fqsen) {
try {
// Create the property
$property = (new ContextNode($this->code_base, $this->context, $node))->getOrCreateProperty($property_name);
$property->getUnionType()->addUnionType($this->right_type);
} catch (\Exception $exception) {
// swallow it
}
} elseif (!empty($class_list)) {
$this->emitIssue(Issue::UndeclaredProperty, $node->lineno ?? 0, "{$class_list[0]->getFQSEN()}->{$property_name}");
} else {
// If we hit this part, we couldn't figure out
// the class, so we ignore the issue
}
return $this->context;
}