private function checkForTypeFunctionCall(\PHPParser_Node $node, LinkedFlowScope $blindScope, $outcome) { if ($node instanceof \PHPParser_Node_Stmt_Case) { $left = $node->getAttribute('parent')->cond; $right = $node->cond; } else { $left = $node->left; $right = $node->right; } // Handle gettype() function calls. $gettypeNode = $stringNode = null; if (NodeUtil::isGetTypeFunctionCall($left) && $right instanceof \PHPParser_Node_Scalar_String) { $gettypeNode = $left; $stringNode = $right; } else { if (NodeUtil::isGetTypeFunctionCall($right) && $left instanceof \PHPParser_Node_Scalar_String) { $gettypeNode = $right; $stringNode = $left; } } if (null !== $gettypeNode && null !== $stringNode && isset($gettypeNode->args[0])) { $operandNode = $gettypeNode->args[0]->value; $operandType = $this->getTypeIfRefinable($operandNode, $blindScope); if (null !== $operandType) { $resultEqualsValue = $node instanceof \PHPParser_Node_Expr_Equal || $node instanceof \PHPParser_Node_Expr_Identical || $node instanceof \PHPParser_Node_Stmt_Case; if (!$outcome) { $resultEqualsValue = !$resultEqualsValue; } return $this->caseGettypeFunctionCall($operandNode, $operandType, $stringNode->value, $resultEqualsValue, $blindScope); } } // Handle get_class() function calls. $getClassNode = $stringNode = null; if ('get_class' === NodeUtil::getFunctionName($left) && $right instanceof \PHPParser_Node_Scalar_String) { $getClassNode = $left; $stringNode = $right; } else { if ('get_class' === NodeUtil::getFunctionName($right) && $left instanceof \PHPParser_Node_Scalar_String) { $getClassNode = $right; $stringNode = $left; } } if (null !== $getClassNode && null !== $stringNode && isset($getClassNode->args[0])) { $operandNode = $getClassNode->args[0]->value; $operandType = $this->getTypeIfRefinable($operandNode, $blindScope); if (null !== $operandType) { $resultEqualsValue = $node instanceof \PHPParser_Node_Expr_Equal || $node instanceof \PHPParser_Node_Expr_Identical || $node instanceof \PHPParser_Node_Stmt_Case; if (!$outcome) { $resultEqualsValue = !$resultEqualsValue; } return $this->caseGetClassFunctionCall($operandNode, $operandType, $stringNode->value, $resultEqualsValue, $blindScope); } } // Handle is_??? and assert function calls as well as instanceof checks. $typeFunctionNode = $booleanNode = null; if ((NodeUtil::isTypeFunctionCall($left) || NodeUtil::isMaybeFunctionCall($left, 'assert') || $left instanceof \PHPParser_Node_Expr_Instanceof) && NodeUtil::isBoolean($right)) { $typeFunctionNode = $left; $booleanNode = $right; } else { if ((NodeUtil::isTypeFunctionCall($right) || NodeUtil::isMaybeFunctionCall($right, 'assert') || $right instanceof \PHPParser_Node_Expr_Instanceof) && NodeUtil::isBoolean($left)) { $typeFunctionNode = $right; $booleanNode = $left; } } if (null !== $booleanNode && null !== $typeFunctionNode) { $expectedOutcome = NodeUtil::getBooleanValue($booleanNode) ? $outcome : !$outcome; if ($typeFunctionNode instanceof \PHPParser_Node_Expr_Instanceof) { return $this->firstPreciserScopeKnowingConditionOutcome($typeFunctionNode, $blindScope, $expectedOutcome); } if (isset($typeFunctionNode->args[0])) { if (NodeUtil::isMaybeFunctionCall($typeFunctionNode, 'assert')) { return $this->firstPreciserScopeKnowingConditionOutcome($typeFunctionNode->args[0]->value, $blindScope, $expectedOutcome); } return $this->caseTypeFunctionCall($typeFunctionNode->name, $typeFunctionNode->args[0], $blindScope, $expectedOutcome); } } return null; }
public function getPreciserFunctionReturnTypeKnowingArguments(GlobalFunction $function, array $argValues, array $argTypes) { switch ($function->getName()) { case 'version_compare': switch (count($argTypes)) { case 2: return $this->registry->getNativeType('integer'); case 3: return $this->registry->getNativeType('boolean'); default: return $this->registry->resolveType('integer|boolean'); } case 'unserialize': return $this->registry->getNativeType('unknown_checked'); case 'var_export': if (count($argValues) !== 2) { return null; } if (\Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::isBoolean($argValues[1]) && \Scrutinizer\PhpAnalyzer\PhpParser\NodeUtil::getBooleanValue($argValues[1]) === true) { return $this->registry->getNativeType('string'); } return null; case 'min': case 'max': switch (count($argTypes)) { case 0: return null; case 1: if ($argTypes[0]->isArrayType()) { return $argTypes[0]->getElementType(); } return null; default: // TODO: We could make this a bit smarter as some types are always considered // greater/smaller than other types. // See http://de1.php.net/manual/en/language.operators.comparison.php return $this->registry->createUnionType($argTypes); } case 'str_replace': case 'preg_filter': case 'preg_replace': case 'preg_replace_callback': if (isset($argTypes[2])) { if ($argTypes[2]->isUnknownType()) { return $this->registry->getNativeType('unknown'); } if ($argTypes[2]->isArrayType()) { return $this->registry->resolveType('array<string>'); } $nullableScalar = $this->registry->createNullableType($this->registry->getNativeType('scalar')); if ($argTypes[2]->isSubtypeOf($nullableScalar)) { return $this->registry->getNativeType('string'); } } return null; // Use the non-restricted type if we don't know. // Use the non-restricted type if we don't know. default: return null; } }