本文整理汇总了PHP中Phan\Language\Context::isInClassScope方法的典型用法代码示例。如果您正苦于以下问题:PHP Context::isInClassScope方法的具体用法?PHP Context::isInClassScope怎么用?PHP Context::isInClassScope使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类Phan\Language\Context
的用法示例。
在下文中一共展示了Context::isInClassScope方法的13个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的PHP代码示例。
示例1: visitProp
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitProp(Node $node) : string
{
if (!($node->children['expr']->kind == \ast\AST_VAR && !$node->children['expr']->children['name'] instanceof Node)) {
return '';
}
// $var->prop->method()
$var = $node->children['expr'];
$class = null;
if ($var->children['name'] == 'this') {
// If we're not in a class scope, 'this' won't work
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
return '';
}
// $this->$node->method()
if ($node->children['prop'] instanceof Node) {
// Too hard. Giving up.
return '';
}
$class = $this->context->getClassInScope($this->code_base);
} else {
// Get the list of viable class types for the
// variable
$union_type = AST::varUnionType($this->context, $var)->nonNativeTypes()->nonGenericArrayTypes();
if ($union_type->isEmpty()) {
return '';
}
$class_fqsen = $union_type->head()->asFQSEN();
if (!$this->code_base->hasClassWithFQSEN($class_fqsen)) {
return '';
}
$class = $this->code_base->getClassByFQSEN($class_fqsen);
}
$property_name = $node->children['prop'];
if (!$class->hasPropertyWithName($this->code_base, $property_name)) {
// If we can't find the property, there's
// no type. Thie issue should be caught
// elsewhere.
return '';
}
try {
$property = $class->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
} catch (AccessException $exception) {
Log::err(Log::EACCESS, $exception->getMessage(), $this->context->getFile(), $node->lineno);
return '';
}
$union_type = $property->getUnionType()->nonNativeTypes();
if ($union_type->isEmpty()) {
// If we don't have a type on the property we
// can't figure out the class type.
return '';
} else {
// Return the first type on the property
// that could be a reference to a class
return (string) $union_type->head()->asFQSEN();
}
// No such property was found, or none were classes
// that could be found
return '';
}
示例2: visitNew
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitNew(Node $node) : string
{
// Things of the form `new $class_name();`
if ($node->children['class']->kind == \ast\AST_VAR) {
return '';
}
// Things of the form `new $method->name()`
if ($node->children['class']->kind !== \ast\AST_NAME) {
return '';
}
$class_name = $node->children['class']->children['name'];
if (!in_array($class_name, ['self', 'static', 'parent'])) {
return AST::qualifiedName($this->context, $node->children['class']);
}
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, "Cannot access {$class_name}:: when no class scope is active", $this->context->getFile(), $node->lineno);
return '';
}
if ($class_name == 'static') {
return (string) $this->context->getClassFQSEN();
}
if ($class_name == 'self') {
if ($this->context->isGlobalScope()) {
assert(false, "Unimplemented branch is required for {$this->context}");
} else {
return (string) $this->context->getClassFQSEN();
}
}
if ($class_name == 'parent') {
$clazz = $this->context->getClassInScope($this->code_base);
if (!$clazz->hasParentClassFQSEN()) {
return '';
}
return (string) $clazz->getParentClassFQSEN();
}
return '';
}
示例3: visitReturn
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitReturn(Node $node) : Context
{
// Don't check return types in traits
if ($this->context->isInClassScope()) {
$clazz = $this->context->getClassInScope($this->code_base);
if ($clazz->isTrait()) {
return $this->context;
}
}
// Make sure we're actually returning from a method.
if (!$this->context->isMethodScope() && !$this->context->isClosureScope()) {
return $this->context;
}
// Get the method/function/closure we're in
$method = null;
if ($this->context->isClosureScope()) {
$method = $this->context->getClosureInScope($this->code_base);
} else {
if ($this->context->isMethodScope()) {
$method = $this->context->getMethodInScope($this->code_base);
}
}
assert(!empty($method), "We're supposed to be in either method or closure scope.");
// Figure out what we intend to return
$method_return_type = $method->getUnionType();
// Figure out what is actually being returned
$expression_type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
// If there is no declared type, see if we can deduce
// what it should be based on the return type
if ($method_return_type->isEmpty() || $method->isReturnTypeUndefined()) {
$method->setIsReturnTypeUndefined(true);
// Set the inferred type of the method based
// on what we're returning
$method->getUnionType()->addUnionType($expression_type);
// No point in comparing this type to the
// type we just set
return $this->context;
}
if (!$method->isReturnTypeUndefined() && !$expression_type->canCastToExpandedUnionType($method_return_type, $this->code_base)) {
Issue::emit(Issue::TypeMismatchReturn, $this->context->getFile(), $node->lineno ?? 0, (string) $expression_type, $method->getName(), (string) $method_return_type);
}
if ($method->isReturnTypeUndefined()) {
// Add the new type to the set of values returned by the
// method
$method->getUnionType()->addUnionType($expression_type);
}
return $this->context;
}
示例4: visitClassNode
private function visitClassNode(Node $node) : UnionType
{
// Things of the form `new $class_name();`
if ($node->kind == \ast\AST_VAR) {
return new UnionType();
}
// Anonymous class of form `new class { ... }`
if ($node->kind == \ast\AST_CLASS && $node->flags & \ast\flags\CLASS_ANONYMOUS) {
// Generate a stable name for the anonymous class
$anonymous_class_name = (new ContextNode($this->code_base, $this->context, $node))->getUnqualifiedNameForAnonymousClass();
// Turn that into a fully qualified name
$fqsen = FullyQualifiedClassName::fromStringInContext($anonymous_class_name, $this->context);
// Turn that into a union type
return Type::fromFullyQualifiedString((string) $fqsen)->asUnionType();
}
// Things of the form `new $method->name()`
if ($node->kind !== \ast\AST_NAME) {
return new UnionType();
}
// Get the name of the class
$class_name = $node->children['name'];
// If this is a straight-forward class name, recurse into the
// class node and get its type
if (!Type::isSelfTypeString($class_name)) {
// TODO: does anyone else call this method?
return self::unionTypeFromClassNode($this->code_base, $this->context, $node);
}
// This is a self-referential node
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, "Cannot access {$class_name} when not in a class scope", $this->context->getFile(), $node->lineno);
return new UnionType();
}
// Reference to a parent class
if ($class_name === 'parent') {
$class = $this->context->getClassInScope($this->code_base);
if (!$class->hasParentClassFQSEN()) {
Log::err(Log::ESTATIC, "Reference to parent of parentless class {$class->getFQSEN()}", $this->context->getFile(), $node->lineno);
return new UnionType();
}
return Type::fromFullyQualifiedString((string) $class->getParentClassFQSEN())->asUnionType();
}
return Type::fromFullyQualifiedString((string) $this->context->getClassFQSEN())->asUnionType();
}
示例5: visitReturn
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitReturn(Node $node) : Context
{
// Don't check return types in traits
if ($this->context->isInClassScope()) {
$clazz = $this->context->getClassInScope($this->code_base);
if ($clazz->isTrait()) {
return $this->context;
}
}
// Make sure we're actually returning from a method.
if (!$this->context->isMethodScope() && !$this->context->isClosureScope()) {
return $this->context;
}
// Get the method/function/closure we're in
$method = null;
if ($this->context->isClosureScope()) {
$method = $this->context->getClosureInScope($this->code_base);
} else {
if ($this->context->isMethodScope()) {
$method = $this->context->getMethodInScope($this->code_base);
} else {
assert(false, "We're supposed to be in either method or closure scope.");
}
}
// Figure out what we intend to return
$method_return_type = $method->getUnionType();
// Figure out what is actually being returned
$expression_type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
// If there is no declared type, see if we can deduce
// what it should be based on the return type
if ($method_return_type->isEmpty()) {
// Set the inferred type of the method based
// on what we're returning
$method->getUnionType()->addUnionType($expression_type);
// No point in comparing this type to the
// type we just set
return $this->context;
}
if (!$expression_type->canCastToExpandedUnionType($method_return_type, $this->code_base)) {
Log::err(Log::ETYPE, "return {$expression_type} but {$method->getName()}() is declared to return {$method_return_type}", $this->context->getFile(), $node->lineno);
}
return $this->context;
}
示例6: visitNew
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitNew(Node $node) : string
{
// Things of the form `new $class_name();`
if ($node->children['class']->kind == \ast\AST_VAR) {
return '';
}
// Anonymous class
// $v = new class { ... }
if ($node->children['class']->kind == \ast\AST_CLASS && $node->children['class']->flags & \ast\flags\CLASS_ANONYMOUS) {
return (new ContextNode($this->code_base, $this->context, $node->children['class']))->getUnqualifiedNameForAnonymousClass();
}
// Things of the form `new $method->name()`
if ($node->children['class']->kind !== \ast\AST_NAME) {
return '';
}
$class_name = $node->children['class']->children['name'];
if (!in_array($class_name, ['self', 'static', 'parent'])) {
return (string) UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $node->children['class']);
}
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, "Cannot access {$class_name}:: when no class scope is active", $this->context->getFile(), $node->lineno);
return '';
}
if ($class_name == 'static') {
return (string) $this->context->getClassFQSEN();
}
if ($class_name == 'self') {
if ($this->context->isGlobalScope()) {
assert(false, "Unimplemented branch is required for {$this->context}");
} else {
return (string) $this->context->getClassFQSEN();
}
}
if ($class_name == 'parent') {
$clazz = $this->context->getClassInScope($this->code_base);
if (!$clazz->hasParentClassFQSEN()) {
return '';
}
return (string) $clazz->getParentClassFQSEN();
}
return '';
}
示例7: fromStringInContext
/**
* @param string $string
* A string representing a type
*
* @param Context $context
* The context in which the type string was
* found
*
* @return Type
* Parse a type from the given string
*/
public static function fromStringInContext(string $string, Context $context) : Type
{
assert($string !== '', "Type cannot be empty in {$context}");
$namespace = null;
// Extract the namespace if the type string is
// fully-qualified
if ('\\' === $string[0]) {
list($namespace, $string) = self::namespaceAndTypeFromString($string);
}
$type_name = $string;
// @var bool
// True if this type name if of the form 'C[]'
$is_generic_array_type = self::isGenericArrayString($type_name);
// If this is a generic array type, get the name of
// the type of each element
$non_generic_array_type_name = $type_name;
if ($is_generic_array_type && false !== ($pos = strpos($type_name, '[]'))) {
$non_generic_array_type_name = substr($type_name, 0, $pos);
}
// Check to see if the type name is mapped via
// a using clause.
//
// Gotta check this before checking for native types
// because there are monsters out there that will
// remap the names via things like `use \Foo\String`.
if ($context->hasNamespaceMapFor(T_CLASS, $non_generic_array_type_name)) {
$fqsen = $context->getNamespaceMapFor(T_CLASS, $non_generic_array_type_name);
if ($is_generic_array_type) {
return GenericArrayType::fromElementType(Type::make($fqsen->getNamespace(), $fqsen->getName()));
}
return Type::make($fqsen->getNamespace(), $fqsen->getName());
}
// If this was a fully qualified type, we're all
// set
if (!empty($namespace)) {
return self::fromNamespaceAndName($namespace, $type_name);
}
if ($is_generic_array_type && self::isNativeTypeString($type_name)) {
return self::fromInternalTypeName($type_name);
} else {
// Check to see if its a builtin type
switch (self::canonicalNameFromName($type_name)) {
case 'array':
return \Phan\Language\Type\ArrayType::instance();
case 'bool':
return \Phan\Language\Type\BoolType::instance();
case 'callable':
return \Phan\Language\Type\CallableType::instance();
case 'float':
return \Phan\Language\Type\FloatType::instance();
case 'int':
return \Phan\Language\Type\IntType::instance();
case 'mixed':
return \Phan\Language\Type\MixedType::instance();
case 'null':
return \Phan\Language\Type\NullType::instance();
case 'object':
return \Phan\Language\Type\ObjectType::instance();
case 'resource':
return \Phan\Language\Type\ResourceType::instance();
case 'string':
return \Phan\Language\Type\StringType::instance();
case 'void':
return \Phan\Language\Type\VoidType::instance();
}
}
// Things like `self[]` or `$this[]`
if ($is_generic_array_type && self::isSelfTypeString($non_generic_array_type_name) && $context->isInClassScope()) {
// Callers of this method should be checking on their own
// to see if this type is a reference to 'parent' and
// dealing with it there. We don't want to have this
// method be dependent on the code base
assert('parent' !== $non_generic_array_type_name, __METHOD__ . " does not know how to handle the type name 'parent' in {$context}");
return GenericArrayType::fromElementType(static::fromFullyQualifiedString((string) $context->getClassFQSEN()));
}
// If this is a type referencing the current class
// in scope such as 'self' or 'static', return that.
if (self::isSelfTypeString($type_name) && $context->isInClassScope()) {
// Callers of this method should be checking on their own
// to see if this type is a reference to 'parent' and
// dealing with it there. We don't want to have this
// method be dependent on the code base
assert('parent' !== $type_name, __METHOD__ . " does not know how to handle the type name 'parent' in {$context}");
return static::fromFullyQualifiedString((string) $context->getClassFQSEN());
}
// Attach the context's namespace to the type name
return self::fromNamespaceAndName($context->getNamespace() ?: '\\', $type_name);
}
示例8: fromStringInContext
/**
* @param Context $context
* The context in which the FQSEN string was found
*
* @param $fqsen_string
* An FQSEN string like '\Namespace\Class::methodName'
*
* @return FullyQualifiedMethodName
*/
public static function fromStringInContext(string $fqsen_string, Context $context)
{
// Test to see if we have a class defined
if (false === strpos($fqsen_string, '::')) {
assert($context->isInClassScope(), "Cannot reference class element without class name when not in class scope.");
$fully_qualified_class_name = $context->getClassFQSEN();
} else {
assert(false !== strpos($fqsen_string, '::'), "Fully qualified class element lacks '::' delimiter");
list($class_name_string, $fqsen_string) = explode('::', $fqsen_string);
$fully_qualified_class_name = FullyQualifiedClassName::fromStringInContext($class_name_string, $context);
}
// Split off the alternate ID
$parts = explode(',', $fqsen_string);
$name = $parts[0];
$alternate_id = (int) ($parts[1] ?? 0);
assert(is_int($alternate_id), "Alternate must be an integer");
return static::make($fully_qualified_class_name, $name, $alternate_id);
}
示例9: fromStringInContext
/**
* @param string $string
* A string representing a type
*
* @param Context $context
* The context in which the type string was
* found
*
* @return Type
* Parse a type from the given string
*/
public static function fromStringInContext(string $string, Context $context) : Type
{
assert($string !== '', "Type cannot be empty in {$context}");
$namespace = null;
// Extract the namespace if the type string is
// fully-qualified
if ('\\' === $string[0]) {
list($namespace, $string) = self::namespaceAndTypeFromString($string);
}
$type_name = strtolower($string);
// Check to see if the type name is mapped via
// a using clause.
//
// Gotta check this before checking for native types
// because there are monsters out there that will
// remap the names via things like `use \Foo\String`.
if ($context->hasNamespaceMapFor(T_CLASS, $type_name)) {
$fqsen = $context->getNamespaceMapFor(T_CLASS, $type_name);
return new Type($fqsen->getNamespace(), $fqsen->getName());
}
// If this was a fully qualified type, we're all
// set
if (!empty($namespace)) {
return self::fromNamespaceAndName($namespace, $type_name);
}
// Check to see if its a builtin type
switch (self::canonicalNameFromName($type_name)) {
case 'array':
return \Phan\Language\Type\ArrayType::instance();
case 'bool':
return \Phan\Language\Type\BoolType::instance();
case 'callable':
return \Phan\Language\Type\CallableType::instance();
case 'float':
return \Phan\Language\Type\FloatType::instance();
case 'int':
return \Phan\Language\Type\IntType::instance();
case 'mixed':
return \Phan\Language\Type\MixedType::instance();
case 'null':
return \Phan\Language\Type\NullType::instance();
case 'object':
return \Phan\Language\Type\ObjectType::instance();
case 'resource':
return \Phan\Language\Type\ResourceType::instance();
case 'string':
return \Phan\Language\Type\StringType::instance();
case 'void':
return \Phan\Language\Type\VoidType::instance();
}
// If this is a type referencing the current class
// in scope such as 'self' or 'static', return that.
if (self::isSelfTypeString($type_name) && $context->isInClassScope()) {
return static::fromFullyQualifiedString((string) $context->getClassFQSEN());
}
// Attach the context's namespace to the type name
return self::fromNamespaceAndName($context->getNamespace() ?: '\\', $type_name);
}
示例10: visitMethodCall
/**
* @param Node $node
* A node of the type indicated by the method name that we'd
* like to figure out the type that it produces.
*
* @return string
* The class name represented by the given call
*/
public function visitMethodCall(Node $node) : string
{
if ($node->children['expr']->kind == \ast\AST_VAR) {
if ($node->children['expr']->children['name'] instanceof Node) {
return '';
}
// $var->method()
if ($node->children['expr']->children['name'] == 'this') {
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
return '';
}
return (string) $this->context->getClassFQSEN();
}
$variable_name = $node->children['expr']->children['name'];
if (!$this->context->getScope()->hasVariableWithName($variable_name)) {
// Got lost, couldn't find the variable in the current scope
// If it really isn't defined, it will be caught by the
// undefined var error
return '';
}
$variable = $this->context->getScope()->getVariableWithName($variable_name);
// Hack - loop through the possible types of the var and assume
// first found class is correct
foreach ($variable->getUnionType()->nonGenericArrayTypes()->getTypeList() as $type) {
$child_class_fqsen = FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
if ($this->code_base->hasClassWithFQSEN($child_class_fqsen)) {
return (string) FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
}
}
// Could not find name
return '';
}
if ($node->children['expr']->kind == \ast\AST_PROP) {
$prop = $node->children['expr'];
if (!($prop->children['expr']->kind == \ast\AST_VAR && !$prop->children['expr']->children['name'] instanceof Node)) {
return '';
}
// $var->prop->method()
$var = $prop->children['expr'];
if ($var->children['name'] == 'this') {
// If we're not in a class scope, 'this' won't work
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
return '';
}
// Get the class in scope
$clazz = $this->code_base->getClassByFQSEN($this->context->getClassFQSEN());
if ($prop->children['prop'] instanceof Node) {
// $this->$prop->method() - too dynamic, give up
return '';
}
$property_name = $prop->children['prop'];
if ($clazz->hasPropertyWithName($this->code_base, $property_name)) {
try {
$property = $clazz->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
} catch (AccessException $exception) {
Log::err(Log::EACCESS, $exception->getMessage(), $this->context->getFile(), $node->lineno);
return '';
}
// Find the first viable property type
foreach ($property->getUnionType()->nonGenericArrayTypes()->getTypeList() as $type) {
$class_fqsen = FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
if ($this->code_base->hasClassWithFQSEN($class_fqsen)) {
return (string) $class_fqsen;
}
}
}
// No such property was found, or none were classes
// that could be found
return '';
}
return '';
}
if ($node->children['expr']->kind == \ast\AST_METHOD_CALL) {
// Get the type returned by the first method
// call.
$union_type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
// Find the subset of types that are viable
// classes
$viable_class_types = $union_type->nonNativeTypes()->nonGenericArrayTypes();
// If there are no non-native types, give up
if ($viable_class_types->isEmpty()) {
return '';
}
// Return the first non-native type in the
// list and hope its a class
return (string) $viable_class_types->head();
}
return '';
}
示例11: getPropertyByNameInContext
/**
* @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}}"]));
}
示例12: asClassList
/**
* @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));
}
}
}
示例13: analyzeParameterList
/**
* @param CodeBase $code_base
* The global code base
*
* @param FunctionInterface $method
* The method we're analyzing arguments for
*
* @param Node $node
* The node holding the method call we're looking at
*
* @param Context $context
* The context in which we see the call
*
* @return null
*
* @see \Phan\Deprecated\Pass2::arglist_type_check
* Formerly `function arglist_type_check`
*/
private static function analyzeParameterList(CodeBase $code_base, FunctionInterface $method, Node $node, Context $context)
{
foreach ($node->children ?? [] as $i => $argument) {
// Get the parameter associated with this argument
$parameter = $method->getParameterList()[$i] ?? null;
// This issue should be caught elsewhere
if (!$parameter) {
continue;
}
// If this is a pass-by-reference parameter, make sure
// we're passing an allowable argument
if ($parameter->isPassByReference()) {
if (!$argument instanceof \ast\Node || $argument->kind != \ast\AST_VAR && $argument->kind != \ast\AST_DIM && $argument->kind != \ast\AST_PROP && $argument->kind != \ast\AST_STATIC_PROP) {
Issue::emit(Issue::TypeNonVarPassByRef, $context->getFile(), $node->lineno ?? 0, $i + 1, (string) $method->getFQSEN());
} else {
$variable_name = (new ContextNode($code_base, $context, $argument))->getVariableName();
if (Type::isSelfTypeString($variable_name) && !$context->isInClassScope() && $argument->kind == \ast\AST_STATIC_PROP && $argument->kind == \ast\AST_PROP) {
Issue::emit(Issue::ContextNotObject, $context->getFile(), $node->lineno ?? 0, "{$variable_name}");
}
}
}
// Get the type of the argument. We'll check it against
// the parameter in a moment
$argument_type = UnionType::fromNode($context, $code_base, $argument);
// Expand it to include all parent types up the chain
$argument_type_expanded = $argument_type->asExpandedTypes($code_base);
// Check the method to see if it has the correct
// parameter types. If not, keep hunting through
// alternates of the method until we find one that
// takes the correct types
$alternate_parameter = null;
$alternate_found = false;
foreach ($method->alternateGenerator($code_base) as $alternate_id => $alternate_method) {
if (empty($alternate_method->getParameterList()[$i])) {
continue;
}
// Get the parameter associated with this argument
$alternate_parameter = $alternate_method->getParameterList()[$i] ?? null;
// Expand the types to find all parents and traits
$alternate_parameter_type_expanded = $alternate_parameter->getUnionType()->asExpandedTypes($code_base);
// See if the argument can be cast to the
// parameter
if ($argument_type_expanded->canCastToUnionType($alternate_parameter_type_expanded)) {
$alternate_found = true;
break;
}
}
if (!$alternate_found) {
$parameter_name = $alternate_parameter ? $alternate_parameter->getName() : 'unknown';
$parameter_type = $alternate_parameter ? $alternate_parameter->getUnionType() : 'unknown';
if ($method->isInternal()) {
Issue::emit(Issue::TypeMismatchArgumentInternal, $context->getFile(), $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type);
} else {
Issue::emit(Issue::TypeMismatchArgument, $context->getFile(), $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart());
}
}
}
}