作者:black-silenc
项目:pha
/**
* @return Method[]
*/
public static function methodListFromReflectionClassAndMethod(Context $context, CodeBase $code_base, \ReflectionClass $class, \ReflectionMethod $reflection_method) : array
{
$reflection_method = new \ReflectionMethod($class->getName(), $reflection_method->name);
$method = new Method($context, $reflection_method->name, new UnionType(), $reflection_method->getModifiers());
$method->setNumberOfRequiredParameters($reflection_method->getNumberOfRequiredParameters());
$method->setNumberOfOptionalParameters($reflection_method->getNumberOfParameters() - $reflection_method->getNumberOfRequiredParameters());
$method->setFQSEN(FullyQualifiedMethodName::fromStringInContext($method->getName(), $context));
return self::functionListFromFunction($method, $code_base);
}
作者:nagyistg
项目:pha
public function testFullyQualifiedMethodName()
{
$this->assertFQSENEqual(FullyQualifiedMethodName::make(FullyQualifiedClassName::make('\\Name\\Space', 'a'), 'f'), '\\Name\\Space\\a::f');
$this->assertFQSENEqual(FullyQualifiedMethodName::fromFullyQualifiedString('\\Name\\a::f'), '\\Name\\a::f');
$this->assertFQSENEqual(FullyQualifiedMethodName::fromFullyQualifiedString('Name\\a::f'), '\\Name\\a::f');
$this->assertFQSENEqual(FullyQualifiedMethodName::fromFullyQualifiedString('\\Name\\Space\\a::f,2'), '\\Name\\Space\\a::f,2');
$this->assertFQSENEqual(FullyQualifiedMethodName::fromFullyQualifiedString('\\Name\\Space\\a,1::f,2'), '\\Name\\Space\\a,1::f,2');
$this->assertFQSENEqual(FullyQualifiedMethodName::fromStringInContext('a::methodName', $this->context), '\\a::methodname');
}
作者:ets
项目:pha
public function testSimple()
{
$context = new Context();
$context_namespace = $context->withNamespace('\\A');
$context_class = $context_namespace->withScope(new ClassScope($context_namespace->getScope(), FullyQualifiedClassName::fromFullyQualifiedString('\\A\\B')));
$context_method = $context_namespace->withScope(new FunctionLikeScope($context_namespace->getScope(), FullyQualifiedMethodName::fromFullyQualifiedString('\\A\\b::c')));
$this->assertTrue(!empty($context));
$this->assertTrue(!empty($context_namespace));
$this->assertTrue(!empty($context_class));
$this->assertTrue(!empty($context_method));
}
作者:ablyle
项目:pha
public function testSimple()
{
$context = new Context();
$context_namespace = $context->withNamespace('\\A');
$context_class = $context_namespace->withClassFQSEN(FullyQualifiedClassName::fromFullyQualifiedString('\\A\\B'));
$context_method = $context_namespace->withMethodFQSEN(FullyQualifiedMethodName::fromFullyQualifiedString('\\A\\b::c'));
$this->assertTrue(!empty($context));
$this->assertTrue(!empty($context_namespace));
$this->assertTrue(!empty($context_class));
$this->assertTrue(!empty($context_method));
}
作者:tmli3b3rm4
项目:pha
public static function createSchema() : Schema
{
$schema = new Schema('File', [new Column('file_path', Column::TYPE_STRING, true), new Column('modification_time', Column::TYPE_INT)]);
$schema->addAssociation(new ListAssociation('FileClassFQSEN', Column::TYPE_STRING, function (File $file, array $class_fqsen_string_list) {
$file->getFile()->setClassFQSENList(array_map(function (string $fqsen_string) {
return FullyQualifiedClassName::fromFullyQualifiedString($fqsen_string);
}, $class_fqsen_string_list));
}, function (File $file) {
return array_map(function (FullyQualifiedClassName $fqsen) {
return (string) $fqsen;
}, $file->getFile()->getClassFQSENList());
}));
$schema->addAssociation(new ListAssociation('FileMethodFQSEN', Column::TYPE_STRING, function (File $file, array $method_fqsen_string_list) {
$file->getFile()->setMethodFQSENList(array_map(function (string $fqsen_string) {
if (false !== strpos($fqsen_string, '::')) {
return FullyQualifiedMethodName::fromFullyQualifiedString($fqsen_string);
} else {
return FullyQualifiedFunctionName::fromFullyQualifiedString($fqsen_string);
}
}, $method_fqsen_string_list));
}, function (File $file) {
return array_map(function (FQSEN $fqsen) {
return (string) $fqsen;
}, $file->getFile()->getMethodFQSENList());
}));
$schema->addAssociation(new ListAssociation('FilePropertyFQSEN', Column::TYPE_STRING, function (File $file, array $fqsen_string_list) {
$file->getFile()->setPropertyFQSENList(array_map(function (string $fqsen_string) {
if (false !== strpos($fqsen_string, '::')) {
return FullyQualifiedPropertyName::fromFullyQualifiedString($fqsen_string);
} else {
return FullyQualifiedFunctionName::fromFullyQualifiedString($fqsen_string);
}
}, $fqsen_string_list));
}, function (File $file) {
return array_map(function (FQSEN $fqsen) {
return (string) $fqsen;
}, $file->getFile()->getPropertyFQSENList());
}));
$schema->addAssociation(new ListAssociation('FileConstantFQSEN', Column::TYPE_STRING, function (File $file, array $fqsen_string_list) {
$file->getFile()->setConstantFQSENList(array_map(function (string $fqsen_string) {
if (false !== strpos($fqsen_string, '::')) {
return FullyQualifiedConstantName::fromFullyQualifiedString($fqsen_string);
} else {
return FullyQualifiedFunctionName::fromFullyQualifiedString($fqsen_string);
}
}, $fqsen_string_list));
}, function (File $file) {
return array_map(function (FQSEN $fqsen) {
return (string) $fqsen;
}, $file->getFile()->getConstantFQSENList());
}));
return $schema;
}
作者:hslatma
项目:pha
/**
* @return Method
* The method with the given name
*/
public function getMethodByNameInContext(CodeBase $code_base, string $name, Context $context) : Method
{
$method_fqsen = FullyQualifiedMethodName::make($this->getFQSEN(), $name);
if (!$code_base->hasMethod($method_fqsen)) {
if ('__construct' === $name) {
// Create a default constructor if its requested
// but doesn't exist yet
$default_constructor = Method::defaultConstructorForClassInContext($this, $this->getContext()->withClassFQSEN($this->getFQSEN()));
$this->addMethod($code_base, $default_constructor);
return $default_constructor;
}
throw new CodeBaseException("Method with name {$name} does not exist for class {$this->getFQSEN()}.");
}
return $code_base->getMethod($method_fqsen);
}
作者:ablyle
项目:pha
/**
* Visit a node with kind `\ast\AST_METHOD`
*
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitMethod(Decl $node) : Context
{
// Bomb out if we're not in a class context
$clazz = $this->getContextClass();
$method_name = (string) $node->name;
$method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context);
// Hunt for an available alternate ID if necessary
$alternate_id = 0;
while ($this->code_base->hasMethodWithFQSEN($method_fqsen)) {
$method_fqsen = $method_fqsen->withAlternateId(++$alternate_id);
}
// Create a new context with a new scope
$context = $this->context->withScope(new Scope());
$method = Method::fromNode($context, $this->code_base, $node);
// Override the FQSEN with the found alternate ID
$method->setFQSEN($method_fqsen);
$clazz->addMethod($this->code_base, $method);
if ('__construct' === $method_name) {
$clazz->setIsParentConstructorCalled(false);
} elseif ('__invoke' === $method_name) {
$clazz->getUnionType()->addType(CallableType::instance());
} elseif ('__toString' === $method_name && !$this->context->getIsStrictTypes()) {
$clazz->getUnionType()->addType(StringType::instance());
}
// Send the context into the method and reset the scope
$context = $this->context->withMethodFQSEN($method->getFQSEN());
return $context;
}
作者:tpun
项目:pha
/**
* Visit a node with kind `\ast\AST_METHOD`
*
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitMethod(Decl $node) : Context
{
// Bomb out if we're not in a class context
$class = $this->getContextClass();
$method_name = (string) $node->name;
$method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context);
// Hunt for an available alternate ID if necessary
$alternate_id = 0;
while ($this->code_base->hasMethodWithFQSEN($method_fqsen)) {
$method_fqsen = $method_fqsen->withAlternateId(++$alternate_id);
}
$method = Method::fromNode(clone $this->context, $this->code_base, $node, $method_fqsen);
$class->addMethod($this->code_base, $method, new None());
if ('__construct' === $method_name) {
$class->setIsParentConstructorCalled(false);
if ($class->isGeneric()) {
// Get the set of template type identifiers defined on
// the class
$template_type_identifiers = array_keys($class->getTemplateTypeMap());
// Get the set of template type identifiers defined
// across all parameter types
$parameter_template_type_identifiers = [];
foreach ($method->getParameterList() as $parameter) {
foreach ($parameter->getUnionType()->getTypeSet() as $type) {
if ($type instanceof TemplateType) {
$parameter_template_type_identifiers[] = $type->getName();
}
}
}
$missing_template_type_identifiers = array_diff($template_type_identifiers, $parameter_template_type_identifiers);
if ($missing_template_type_identifiers) {
$this->emitIssue(Issue::GenericConstructorTypes, $node->lineno ?? 0, implode(',', $missing_template_type_identifiers), (string) $class->getFQSEN());
}
}
} elseif ('__invoke' === $method_name) {
$class->getUnionType()->addType(CallableType::instance());
} elseif ('__toString' === $method_name && !$this->context->getIsStrictTypes()) {
$class->getUnionType()->addType(StringType::instance());
}
// Create a new context with a new scope
return $this->context->withScope($method->getInternalScope());
}
作者:black-silenc
项目:pha
/**
* @param FunctionInterface $method
* Any method
*
* @param FullyQualifiedMethodName $fqsen
* The FQSEN for the method
*
* @return null
*/
private function addMethodWithMethodFQSEN(FunctionInterface $method, FullyQualifiedMethodName $fqsen)
{
$this->addMethodInScope($method, $fqsen->getFullyQualifiedClassName());
}
作者:ablyle
项目:pha
/**
* @param Node|string $method_name
* Either then name of the method or a node that
* produces the name of the method.
*
* @param bool $is_static
* Set to true if this is a static method call
*
* @return Method
* A method with the given name on the class referenced
* from the given node
*
* @throws NodeException
* An exception is thrown if we can't understand the node
*
* @throws CodeBaseExtension
* An exception is thrown if we can't find the given
* method
*
* @throws TypeException
* An exception may be thrown if the only viable candidate
* is a non-class type.
*
* @throws IssueException
*/
public function getMethod($method_name, bool $is_static) : Method
{
if ($method_name instanceof Node) {
// The method_name turned out to be a variable.
// There isn't much we can do to figure out what
// it's referring to.
throw new NodeException($method_name, "Unexpected method node");
}
assert(is_string($method_name), "Method name must be a string. Found non-string at {$this->context}");
try {
$class_list = (new ContextNode($this->code_base, $this->context, $this->node->children['expr'] ?? $this->node->children['class']))->getClassList();
} catch (CodeBaseException $exception) {
throw new IssueException(Issue::fromType(Issue::UndeclaredClassMethod)($this->context->getFile(), $this->node->lineno ?? 0, [$method_name, (string) $exception->getFQSEN()]));
}
// If there were no classes on the left-type, figure
// out what we were trying to call the method on
// and send out an error.
if (empty($class_list)) {
$union_type = UnionTypeVisitor::unionTypeFromClassNode($this->code_base, $this->context, $this->node->children['expr'] ?? $this->node->children['class']);
if (!$union_type->isEmpty() && $union_type->isNativeType() && !$union_type->hasAnyType([MixedType::instance(), ObjectType::instance(), StringType::instance()]) && !(Config::get()->null_casts_as_any_type && $union_type->hasType(NullType::instance()))) {
throw new IssueException(Issue::fromType(Issue::NonClassMethodCall)($this->context->getFile(), $this->node->lineno ?? 0, [$method_name, (string) $union_type]));
}
throw new NodeException($this->node, "Can't figure out method call for {$method_name}");
}
// Hunt to see if any of them have the method we're
// looking for
foreach ($class_list as $i => $class) {
if ($class->hasMethodWithName($this->code_base, $method_name)) {
return $class->getMethodByNameInContext($this->code_base, $method_name, $this->context);
} else {
if ($class->hasMethodWithName($this->code_base, '__call')) {
return $class->getMethodByNameInContext($this->code_base, '__call', $this->context);
}
}
}
// Figure out an FQSEN for the method we couldn't find
$method_fqsen = FullyQualifiedMethodName::make($class_list[0]->getFQSEN(), $method_name);
if ($is_static) {
throw new IssueException(Issue::fromType(Issue::UndeclaredStaticMethod)($this->context->getFile(), $this->node->lineno ?? 0, [(string) $method_fqsen]));
}
throw new IssueException(Issue::fromType(Issue::UndeclaredMethod)($this->context->getFile(), $this->node->lineno ?? 0, [(string) $method_fqsen]));
}
作者:zhangf91
项目:pha
/**
* A list of types for parameters associated with the
* given builtin function with the given name
*
* @param FullyQualifiedMethodName|FullyQualifiedFunctionName $function_fqsen
*
* @see internal_varargs_check
* Formerly `function internal_varargs_check`
*/
public static function internalFunctionSignatureMapForFQSEN($function_fqsen) : array
{
$context = new Context();
$map = self::internalFunctionSignatureMap();
if ($function_fqsen instanceof FullyQualifiedMethodName) {
$class_fqsen = $function_fqsen->getFullyQualifiedClassName();
$class_name = $class_fqsen->getName();
$function_name = $class_name . '::' . $function_fqsen->getName();
} else {
$function_name = $function_fqsen->getName();
}
$function_name_original = $function_name;
$alternate_id = 0;
$configurations = [];
while (isset($map[$function_name])) {
// Get some static data about the function
$type_name_struct = $map[$function_name];
if (empty($type_name_struct)) {
continue;
}
// Figure out the return type
$return_type_name = array_shift($type_name_struct);
$return_type = $return_type_name ? UnionType::fromStringInContext($return_type_name, $context) : null;
$name_type_name_map = $type_name_struct;
$property_name_type_map = [];
foreach ($name_type_name_map as $name => $type_name) {
$property_name_type_map[$name] = empty($type_name) ? new UnionType() : UnionType::fromStringInContext($type_name, $context);
}
$configurations[] = ['return_type' => $return_type, 'property_name_type_map' => $property_name_type_map];
$function_name = $function_name_original . '\'' . ++$alternate_id;
}
return $configurations;
}
作者:actan
项目:pha
/**
* Visit a node with kind `\ast\AST_METHOD`
*
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitMethod(Decl $node) : Context
{
// Bomb out if we're not in a class context
$clazz = $this->getContextClass();
$method_name = (string) $node->name;
$method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context);
// Hunt for an available alternate ID if necessary
$alternate_id = 0;
while ($this->code_base->hasMethod($method_fqsen)) {
$method_fqsen = $method_fqsen->withAlternateId(++$alternate_id);
}
// Create a new context with a new scope
$context = $this->context->withScope(new Scope());
// Add $this to the scope of non-static methods
if (!($node->flags & \ast\flags\MODIFIER_STATIC)) {
assert($clazz->getContext()->getScope()->hasVariableWithName('this'), "Classes must have a \$this variable.");
$context = $context->withScopeVariable($clazz->getContext()->getScope()->getVariableWithName('this'));
}
$method = Method::fromNode($context, $this->code_base, $node);
// Override the FQSEN with the found alternate ID
$method->setFQSEN($method_fqsen);
$clazz->addMethod($this->code_base, $method);
if ('__construct' === $method_name) {
$clazz->setIsParentConstructorCalled(false);
} else {
if ('__invoke' === $method_name) {
$clazz->getUnionType()->addType(CallableType::instance());
} else {
if ('__toString' === $method_name && !$this->context->getIsStrictTypes()) {
$clazz->getUnionType()->addType(StringType::instance());
}
}
}
// Add each method parameter to the scope. We clone it
// so that changes to the variable don't alter the
// parameter definition
foreach ($method->getParameterList() as $parameter) {
$method->getContext()->addScopeVariable(clone $parameter);
}
// Send the context into the method and reset the scope
$context = $method->getContext()->withMethodFQSEN($method->getFQSEN());
return $context;
}
作者:ablyle
项目:pha
/**
* @return Method
* A default constructor for the given class
*/
public static function defaultConstructorForClassInContext(Clazz $clazz, Context $context) : Method
{
$method = new Method($context, '__construct', $clazz->getUnionType(), 0);
$method->setFQSEN(FullyQualifiedMethodName::make($clazz->getFQSEN(), '__construct'));
return $method;
}
作者:hslatma
项目:pha
/**
* @return FullyQualifiedMethodName
* A fully-qualified method name
*/
public function withMethodName(string $method_name, int $method_alternate_id = 0) : FullyQualifiedMethodName
{
return FullyQualifiedMethodName::make($this, $method_name, $method_alternate_id);
}
作者:themario
项目:pha
/**
* @return FullyQualifiedFunctionName|FullyQualifiedMethodName
*/
public function getFQSEN()
{
// Allow overrides
if ($this->fqsen) {
return $this->fqsen;
}
return FullyQualifiedMethodName::fromStringInContext($this->getName(), $this->getContext());
}
作者:nagyistg
项目:pha
/**
* @return void
*/
public function unserialize($serialized)
{
list($file_ref, $serialized) = explode('^', $serialized);
parent::unserialize($file_ref);
list($namespace, $class_fqsen, $method_fqsen, $closure_fqsen) = explode('|', $serialized);
$this->namespace = $namespace;
$this->class_fqsen = $class_fqsen ? FullyQualifiedClassName::fromFullyQualifiedString($class_fqsen) : null;
// Determine if we have a method or a function
if (false === strpos($method_fqsen, '::')) {
$this->method_fqsen = $method_fqsen ? FullyQualifiedFunctionName::fromFullyQualifiedString($method_fqsen) : null;
} else {
$this->method_fqsen = $method_fqsen ? FullyQualifiedMethodName::fromFullyQualifiedString($method_fqsen) : null;
}
$this->closure_fqsen = $closure_fqsen ? FullyQualifiedFunctionName::fromFullyQualifiedString($closure_fqsen) : null;
}
作者:themario
项目:pha
/**
* Visit a node with kind `\ast\AST_STATIC_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 visitStaticCall(Node $node) : UnionType
{
$class_name = AST::classNameFromNode($this->context, $this->code_base, $node);
// assert(!empty($class_name), 'Class name cannot be empty');
if (!$class_name) {
return new UnionType();
}
$method_name = $node->children['method'];
// Give up on any complicated nonsense where the
// method name is a variable such as in
// `$variable->$function_name()`.
if ($method_name instanceof Node) {
return new UnionType();
}
// Method names can some times turn up being
// other method calls.
assert(is_string($method_name), "Method name must be a string. Something else given.");
$method_fqsen = FullyQualifiedMethodName::make(FullyQualifiedClassName::fromStringInContext($class_name, $this->context), $method_name);
if (!$this->code_base->hasMethod($method_fqsen)) {
return new UnionType();
}
$method = $this->code_base->getMethod($method_fqsen);
return $method->getUnionType();
}
作者:black-silenc
项目:pha
/**
* @param array
* A map from column name to value
*
* @return Model
* An instance of the model derived from row data
*/
public static function fromRow(array $row) : Method
{
list($scope, $name) = explode('|', $row['scope_name']);
$method_element = new MethodElement(unserialize(base64_decode($row['context'])), $row['name'], UnionType::fromFullyQualifiedString($row['type']), (int) $row['flags']);
$method_element->setNumberOfRequiredParameters($row['number_of_required_parameters']);
$method_element->setNumberOfOptionalParameters($row['number_of_optional_parameters']);
$method = new Method($method_element, $scope, $name);
if (false !== strpos($row['fqsen'], '::')) {
$fqsen = FullyQualifiedMethodName::fromFullyQualifiedString($row['fqsen']);
} else {
$fqsen = FullyQualifiedFunctionName::fromFullyQualifiedString($row['fqsen']);
}
$method->getMethod()->setFQSEN($fqsen);
return $method;
}
作者:tmli3b3rm4
项目:pha
/**
* Visit a node with kind `\ast\AST_METHOD`
*
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitMethod(Node $node) : Context
{
// Bomb out if we're not in a class context
$clazz = $this->getContextClass();
$method_name = $node->name;
$method_fqsen = FullyQualifiedMethodName::fromStringInContext($method_name, $this->context);
// Hunt for an available alternate ID if necessary
$alternate_id = 0;
while ($this->code_base->hasMethod($method_fqsen)) {
$method_fqsen = $method_fqsen->withAlternateId(++$alternate_id);
}
$method = Method::fromNode(clone $this->context, $this->code_base, $node);
// Override the FQSEN with the found alternate ID
$method->setFQSEN($method_fqsen);
$clazz->addMethod($this->code_base, $method);
if ('__construct' === $method_name) {
$clazz->setIsParentConstructorCalled(false);
} else {
if ('__invoke' === $method_name) {
$clazz->getUnionType()->addType(CallableType::instance());
} else {
if ('__toString' === $method_name) {
$clazz->getUnionType()->addType(StringType::instance());
}
}
}
// Send the context into the method
$context = $this->context->withMethodFQSEN($method->getFQSEN());
// Add each method parameter to the scope. We clone it
// so that changes to the variable don't alter the
// parameter definition
foreach ($method->getParameterList() as $parameter) {
$context->addScopeVariable(clone $parameter);
}
return $context;
}
作者:kangko
项目:pha
/**
* @param Node|string $method_name_or_node
* Either then name of the method or a node that
* produces the name of the method.
*
* @param bool $is_static
* Set to true if this is a static method call
*
* @return Method
* A method with the given name on the class referenced
* from the given node
*
* @throws NodeException
* An exception is thrown if we can't understand the node
*
* @throws CodeBaseExtension
* An exception is thrown if we can't find the given
* method
*
* @throws TypeException
* An exception may be thrown if the only viable candidate
* is a non-class type.
*/
public function getMethod($method_name_or_node, bool $is_static) : Method
{
$clazz = $this->getClass();
if ($method_name_or_node instanceof Node) {
// TODO: The method_name turned out to
// be a variable. We'd have to look
// that up to figure out what the
// string is, but thats a drag.
throw new NodeException($method_name_or_node, "Unexpected method node");
}
$method_name = $method_name_or_node;
assert(is_string($method_name), "Method name must be a string. Found non-string at {$this->context}");
if (!$clazz->hasMethodWithName($this->code_base, $method_name)) {
$method_fqsen = FullyQualifiedMethodName::make($clazz->getFQSEN(), $method_name);
if ($is_static) {
throw new CodeBaseException($method_fqsen, "static call to undeclared method {$method_fqsen}");
} else {
throw new CodeBaseException($method_fqsen, "call to undeclared method {$method_fqsen}");
}
}
$method = $clazz->getMethodByNameInContext($this->code_base, $method_name, $this->context);
return $method;
}