private function traverseCall(\PHPParser_Node $node, LinkedFlowScope $scope) { assert($node instanceof \PHPParser_Node_Expr_MethodCall || $node instanceof \PHPParser_Node_Expr_FuncCall || $node instanceof \PHPParser_Node_Expr_StaticCall); $scope = $this->traverseChildren($node, $scope); // Propagate type for constants if (NodeUtil::isConstantDefinition($node)) { $constantName = $node->args[0]->value->value; if ($constant = $this->typeRegistry->getConstant($constantName)) { $constant->setPhpType($this->getType($node->args[1]->value)); } } // If the assert function is called inside a block node, we are just going to assume // that some sort of exception is thrown if the assert fails (and it will not silently // be ignored). // TODO: This needs to be extracted as a general concept where we can have different // effects for functions/methods. For example, array_pop affects the known item // types of arrays on which it is called. if ($node->getAttribute('parent') instanceof BlockNode && NodeUtil::isMaybeFunctionCall($node, 'assert') && isset($node->args[0])) { $scope = $this->reverseInterpreter->getPreciserScopeKnowingConditionOutcome($node->args[0]->value, $scope, true); } $returnType = null; if (null !== ($function = $this->typeRegistry->getCalledFunctionByNode($node))) { $node->setAttribute('returns_by_ref', $function->isReturnByRef()); $argValues = $argTypes = array(); foreach ($node->args as $arg) { $argValues[] = $arg->value; $argTypes[] = $this->getType($arg); } if ($function instanceof ContainerMethodInterface) { $objType = $function->getContainer(); $maybeReturnType = $function->getReturnType(); if (null !== ($restrictedType = $this->methodInterpreter->getPreciserMethodReturnTypeKnowingArguments($function, $argValues, $argTypes))) { $maybeReturnType = $restrictedType; } $returnType = $this->updateThisReference($node instanceof \PHPParser_Node_Expr_MethodCall ? $node->var : $node->class, $scope->getTypeOfThis(), $objType, $maybeReturnType); } else { if ($function instanceof GlobalFunction) { if (null !== ($restrictedType = $this->functionInterpreter->getPreciserFunctionReturnTypeKnowingArguments($function, $argValues, $argTypes))) { $returnType = $restrictedType; } else { if (null === $returnType) { $returnType = $function->getReturnType(); } } } else { throw new \LogicException(sprintf('Unknown function "%s".', get_class($function))); } } } $node->setAttribute('type', $returnType ?: $this->typeRegistry->getNativeType('unknown')); return $scope; }