A list of types for parameters associated with the
given builtin function with the given name
public static internalFunctionSignatureMapForFQSEN ( |
||
$function_fqsen | ||
return | array |
/** * @param FunctionInterface $function * Get a list of methods hydrated with type information * for the given partial method * * @param CodeBase $code_base * The global code base holding all state * * @return Method[] * A list of typed methods based on the given method */ private static function functionListFromFunction(FunctionInterface $function, CodeBase $code_base) : array { // See if we have any type information for this // internal function $map_list = UnionType::internalFunctionSignatureMapForFQSEN($function->getFQSEN()); if (!$map_list) { return [$function]; } $alternate_id = 0; return array_map(function ($map) use($function, &$alternate_id) : FunctionInterface { $alternate_function = clone $function; $alternate_function->setFQSEN($alternate_function->getFQSEN()->withAlternateId($alternate_id++)); // Set the return type if one is defined if (!empty($map['return_type'])) { $alternate_function->setUnionType($map['return_type']); } // Load properties if defined foreach ($map['property_name_type_map'] ?? [] as $parameter_name => $parameter_type) { $flags = 0; $is_optional = false; // Check to see if its a pass-by-reference parameter if (strpos($parameter_name, '&') === 0) { $flags |= \ast\flags\PARAM_REF; $parameter_name = substr($parameter_name, 1); } // Check to see if its variadic if (strpos($parameter_name, '...') !== false) { $flags |= \ast\flags\PARAM_VARIADIC; $parameter_name = str_replace('...', '', $parameter_name); } // Check to see if its an optional parameter if (strpos($parameter_name, '=') !== false) { $is_optional = true; $parameter_name = str_replace('=', '', $parameter_name); } $parameter = new Parameter($function->getContext(), $parameter_name, $parameter_type, $flags); if ($is_optional) { $parameter->setDefaultValueType(NullType::instance()->asUnionType()); } // Add the parameter $alternate_function->appendParameter($parameter); } $alternate_function->setNumberOfRequiredParameters(array_reduce($alternate_function->getParameterList(), function (int $carry, Parameter $parameter) : int { return $carry + ($parameter->isOptional() ? 0 : 1); }, 0)); $alternate_function->setNumberOfOptionalParameters(count($alternate_function->getParameterList()) - $alternate_function->getNumberOfRequiredParameters()); if ($alternate_function instanceof Method) { if ($alternate_function->getIsMagicCall() || $alternate_function->getIsMagicCallStatic()) { $alternate_function->setNumberOfOptionalParameters(999); $alternate_function->setNumberOfRequiredParameters(0); } } return $alternate_function; }, $map_list); }
/** * @param Method $method * Get a list of methods hydrated with type information * for the given partial method * * @param CodeBase $code_base * The global code base holding all state * * @return Method[] * A list of typed methods based on the given method */ private static function methodListFromMethod(Method $method, CodeBase $code_base) : array { // See if we have any type information for this // internal function $map_list = UnionType::internalFunctionSignatureMapForFQSEN($method->getFQSEN()); if (!$map_list) { return [$method]; } $alternate_id = 0; return array_map(function ($map) use($method, &$alternate_id) : Method { $alternate_method = clone $method; $alternate_method->setFQSEN($alternate_method->getFQSEN()->withAlternateId($alternate_id++)); // Set the return type if one is defined if (!empty($map['return_type'])) { $alternate_method->setUnionType($map['return_type']); } // Load properties if defined foreach ($map['property_name_type_map'] ?? [] as $parameter_name => $parameter_type) { $flags = 0; $is_optional = false; // Check to see if its a pass-by-reference parameter if (strpos($parameter_name, '&') === 0) { $flags |= \ast\flags\PARAM_REF; $parameter_name = substr($parameter_name, 1); } // Check to see if its variadic if (strpos($parameter_name, '...') !== false) { $flags |= \ast\flags\PARAM_VARIADIC; $parameter_name = str_replace('...', '', $parameter_name); } // Check to see if its an optional parameter if (strpos($parameter_name, '=') !== false) { $is_optional = true; $parameter_name = str_replace('=', '', $parameter_name); } $parameter = new Parameter($method->getContext(), $parameter_name, $parameter_type, $flags); if ($is_optional) { $parameter->setDefaultValue(null, NullType::instance()->asUnionType()); } // Add the parameter $alternate_method->parameter_list[] = $parameter; } return $alternate_method; }, $map_list); }
/** * Visit a node with kind `\ast\AST_CALL` * * @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 UnionType * The set of types that are possibly produced by the * given node */ public function visitCall(Node $node) : UnionType { if ($node->children['expr']->kind !== \ast\AST_NAME) { // Things like `$func()` return new UnionType(); } $function_name = $node->children['expr']->children['name']; try { $function = (new ContextNode($this->code_base, $this->context, $node->children['expr']))->getFunction($function_name); } catch (CodeBaseException $exception) { // If the function wasn't declared, it'll be caught // and reported elsewhere return new UnionType(); } $function_fqsen = $function->getFQSEN(); // TODO: I don't believe we need this any more // If this is an internal function, see if we can get // its types from the static dataset. if ($function->getContext()->isInternal() && $function->getUnionType()->isEmpty()) { $map = UnionType::internalFunctionSignatureMapForFQSEN($function->getFQSEN()); return $map[$function_name] ?? new UnionType(); } return $function->getUnionType(); }
/** * Visit a node with kind `\ast\AST_CALL` * * @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 UnionType * The set of types that are possibly produced by the * given node */ public function visitCall(Node $node) : UnionType { if ($node->children['expr']->kind !== \ast\AST_NAME) { // Things like `$func()` return new UnionType(); } $function_name = $node->children['expr']->children['name']; $function_fqsen = null; // If its not fully qualified if ($node->children['expr']->flags & \ast\flags\NAME_NOT_FQ) { // Check to see if we have a mapped name if ($this->context->hasNamespaceMapFor(T_FUNCTION, $function_name)) { $function_fqsen = $this->context->getNamespaceMapFor(T_FUNCTION, $function_name); } else { $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $this->context); } // If the name is fully qualified } else { $function_fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString($function_name); } // If the function doesn't exist, check to see if its // a call to a builtin method if (!$this->code_base->hasMethod($function_fqsen)) { $function_fqsen = FullyQualifiedFunctionName::make('', $function_name); } if (!$this->code_base->hasMethod($function_fqsen)) { // Missing internal (bulitin) method. return new UnionType(); } $function = $this->code_base->getMethod($function_fqsen); // If this is an internal function, see if we can get // its types from the static dataset. if ($function->getContext()->isInternal() && $function->getUnionType()->isEmpty()) { $map = UnionType::internalFunctionSignatureMapForFQSEN($function_fqsen); return $map[$function_name] ?? new UnionType(); } return $function->getUnionType(); }
/** * Visit a node with kind `\ast\AST_CALL` * * @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 UnionType * The set of types that are possibly produced by the * given node */ public function visitCall(Node $node) : UnionType { if ($node->children['expr']->kind !== \ast\AST_NAME) { // Things like `$func()` return new UnionType(); } $function_name = $node->children['expr']->children['name']; $function = AST::functionFromNameInContext($function_name, $this->context, $this->code_base); $function_fqsen = $function->getFQSEN(); // If this is an internal function, see if we can get // its types from the static dataset. if ($function->getContext()->isInternal() && $function->getUnionType()->isEmpty()) { $map = UnionType::internalFunctionSignatureMapForFQSEN($function->getFQSEN()); return $map[$function_name] ?? new UnionType(); } return $function->getUnionType(); }