public static fromStringInContext ( string $fqsen_string, |
||
$fqsen_string | string | An FQSEN string like '\Namespace\Class' |
$context | The context in which the FQSEN string was found | |
return |
public function testFullyQualifiedFunctionName() { $this->assertFQSENEqual(FullyQualifiedFunctionName::make('\\Name\\Space', 'g'), '\\Name\\Space\\g'); $this->assertFQSENEqual(FullyQualifiedFunctionName::make('', 'g'), '\\g'); $this->assertFQSENEqual(FullyQualifiedGlobalConstantName::make('', 'g'), '\\g'); $this->assertFQSENEqual(FullyQualifiedFunctionName::fromFullyQualifiedString('\\g'), '\\g'); $this->assertFQSENEqual(FullyQualifiedFunctionName::fromStringInContext('g', $this->context), '\\g'); }
/** * Visit a node with kind `\ast\AST_FUNC_DECL` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitFuncDecl(Decl $node) : Context { $function_name = (string) $node->name; // Hunt for an un-taken alternate ID $alternate_id = 0; $function_fqsen = null; do { $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $this->context)->withNamespace($this->context->getNamespace())->withAlternateId($alternate_id++); } while ($this->code_base->hasFunctionWithFQSEN($function_fqsen)); $func = Func::fromNode($this->context->withLineNumberStart($node->lineno ?? 0)->withLineNumberEnd($node->endLineno ?? 0), $this->code_base, $node); $func->setFQSEN($function_fqsen); $this->code_base->addFunction($func); // Send the context into the function and reset the scope $context = $this->context->withMethodFQSEN($function_fqsen)->withScope(new Scope()); // Add each method parameter to the scope. We clone it // so that changes to the variable don't alter the // parameter definition foreach ($func->getParameterList() as $parameter) { $context->addScopeVariable(clone $parameter); } return $context; }
/** * Visit a node with kind `\ast\AST_FUNC_DECL` * * @param Node $node * A node to parse * * @return Context * A new or an unchanged context resulting from * parsing the node */ public function visitFuncDecl(Decl $node) : Context { $function_name = (string) $node->name; // Hunt for an un-taken alternate ID $alternate_id = 0; $function_fqsen = null; do { $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $this->context)->withNamespace($this->context->getNamespace())->withAlternateId($alternate_id++); } while ($this->code_base->hasFunctionWithFQSEN($function_fqsen)); $func = Func::fromNode($this->context->withLineNumberStart($node->lineno ?? 0)->withLineNumberEnd($node->endLineno ?? 0), $this->code_base, $node, $function_fqsen); $this->code_base->addFunction($func); // Send the context into the function and reset the scope $context = $this->context->withScope($func->getInternalScope()); return $context; }
/** * @param string $function_name * The name of the function we'd like to look up * * @param bool $is_function_declaration * This must be set to true if we're getting a function * that is being declared and false if we're getting a * function being called. * * @return FunctionInterface * A method with the given name in the given context * * @throws IssueException * An exception is thrown if we can't find the given * function */ public function getFunction(string $function_name, bool $is_function_declaration = false) : FunctionInterface { if ($is_function_declaration) { $function_fqsen = FullyQualifiedFunctionName::make($this->context->getNamespace(), $function_name); } else { $function_fqsen = FullyQualifiedFunctionName::make($this->context->getNamespace(), $function_name); // If it doesn't exist in the local namespace, try it // in the global namespace if (!$this->code_base->hasFunctionWithFQSEN($function_fqsen)) { $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $this->context); } } // Make sure the method we're calling actually exists if (!$this->code_base->hasFunctionWithFQSEN($function_fqsen)) { throw new IssueException(Issue::fromType(Issue::UndeclaredFunction)($this->context->getFile(), $this->node->lineno ?? 0, ["{$function_fqsen}()"])); } return $this->code_base->getFunctionByFQSEN($function_fqsen); }
/** * @param string $function_name * The name of the function we'd like to look up * * @param Context $context * The context in which we found the reference to the * given function name * * @param CodeBase $code_base * The global code base holding all state * * @param bool $is_function_declaration * This must be set to true if we're getting a function * that is being declared and false if we're getting a * function being called. * * @return Method * A method with the given name in the given context * * @throws CodeBaseExtension * An exception is thrown if we can't find the given * function */ public static function functionFromNameInContext(string $function_name, Context $context, CodeBase $code_base, bool $is_function_declaration = false) : Method { if ($is_function_declaration) { $function_fqsen = FullyQualifiedFunctionName::make($context->getNamespace(), $function_name); } else { $function_fqsen = FullyQualifiedFunctionName::make($context->getNamespace(), $function_name); // If it doesn't exist in the local namespace, try it // in the global namespace if (!$code_base->hasMethod($function_fqsen)) { $function_fqsen = FullyQualifiedFunctionName::fromStringInContext($function_name, $context); } } // Make sure the method we're calling actually exists if (!$code_base->hasMethod($function_fqsen)) { throw new CodeBaseException("call to undefined function {$function_fqsen}()"); } $method = $code_base->getMethod($function_fqsen); return $method; }
/** * 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(); }
/** * @return FullyQualifiedFunctionName|FullyQualifiedMethodName */ public function getFQSEN() : FQSEN { // Allow overrides if ($this->fqsen) { return $this->fqsen; } if ($this->getContext()->isInClassScope()) { return FullyQualifiedMethodName::fromStringInContext($this->getName(), $this->getContext()); } return FullyQualifiedFunctionName::fromStringInContext($this->getName(), $this->getContext()); }