public static isSelfTypeString ( string $type_string ) : boolean | ||
$type_string | string | A string defining a type such as 'self' or 'int'. |
Résultat | boolean | True if the given type references the class context in which it exists such as 'static' or 'self'. |
/** * @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::maybeEmit($code_base, $context, Issue::TypeNonVarPassByRef, $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::maybeEmit($code_base, $context, Issue::ContextNotObject, $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::maybeEmit($code_base, $context, Issue::TypeMismatchArgumentInternal, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type); } else { Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgument, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart()); } } } }
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(); }
/** * @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) { // There's nothing reasonable we can do here if ($method instanceof Method) { if ($method->getIsMagicCall() || $method->getIsMagicCallStatic()) { return; } } foreach ($node->children ?? [] as $i => $argument) { // Get the parameter associated with this argument $parameter = $method->getParameterForCaller($i); // 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::maybeEmit($code_base, $context, Issue::TypeNonVarPassByRef, $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::maybeEmit($code_base, $context, Issue::ContextNotObject, $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) { // Get the parameter associated with this argument $candidate_alternate_parameter = $alternate_method->getParameterForCaller($i); if (is_null($candidate_alternate_parameter)) { continue; } $alternate_parameter = $candidate_alternate_parameter; // See if the argument can be cast to the // parameter if ($argument_type_expanded->canCastToUnionType($alternate_parameter->getUnionType())) { $alternate_found = true; break; } } if (!$alternate_found) { $parameter_name = $alternate_parameter ? $alternate_parameter->getName() : 'unknown'; $parameter_type = $alternate_parameter ? $alternate_parameter->getUnionType() : 'unknown'; if (is_object($parameter_type) && $parameter_type->hasTemplateType()) { // Don't worry about template types } elseif ($method->isInternal()) { // If we are not in strict mode and we accept a string parameter // and the argument we are passing has a __toString method then it is ok if (!$context->getIsStrictTypes() && $parameter_type->hasType(StringType::instance())) { try { foreach ($argument_type_expanded->asClassList($code_base, $context) as $clazz) { if ($clazz->hasMethodWithName($code_base, "__toString")) { return; } } } catch (CodeBaseException $e) { // Swallow "Cannot find class", go on to emit issue } } Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgumentInternal, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type); } else { Issue::maybeEmit($code_base, $context, Issue::TypeMismatchArgument, $node->lineno ?? 0, $i + 1, $parameter_name, $argument_type_expanded, (string) $method->getFQSEN(), (string) $parameter_type, $method->getFileRef()->getFile(), $method->getFileRef()->getLineNumberStart()); } } } }