/** * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitNew(Node $node) : Context { try { $context_node = new ContextNode($this->code_base, $this->context, $node); $method = $context_node->getMethod('__construct', false); // Add a reference to each class this method // could be called on foreach ($context_node->getClassList() as $class) { $class->addReference($this->context); } $this->analyzeCallToMethod($this->code_base, $method, $node); $class_list = $context_node->getClassList(); foreach ($class_list as $class) { // Make sure we're not instantiating an abstract // class if ($class->isAbstract() && (!$this->context->hasClassFQSEN() || $class->getFQSEN() != $this->context->getClassFQSEN())) { Issue::emit(Issue::TypeInstantiateAbstract, $this->context->getFile(), $node->lineno ?? 0, (string) $class->getFQSEN()); } // Make sure we're not instantiating an interface if ($class->isInterface()) { Issue::emit(Issue::TypeInstantiateInterface, $this->context->getFile(), $node->lineno ?? 0, (string) $class->getFQSEN()); } } } catch (IssueException $exception) { $exception->getIssueInstance()(); } catch (\Exception $exception) { // If we can't figure out what kind of a call // this is, don't worry about it return $this->context; } return $this->context; }
public function visitNew(Node $node) : bool { if (!$this->classExists()) { return $this->classExistsOrIsNative($node); } $clazz = $this->code_base->getClassByFQSEN($this->class_fqsen); if ($clazz->isAbstract()) { if (!$this->context->hasClassFQSEN() || $clazz->getFQSEN() != $this->context->getClassFQSEN()) { Log::err(Log::ETYPE, "Cannot instantiate abstract class {$this->class_name}", $this->context->getFile(), $node->lineno); return false; } return true; } if ($clazz->isInterface()) { if (!UnionType::fromStringInContext($this->class_name, $this->context)->isNativeType()) { Log::err(Log::ETYPE, "Cannot instantiate interface {$this->class_name}", $this->context->getFile(), $node->lineno); return false; } } return true; }
/** * @param string $name * The name of the property * * @param Context $context * The context of the caller requesting the property * * @return Property * A property with the given name * * @throws AccessException * An exception may be thrown if the caller does not * have access to the given property from the given * context */ public function getPropertyByNameInContext(CodeBase $code_base, string $name, Context $context) : Property { $property = $code_base->getProperty($this->getFQSEN(), $name); // If we're getting the property from outside of this // class and the property isn't public and we don't // have a getter or setter, emit an access error if ((!$context->hasClassFQSEN() || $context->getClassFQSEN() != $this->getFQSEN()) && !$property->isPublic() && !$this->hasMethodWithName($code_base, '__get') && !$this->hasMethodWithName($code_base, '__set')) { if ($property->isPrivate()) { throw new AccessException("Cannot access private property {$this->getFQSEN()}::\${$property->getName()}"); } if ($property->isProtected()) { throw new AccessException("Cannot access protected property {$this->getFQSEN()}::\${$property->getName()}"); } } return $property; }
/** * @param string $name * The name of the property * * @param Context $context * The context of the caller requesting the property * * @return Property * A property with the given name * * @throws IssueException * An exception may be thrown if the caller does not * have access to the given property from the given * context */ public function getPropertyByNameInContext(CodeBase $code_base, string $name, Context $context) : Property { // Check to see if we have the property if (!$code_base->hasProperty($this->getFQSEN(), $name)) { // If we don't have the property but do have a // __get method, then we can create the property if ($this->hasMethodWithName($code_base, '__get')) { $property = new Property($context, $name, new UnionType(), 0); $property->setFQSEN(FullyQualifiedPropertyName::make($this->getFQSEN(), $name)); $this->addProperty($code_base, $property); } else { throw new IssueException(Issue::fromType(Issue::UndeclaredProperty)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$name}}"])); } } $property = $code_base->getProperty($this->getFQSEN(), $name); // If we're getting the property from outside of this // class and the property isn't public and we don't // have a getter or setter, emit an access error if ((!$context->hasClassFQSEN() || $context->getClassFQSEN() != $this->getFQSEN()) && !$property->isPublic() && !$this->hasMethodWithName($code_base, '__get') && !$this->hasMethodWithName($code_base, '__set')) { if ($property->isPrivate()) { throw new IssueException(Issue::fromType(Issue::AccessPropertyPrivate)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$property->getName()}"])); } if ($property->isProtected()) { throw new IssueException(Issue::fromType(Issue::AccessPropertyProtected)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$property->getName()}"])); } } return $property; }
/** * @param string $name * The name of the property * * @param Context $context * The context of the caller requesting the property * * @return Property * A property with the given name * * @throws IssueException * An exception may be thrown if the caller does not * have access to the given property from the given * context */ public function getPropertyByNameInContext(CodeBase $code_base, string $name, Context $context) : Property { // Get the FQSEN of the property we're looking for $property_fqsen = FullyQualifiedPropertyName::make($this->getFQSEN(), $name); $property = null; // Figure out if we have the property $has_property = $code_base->hasPropertyWithFQSEN($property_fqsen); // Figure out if the property is accessible $is_property_accessible = false; if ($has_property) { $property = $code_base->getPropertyByFQSEN($property_fqsen); $is_remote_access = !$context->hasClassFQSEN() || $context->getClassFQSEN() != $this->getFQSEN(); $is_property_accessible = !$is_remote_access || $property->isPublic(); } // If the property exists and is accessible, return it if ($is_property_accessible) { return $property; } // Check to see if we can use a __get magic method if ($this->hasMethodWithName($code_base, '__get')) { $method = $this->getMethodByName($code_base, '__get'); // Make sure the magic method is accessible if ($method->isPrivate()) { throw new IssueException(Issue::fromType(Issue::AccessPropertyPrivate)($context->getFile(), $context->getLineNumberStart(), [(string) $property_fqsen])); } else { if ($method->isProtected()) { throw new IssueException(Issue::fromType(Issue::AccessPropertyProtected)($context->getFile(), $context->getLineNumberStart(), [(string) $property_fqsen])); } } $property = new Property($context, $name, $method->getUnionType(), 0); $property->setFQSEN($property_fqsen); $this->addProperty($code_base, $property); return $property; } else { if ($has_property) { // If we have a property, but its inaccessible, emit // an issue if ($property->isPrivate()) { throw new IssueException(Issue::fromType(Issue::AccessPropertyPrivate)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$property->getName()}"])); } if ($property->isProtected()) { throw new IssueException(Issue::fromType(Issue::AccessPropertyProtected)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$property->getName()}"])); } } } // Check to see if missing properties are allowed if (Config::get()->allow_missing_properties) { $property = new Property($context, $name, new UnionType(), 0); $property->setFQSEN($property_fqsen); $this->addProperty($code_base, $property); return $property; } throw new IssueException(Issue::fromType(Issue::UndeclaredProperty)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$name}}"])); }