/** * @return string * A unique and stable name for an anonymous class */ public function getUnqualifiedNameForAnonymousClass() : string { assert((bool) ($this->node->flags & \ast\flags\CLASS_ANONYMOUS), "Node must be an anonymous class node"); $class_name = 'anonymous_class_' . substr(md5(implode('|', [$this->context->getFile(), $this->context->getLineNumberStart()])), 0, 8); return $class_name; }
public static function fromClosureInContext(Context $context) : FullyQualifiedFunctionName { $name = 'closure_' . substr(md5(implode('|', [$context->getFile(), $context->getLineNumberStart()])), 0, 12); return static::fromStringInContext($name, $context); }
/** * Emit an issue if it is not suppressed * * @param CodeBase $code_base * The code base in which the issue was found * * @param Context $context * The context in which the issue was found * * @param string $issue_type * A name for the type of issue such as 'PhanPluginMyIssue' * * @param string $issue_message * The complete issue message to emit such as 'class with * fqsen \NS\Name is broken in some fashion'. * * @param int $severity * A value from the set {Issue::SEVERITY_LOW, * Issue::SEVERITY_NORMAL, Issue::SEVERITY_HIGH}. * * @param int $remediation_difficulty * A guess at how hard the issue will be to fix from the * set {Issue:REMEDIATION_A, Issue:REMEDIATION_B, ... * Issue::REMEDIATION_F} with F being the hardest. */ public function emitIssue(CodeBase $code_base, Context $context, string $issue_type, string $issue_message, int $severity = Issue::SEVERITY_NORMAL, int $remediation_difficulty = Issue::REMEDIATION_B, int $issue_type_id = Issue::TYPE_ID_UNKNOWN) { $issue = new Issue($issue_type, Issue::CATEGORY_PLUGIN, $severity, $issue_message, $remediation_difficulty, $issue_type_id); $issue_instance = new IssueInstance($issue, $context->getFile(), $context->getLineNumberStart(), []); Issue::maybeEmitInstance($code_base, $context, $issue_instance); }
/** * @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->isInClassScope() || !$context->getClassInScope($code_base)->getUnionType()->canCastToExpandedUnionType($this->getUnionType(), $code_base); $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_fqsen); $this->addProperty($code_base, $property, new None()); 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 // or we're stdclass if (Config::get()->allow_missing_properties || $this->getFQSEN() == FullyQualifiedClassName::getStdClassFQSEN()) { $property = new Property($context, $name, new UnionType(), 0, $property_fqsen); $this->addProperty($code_base, $property, new None()); return $property; } throw new IssueException(Issue::fromType(Issue::UndeclaredProperty)($context->getFile(), $context->getLineNumberStart(), ["{$this->getFQSEN()}::\${$name}}"])); }
/** * @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 CodeBase $code_base * The code base in which to find classes * * @param Context $context * The context in which we're resolving this union * type. * * @return Clazz[] * A list of classes representing the non-native types * associated with this UnionType * * @throws CodeBaseException * An exception is thrown if a non-native type does not have * an associated class */ public function asClassList(CodeBase $code_base, Context $context) { // Iterate over each viable class type to see if any // have the constant we're looking for foreach ($this->nonNativeTypes()->getTypeSet() as $class_type) { // Get the class FQSEN $class_fqsen = $class_type->asFQSEN(); if ($class_type->isStaticType()) { if (!$context->isInClassScope()) { throw new IssueException(Issue::fromType(Issue::ContextNotObject)($context->getFile(), $context->getLineNumberStart(), [(string) $class_type])); } (yield $context->getClassInScope($code_base)); } else { // See if the class exists if (!$code_base->hasClassWithFQSEN($class_fqsen)) { throw new CodeBaseException($class_fqsen, "Cannot find class {$class_fqsen}"); } (yield $code_base->getClassByFQSEN($class_fqsen)); } } }