本文整理汇总了PHP中Phan\Language\UnionType::fromNode方法的典型用法代码示例。如果您正苦于以下问题:PHP UnionType::fromNode方法的具体用法?PHP UnionType::fromNode怎么用?PHP UnionType::fromNode使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类Phan\Language\UnionType
的用法示例。
在下文中一共展示了UnionType::fromNode方法的15个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的PHP代码示例。
示例1: fromNodeInContext
/**
* @param Node $node
* An AST_VAR node
*
* @param Context $context
* The context in which the variable is found
*
* @param CodeBase $code_base
*
* @return Variable
* A variable begotten from a node
*/
public static function fromNodeInContext(Node $node, Context $context, CodeBase $code_base, bool $should_check_type = true) : Variable
{
$variable_name = AST::variableName($node);
// Get the type of the assignment
$union_type = $should_check_type ? UnionType::fromNode($context, $code_base, $node) : new UnionType();
$variable = new Variable($context->withLineNumberStart($node->lineno ?? 0)->withLineNumberEnd($node->endLineno ?? 0), $variable_name, $union_type, $node->flags);
return $variable;
}
示例2: get
/**
* Force the future to figure out the type of the
* given object or throw an IssueException if it
* is unable to do so
*
* @return UnionType
* The type of the future
*
* @throws IssueException
* An exception is thrown if we are unable to determine
* the type at the time this method is called
*/
public function get() : UnionType
{
return UnionType::fromNode($this->context, $this->code_base, $this->node, false);
}
示例3: visitForeach
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitForeach(Node $node) : Context
{
if ($node->children['value']->kind == \ast\AST_LIST) {
foreach ($node->children['value']->children as $child_node) {
$variable = Variable::fromNodeInContext($child_node, $this->context, $this->code_base, false);
$this->context->addScopeVariable($variable);
}
// Otherwise, read the value as regular variable and
// add it to the scope
} else {
// Create a variable for the value
$variable = Variable::fromNodeInContext($node->children['value'], $this->context, $this->code_base, false);
// Get the type of the node from the left side
$type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
// Filter out the non-generic types of the
// expression
$non_generic_type = $type->asNonGenericTypes();
// If we were able to figure out the type and its
// a generic type, then set its element types as
// the type of the variable
if (!$non_generic_type->isEmpty()) {
$variable->setUnionType($non_generic_type);
}
// Add the variable to the scope
$this->context->addScopeVariable($variable);
}
// If there's a key, make a variable out of that too
if (!empty($node->children['key'])) {
if ($node->children['key'] instanceof \ast\Node && $node->children['key']->kind == \ast\AST_LIST) {
Log::err(Log::EFATAL, "Can't use list() as a key element - aborting", $this->context->getFile(), $node->lineno);
}
$variable = Variable::fromNodeInContext($node->children['key'], $this->context, $this->code_base, false);
$this->context->addScopeVariable($variable);
}
// Note that we're not creating a new scope, just
// adding variables to the existing scope
return $this->context;
}
示例4: analyzeInternalArgumentType
/**
* Check to see if the given Clazz is a duplicate
*
* @param Method $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(Method $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(), "arg#1(pieces) is %s but {$method->getFQSEN()}() takes array when passed only 1 arg");
return;
} else {
if ($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())) {
Log::err(Log::EPARAM, "arg#2(glue) is {$arg2_type} but {$method->getFQSEN()}() takes string when arg#1 is array", $context->getFile(), $context->getLineNumberStart());
}
} else {
if ((string) $arg1_type == 'string') {
if (!$arg2_type->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Log::err(Log::EPARAM, "arg#2(pieces) is {$arg2_type} but {$method->getFQSEN()}() takes array when arg#1 is string", $context->getFile(), $context->getLineNumberStart());
}
}
}
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) {
Log::err(Log::EPARAM, "call with {$argcount} arg(s) to {$method->getFQSEN()}() which requires {$method->getNumberOfRequiredParameters()} arg(s)", $context->getFile(), $context->getLineNumberStart());
return;
}
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), "The last argument to {$method->getFQSEN()} must be a callable");
for ($i = 0; $i < $argcount - 1; $i++) {
self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, CallableType::instance()->asUnionType(), "arg#" . ($i + 1) . " is %s but {$method->getFQSEN()}() takes array");
}
return;
case 'array_diff_uassoc':
case 'array_uintersect_uassoc':
if ($argcount < 4) {
Log::err(Log::EPARAM, "call with {$argcount} arg(s) to {$method->getFQSEN()}() which requires {$method->getNumberOfRequiredParameters()} arg(s)", $context->getFile(), $context->getLineNumberStart());
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(), "The last argument to {$method->getFQSEN()} must be a callable");
self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 2], $context, $code_base, CallableType::instance()->asUnionType(), "The second last argument to {$method->getFQSEN()} must be a callable");
for ($i = 0; $i < $argcount - 2; $i++) {
self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#" . ($i + 1) . " is %s but {$method->getFQSEN()}() takes 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, ArrayType::instance()->asUnionType(), "arg#1(token) is %s but {$method->getFQSEN()}() takes string when passed only one arg");
}
// 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(), "arg#1(values) is %s but {$method->getFQSEN()}() takes array when passed only one arg")) {
return;
}
}
// The arginfo check will handle the other case
break;
default:
break;
//.........这里部分代码省略.........
示例5: visitMethodCall
/**
* Visit a node with kind `\ast\AST_METHOD_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 visitMethodCall(Node $node) : 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.");
try {
$class_fqsen = null;
foreach ($this->classListFromNode($node->children['class'] ?? $node->children['expr']) as $i => $class) {
$class_fqsen = $class->getFQSEN();
if (!$class->hasMethodWithName($this->code_base, $method_name)) {
continue;
}
try {
$method = $class->getMethodByNameInContext($this->code_base, $method_name, $this->context);
$union_type = $method->getUnionType();
// Map template types to concrete types
if ($union_type->hasTemplateType()) {
// Get the type of the object calling the property
$expression_type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
// Map template types to concrete types
$union_type = $union_type->withTemplateParameterTypeMap($expression_type->getTemplateParameterTypeMap($this->code_base));
}
// Remove any references to \static or \static[]
// once we're talking about the method's return
// type outside of its class
if ($union_type->hasStaticType()) {
$union_type = clone $union_type;
$union_type->removeType(\Phan\Language\Type\StaticType::instance());
}
if ($union_type->genericArrayElementTypes()->hasStaticType()) {
$union_type = clone $union_type;
// Find the static type on the list
$static_type = $union_type->getTypeSet()->find(function (Type $type) : bool {
return $type->isGenericArray() && $type->genericArrayElementType()->isStaticType();
});
// Remove it from the list
$union_type->removeType($static_type);
}
return $union_type;
} catch (IssueException $exception) {
return new UnionType();
}
}
} catch (IssueException $exception) {
// Swallow it
} catch (CodeBaseException $exception) {
$this->emitIssue(Issue::UndeclaredClassMethod, $node->lineno ?? 0, $method_name, (string) $exception->getFQSEN());
}
return new UnionType();
}
示例6: analyzeCallToMethod
//.........这里部分代码省略.........
}
}
}
// Confirm the argument types are clean
ArgumentType::analyze($method, $node, $this->context, $this->code_base);
// Take another pass over pass-by-reference parameters
// and assign types to passed in variables
foreach ($argument_list->children as $i => $argument) {
$parameter = $method->getParameterList()[$i] ?? null;
if (!$parameter) {
continue;
}
// If the parameter is pass-by-reference and we're
// passing a variable in, see if we should pass
// the parameter and variable types to eachother
$variable = null;
if ($parameter->isPassByReference()) {
if ($argument->kind == \ast\AST_VAR) {
$variable = AST::getOrCreateVariableFromNodeInContext($argument, $this->context, $this->code_base);
} else {
if ($argument->kind == \ast\AST_STATIC_PROP || $argument->kind == \ast\AST_PROP) {
$property_name = $argument->children['prop'];
if (is_string($property_name)) {
// We don't do anything with it; just create it
// if it doesn't exist
try {
$variable = AST::getOrCreatePropertyFromNodeInContext($argument->children['prop'], $argument, $this->context, $this->code_base);
} catch (CodeBaseException $exception) {
Log::err(Log::EUNDEF, $exception->getMessage(), $this->context->getFile(), $node->lineno);
} catch (NodeException $exception) {
// If we can't figure out what kind of a call
// this is, don't worry about it
}
} else {
// This is stuff like `Class->$foo`. I'm ignoring
// it.
}
}
}
if ($variable) {
$variable->getUnionType()->addUnionType($parameter->getUnionType());
}
}
}
// If we're in quick mode, don't retest methods based on
// parameter types passed in
if (Config::get()->quick_mode) {
return;
}
// We're going to hunt to see if any of the arguments
// have a mismatch with the parameters. If so, we'll
// re-check the method to see how the parameters impact
// its return type
$has_argument_parameter_mismatch = false;
// Now that we've made sure the arguments are sufficient
// for definitions on the method, we iterate over the
// arguments again and add their types to the parameter
// types so we can test the method again
$argument_list = $node->children['args'];
// We create a copy of the parameter list so we can switch
// back to it after
$original_parameter_list = $method->getParameterList();
foreach ($argument_list->children as $i => $argument) {
$parameter = $method->getParameterList()[$i] ?? null;
if (!$parameter) {
continue;
}
// If the parameter has no type, pass the
// argument's type to it
if ($parameter->getUnionType()->isEmpty()) {
$has_argument_parameter_mismatch = true;
$argument_type = UnionType::fromNode($this->context, $this->code_base, $argument);
// If this isn't an internal function or method
// and it has no type, add the argument's type
// to it so we can compare it to subsequent
// calls
if (!$parameter->getContext()->isInternal()) {
// Clone the parameter in the original
// parameter list so we can reset it
// later
$original_parameter_list[$i] = clone $parameter;
// Then set the new type on that parameter based
// on the argument's type. We'll use this to
// retest the method with the passed in types
$parameter->getUnionType()->addUnionType($argument_type);
}
}
}
// Now that we know something about the parameters used
// to call the method, we can reanalyze the method with
// the types of the parameter, making sure we don't get
// into an infinite loop of checking calls to the current
// method in scope
if ($has_argument_parameter_mismatch && !$method->getContext()->isInternal() && (!$this->context->isMethodScope() || $method->getFQSEN() !== $this->context->getMethodFQSEN())) {
$method->analyze($method->getContext(), $code_base);
}
// Reset to the original parameter list after having
// tested the parameters with the types passed in
$method->setParameterList($original_parameter_list);
}
示例7: visitPropDecl
/**
* Visit a node with kind `\ast\AST_PROP_DECL`
*
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitPropDecl(Node $node) : Context
{
// Bomb out if we're not in a class context
$clazz = $this->getContextClass();
// Get a comment on the property declaration
$comment = Comment::fromStringInContext($node->children[0]->docComment ?? '', $this->context);
foreach ($node->children ?? [] as $i => $child_node) {
// Ignore children which are not property elements
if (!$child_node || $child_node->kind != \ast\AST_PROP_ELEM) {
continue;
}
// If something goes wrong will getting the type of
// a property, we'll store it as a future union
// type and try to figure it out later
$future_union_type = null;
try {
// Get the type of the default
$union_type = UnionType::fromNode($this->context, $this->code_base, $child_node->children['default'], false);
} catch (IssueException $exception) {
$future_union_type = new FutureUnionType($this->code_base, $this->context, $child_node->children['default']);
$union_type = new UnionType();
}
// Don't set 'null' as the type if thats the default
// given that its the default default.
if ($union_type->isType(NullType::instance())) {
$union_type = new UnionType();
}
$property_name = $child_node->children['name'];
assert(is_string($property_name), 'Property name must be a string. ' . 'Got ' . print_r($property_name, true) . ' at ' . $this->context);
$property = new Property(clone $this->context->withLineNumberStart($child_node->lineno ?? 0), is_string($child_node->children['name']) ? $child_node->children['name'] : '_error_', $union_type, $node->flags ?? 0);
$property->setFQSEN(FullyQualifiedPropertyName::make($clazz->getFQSEN(), $property->getName()));
// Add the property to the class
$clazz->addProperty($this->code_base, $property);
$property->setSuppressIssueList($comment->getSuppressIssueList());
// Look for any @var declarations
if ($variable = $comment->getVariableList()[$i] ?? null) {
if ((string) $union_type != 'null' && !$union_type->canCastToUnionType($variable->getUnionType())) {
$this->emitIssue(Issue::TypeMismatchProperty, $child_node->lineno ?? 0, (string) $union_type, (string) $property->getFQSEN(), (string) $variable->getUnionType());
}
// Set the declared type to the doc-comment type and add
// |null if the default value is null
$property->getUnionType()->addUnionType($variable->getUnionType());
}
// Wait until after we've added the (at)var type
// before setting the future so that calling
// $property->getUnionType() doesn't force the
// future to be reified.
if (!empty($future_union_type)) {
$property->setFutureUnionType($future_union_type);
}
}
return $this->context;
}
示例8: visitInstanceof
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitInstanceof(Node $node) : Context
{
// Only look at things of the form
// `$variable instanceof ClassName`
if ($node->children['expr']->kind !== \ast\AST_VAR) {
return $this->context;
}
$context = $this->context;
try {
// Get the variable we're operating on
$variable = (new ContextNode($this->code_base, $this->context, $node->children['expr']))->getVariable();
// Get the type that we're checking it against
$type = UnionType::fromNode($this->context, $this->code_base, $node->children['class']);
// Make a copy of the variable
$variable = clone $variable;
// Add the type to the variable
$variable->getUnionType()->addUnionType($type);
// Overwrite the variable with its new type
$context = $context->withScopeVariable($variable);
} catch (\Exception $exception) {
// Swallow it
}
return $context;
}
示例9: analyzeInternalArgumentType
/**
* Check to see if the given Clazz is a duplicate
*
* @param Method $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(Method $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;
} else {
if ($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::emit(Issue::ParamSpecial1, $context->getFile(), $context->getLineNumberStart(), 2, 'glue', (string) $arg2_type, (string) $method->getFQSEN(), 'string', 1, 'array');
}
} else {
if ((string) $arg1_type == 'string') {
if (!$arg2_type->canCastToUnionType(ArrayType::instance()->asUnionType())) {
Issue::emit(Issue::ParamSpecial1, $context->getFile(), $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::emit(Issue::ParamTooFewInternal, $context->getFile(), $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::emit(Issue::ParamTooFewInternal, $context->getFile(), $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)
//.........这里部分代码省略.........
示例10: visitMethodCall
/**
* @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 string
* The class name represented by the given call
*/
public function visitMethodCall(Node $node) : string
{
if ($node->children['expr']->kind == \ast\AST_VAR) {
if ($node->children['expr']->children['name'] instanceof Node) {
return '';
}
// $var->method()
if ($node->children['expr']->children['name'] == 'this') {
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
return '';
}
return (string) $this->context->getClassFQSEN();
}
$variable_name = $node->children['expr']->children['name'];
if (!$this->context->getScope()->hasVariableWithName($variable_name)) {
// Got lost, couldn't find the variable in the current scope
// If it really isn't defined, it will be caught by the
// undefined var error
return '';
}
$variable = $this->context->getScope()->getVariableWithName($variable_name);
// Hack - loop through the possible types of the var and assume
// first found class is correct
foreach ($variable->getUnionType()->nonGenericArrayTypes()->getTypeList() as $type) {
$child_class_fqsen = FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
if ($this->code_base->hasClassWithFQSEN($child_class_fqsen)) {
return (string) FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
}
}
// Could not find name
return '';
}
if ($node->children['expr']->kind == \ast\AST_PROP) {
$prop = $node->children['expr'];
if (!($prop->children['expr']->kind == \ast\AST_VAR && !$prop->children['expr']->children['name'] instanceof Node)) {
return '';
}
// $var->prop->method()
$var = $prop->children['expr'];
if ($var->children['name'] == 'this') {
// If we're not in a class scope, 'this' won't work
if (!$this->context->isInClassScope()) {
Log::err(Log::ESTATIC, 'Using $this when not in object context', $this->context->getFile(), $node->lineno);
return '';
}
// Get the class in scope
$clazz = $this->code_base->getClassByFQSEN($this->context->getClassFQSEN());
if ($prop->children['prop'] instanceof Node) {
// $this->$prop->method() - too dynamic, give up
return '';
}
$property_name = $prop->children['prop'];
if ($clazz->hasPropertyWithName($this->code_base, $property_name)) {
try {
$property = $clazz->getPropertyByNameInContext($this->code_base, $property_name, $this->context);
} catch (AccessException $exception) {
Log::err(Log::EACCESS, $exception->getMessage(), $this->context->getFile(), $node->lineno);
return '';
}
// Find the first viable property type
foreach ($property->getUnionType()->nonGenericArrayTypes()->getTypeList() as $type) {
$class_fqsen = FullyQualifiedClassName::fromStringInContext((string) $type, $this->context);
if ($this->code_base->hasClassWithFQSEN($class_fqsen)) {
return (string) $class_fqsen;
}
}
}
// No such property was found, or none were classes
// that could be found
return '';
}
return '';
}
if ($node->children['expr']->kind == \ast\AST_METHOD_CALL) {
// Get the type returned by the first method
// call.
$union_type = UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
// Find the subset of types that are viable
// classes
$viable_class_types = $union_type->nonNativeTypes()->nonGenericArrayTypes();
// If there are no non-native types, give up
if ($viable_class_types->isEmpty()) {
return '';
}
// Return the first non-native type in the
// list and hope its a class
return (string) $viable_class_types->head();
}
return '';
}
示例11: visitUnaryOp
/**
* Visit a node with kind `\ast\AST_UNARY_OP`
*
* @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 visitUnaryOp(Node $node) : UnionType
{
// Shortcut some easy operators
switch ($node->flags) {
case \ast\flags\UNARY_BOOL_NOT:
return BoolType::instance()->asUnionType();
}
return UnionType::fromNode($this->context, $this->code_base, $node->children['expr']);
}
示例12: visitIfElem
/**
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitIfElem(Node $node) : Context
{
if (!isset($node->children['cond']) || !$node->children['cond'] instanceof Node) {
return $this->context;
}
// Get the type just to make sure everything
// is defined.
$expression_type = UnionType::fromNode($this->context, $this->code_base, $node->children['cond']);
// Look to see if any proofs we do within the condition
// can say anything about types within the statement
// list.
return (new ConditionVisitor($this->code_base, $this->context))($node->children['cond']);
}
示例13: analyzeCallToMethod
//.........这里部分代码省略.........
// We're going to hunt to see if any of the arguments
// have a mismatch with the parameters. If so, we'll
// re-check the method to see how the parameters impact
// its return type
$has_argument_parameter_mismatch = false;
// Now that we've made sure the arguments are sufficient
// for definitions on the method, we iterate over the
// arguments again and add their types to the parameter
// types so we can test the method again
$argument_list = $node->children['args'];
// We create a copy of the parameter list so we can switch
// back to it after
$original_parameter_list = $method->getParameterList();
// Create a backup of the method's scope so that we can
// reset it after fucking with it below
$original_method_scope = $method->getInternalScope();
foreach ($argument_list->children as $i => $argument) {
// TODO(Issue #376): Support inference on the child in **the set of vargs**, not just the first vararg
// This is just testing the first vararg.
// The implementer will also need to restore the original parameter list.
$parameter = $original_parameter_list[$i] ?? null;
if (!$parameter) {
continue;
}
// If the parameter has no type, pass the
// argument's type to it
if ($parameter->getVariadicElementUnionType()->isEmpty()) {
$has_argument_parameter_mismatch = true;
// If this isn't an internal function or method
// and it has no type, add the argument's type
// to it so we can compare it to subsequent
// calls
if (!$parameter->isInternal()) {
$argument_type = UnionType::fromNode($this->context, $this->code_base, $argument);
// Clone the parameter in the original
// parameter list so we can reset it
// later
// TODO: If there are varargs and this is beyond the end, ensure last arg is cloned.
$original_parameter_list[$i] = clone $original_parameter_list[$i];
// Then set the new type on that parameter based
// on the argument's type. We'll use this to
// retest the method with the passed in types
$parameter->getVariadicElementUnionType()->addUnionType($argument_type);
if (!is_object($argument)) {
continue;
}
// If we're passing by reference, get the variable
// we're dealing with wrapped up and shoved into
// the scope of the method
if ($parameter->isPassByReference()) {
if ($original_parameter_list[$i]->isVariadic()) {
// For now, give up and work on it later.
// TODO(Issue #376): It's possible to have a parameter `&...$args`. Analysing that is going to be a problem.
// Is it possible to create `PassByReferenceVariableCollection extends Variable` or something similar?
} elseif ($argument->kind == \ast\AST_VAR) {
// Get the variable
$variable = (new ContextNode($this->code_base, $this->context, $argument))->getOrCreateVariable();
$pass_by_reference_variable = new PassByReferenceVariable($parameter, $variable);
$parameter_list = $method->getParameterList();
$parameter_list[$i] = $pass_by_reference_variable;
$method->setParameterList($parameter_list);
// Add it to the scope of the function wrapped
// in a way that makes it addressable as the
// parameter its mimicking
$method->getInternalScope()->addVariable($pass_by_reference_variable);
} else {
示例14: visitClassConstDecl
/**
* Visit a node with kind `\ast\AST_CLASS_CONST_DECL`
*
* @param Node $node
* A node to parse
*
* @return Context
* A new or an unchanged context resulting from
* parsing the node
*/
public function visitClassConstDecl(Node $node) : Context
{
$clazz = $this->getContextClass();
foreach ($node->children ?? [] as $child_node) {
$constant = new Constant($this->context->withLineNumberStart($child_node->lineno ?? 0)->withLineNumberEnd($child_node->endLineno ?? 0), $child_node->children['name'], UnionType::fromNode($this->context, $this->code_base, $child_node->children['value']), $child_node->flags ?? 0);
$clazz->addConstant($this->code_base, $constant);
}
return $this->context;
}
示例15: visitArray
/**
* Visit a node with kind `\ast\AST_ARRAY`
*
* @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 visitArray(Node $node) : UnionType
{
if (!empty($node->children) && $node->children[0] instanceof Node && $node->children[0]->kind == \ast\AST_ARRAY_ELEM) {
$element_types = [];
// Check the first 5 (completely arbitrary) elements
// and assume the rest are the same type
for ($i = 0; $i < 5; $i++) {
// Check to see if we're out of elements
if (empty($node->children[$i])) {
break;
}
if ($node->children[$i]->children['value'] instanceof Node) {
$element_types[] = UnionType::fromNode($this->context, $this->code_base, $node->children[$i]->children['value']);
} else {
$element_types[] = Type::fromObject($node->children[$i]->children['value'])->asUnionType();
}
}
$element_types = array_values(array_unique($element_types));
if (count($element_types) == 1) {
return $element_types[0]->asGenericArrayTypes();
}
}
return ArrayType::instance()->asUnionType();
}