/** * Check to see if the given Clazz is a duplicate * * @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 * * @param CodeBase $code_base * * @return null * * @see \Phan\Deprecated\Pass2::arg_check * Formerly `function arg_check` */ private static function analyzeInternalArgumentType(FunctionInterface $method, Node $node, Context $context, CodeBase $code_base) { $arglist = $node->children['args']; $argcount = count($arglist->children); switch ($method->getName()) { case 'join': case 'implode': // (string glue, array pieces), // (array pieces, string glue) or // (array pieces) if ($argcount == 1) { self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) { // "arg#1(pieces) is %s but {$method->getFQSEN()}() takes array when passed only 1 arg" return Issue::fromType(Issue::ParamSpecial2)($context->getFile(), $context->getLineNumberStart(), [1, 'pieces', (string) $method->getFQSEN(), 'string', 'array']); }); return; } elseif ($argcount == 2) { $arg1_type = UnionType::fromNode($context, $code_base, $arglist->children[0]); $arg2_type = UnionType::fromNode($context, $code_base, $arglist->children[1]); if ((string) $arg1_type == 'array') { if (!$arg1_type->canCastToUnionType(StringType::instance()->asUnionType())) { Issue::maybeEmit($code_base, $context, Issue::ParamSpecial1, $context->getLineNumberStart(), 2, 'glue', (string) $arg2_type, (string) $method->getFQSEN(), 'string', 1, 'array'); } } elseif ((string) $arg1_type == 'string') { if (!$arg2_type->canCastToUnionType(ArrayType::instance()->asUnionType())) { Issue::maybeEmit($code_base, $context, Issue::ParamSpecial1, $context->getLineNumberStart(), 2, 'pieces', (string) $arg2_type, (string) $method->getFQSEN(), 'array', 1, 'string'); } } return; } // Any other arg counts we will let the regular // checks handle break; case 'array_udiff': case 'array_diff_uassoc': case 'array_uintersect_assoc': case 'array_intersect_ukey': if ($argcount < 3) { Issue::maybeEmit($code_base, $context, Issue::ParamTooFewInternal, $context->getLineNumberStart(), $argcount, (string) $method->getFQSEN(), $method->getNumberOfRequiredParameters()); return; } self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) { // "The last argument to {$method->getFQSEN()} must be a callable" return Issue::fromType(Issue::ParamSpecial3)($context->getFile(), $context->getLineNumberStart(), [(string) $method->getFQSEN(), 'callable']); }); for ($i = 0; $i < $argcount - 1; $i++) { self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method, $i) { // "arg#".($i+1)." is %s but {$method->getFQSEN()}() takes array" return Issue::fromType(Issue::ParamTypeMismatch)($context->getFile(), $context->getLineNumberStart(), [$i + 1, (string) $node_type, (string) $method->getFQSEN(), 'array']); }); } return; case 'array_diff_uassoc': case 'array_uintersect_uassoc': if ($argcount < 4) { Issue::maybeEmit($code_base, $context, Issue::ParamTooFewInternal, $context->getLineNumberStart(), $argcount, (string) $method->getFQSEN(), $method->getNumberOfRequiredParameters()); return; } // The last 2 arguments must be a callable and there // can be a variable number of arrays before it self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) { // "The last argument to {$method->getFQSEN()} must be a callable" return Issue::fromType(Issue::ParamSpecial3)($context->getFile(), $context->getLineNumberStart(), [(string) $method->getFQSEN(), 'callable']); }); self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 2], $context, $code_base, CallableType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) { // "The second last argument to {$method->getFQSEN()} must be a callable" return Issue::fromType(Issue::ParamSpecial4)($context->getFile(), $context->getLineNumberStart(), [(string) $method->getFQSEN(), 'callable']); }); for ($i = 0; $i < $argcount - 2; $i++) { self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, ArrayType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method, $i) { // "arg#".($i+1)." is %s but {$method->getFQSEN()}() takes array" return Issue::fromType(Issue::ParamTypeMismatch)($context->getFile(), $context->getLineNumberStart(), [$i + 1, (string) $node_type, (string) $method->getFQSEN(), 'array']); }); } return; case 'strtok': // (string str, string token) or (string token) if ($argcount == 1) { // If we have just one arg it must be a string token self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, StringType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) { return Issue::fromType(Issue::ParamSpecial2)($context->getFile(), $context->getLineNumberStart(), [1, 'token', (string) $node_type, (string) $method->getFQSEN(), 'string']); }); } // The arginfo check will handle the other case break; case 'min': case 'max': if ($argcount == 1) { // If we have just one arg it must be an array if (!self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), function (UnionType $node_type) use($context, $method) { // "arg#1(values) is %s but {$method->getFQSEN()}() takes array when passed only one arg" return Issue::fromType(Issue::ParamSpecial2)($context->getFile(), $context->getLineNumberStart(), [1, 'values', (string) $node_type, (string) $method->getFQSEN(), 'array']); })) { return; } } // The arginfo check will handle the other case break; default: break; } }
/** * @return array * Get a map from column name to row values for * this instance */ public function toRow() : array { return ['scope_name' => $this->primaryKeyValue(), 'fqsen' => (string) $this->method->getFQSEN(), 'name' => (string) $this->method->getName(), 'type' => (string) $this->method->getUnionType(), 'flags' => $this->method->getFlags(), 'context' => base64_encode(serialize($this->method->getContext())), 'is_deprecated' => $this->method->isDeprecated(), 'number_of_required_parameters' => $this->method->getNumberOfRequiredParameters(), 'number_of_optional_parameters' => $this->method->getNumberOfOptionalParameters()]; }