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; }
private function enterNode(\PHPParser_Node $node) { if (NodeUtil::isMethodContainer($node)) { $this->commentParser->setCurrentClassName(implode("\\", $node->namespacedName->parts)); $this->commentParser->setImportedNamespaces($this->importedNamespaces); $this->classParser->setImportedNamespaces($this->importedNamespaces); $class = $this->classParser->parse($node); $this->classFiles[$class] = $this->phpFile; if ($this->typeRegistry->hasClass($class->getName(), TypeRegistry::LOOKUP_NO_CACHE)) { $this->analyzer->logger->warning(sprintf('The class "%s" has been defined more than once (maybe as a fixture for code generation). Ignoring the second definition.', $class->getName())); return; } $this->typeRegistry->registerClass($class); } else { if ($node instanceof \PHPParser_Node_Stmt_Function) { $this->functionParser->setImportedNamespaces($this->importedNamespaces); $function = $this->functionParser->parse($node); if ($this->typeRegistry->hasFunction($functionName = $function->getName(), false)) { $this->analyzer->logger->warning(sprintf('The function "%s" has been defined more than once (probably conditionally). Ignoring the second definition.', $functionName)); return; } $this->typeRegistry->registerFunction($function); } else { if (NodeUtil::isConstantDefinition($node)) { assert($node instanceof \PHPParser_Node_Expr_FuncCall); if (!$node->args[0]->value instanceof \PHPParser_Node_Scalar_String) { return; } $constant = new GlobalConstant($node->args[0]->value->value); $constant->setAstNode($node); $constant->setPhpType($this->typeRegistry->getNativeType('unknown')); if (null !== ($type = $node->args[1]->value->getAttribute('type'))) { $constant->setPhpType($type); } if ($this->typeRegistry->hasConstant($constant->getName(), false)) { $this->analyzer->logger->warning(sprintf('The constant "%s" was defined more than once. Ignoring all but first definition.', $constant->getName())); return; } $this->typeRegistry->registerConstant($constant); } } } }