2016-12-28 21:52:44 +01:00
|
|
|
|
<?php
|
|
|
|
|
namespace Psalm\Checker\Statements\Expression;
|
|
|
|
|
|
|
|
|
|
use PhpParser;
|
|
|
|
|
use Psalm\Checker\ClassLikeChecker;
|
|
|
|
|
use Psalm\Checker\Statements\ExpressionChecker;
|
|
|
|
|
use Psalm\Checker\TypeChecker;
|
|
|
|
|
use Psalm\CodeLocation;
|
2017-04-06 21:36:22 +02:00
|
|
|
|
use Psalm\Issue\TypeDoesNotContainNull;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
use Psalm\Issue\TypeDoesNotContainType;
|
2017-10-23 01:53:53 +02:00
|
|
|
|
use Psalm\Issue\UnevaluatedCode;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
use Psalm\IssueBuffer;
|
2017-01-07 20:35:07 +01:00
|
|
|
|
use Psalm\StatementsSource;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
use Psalm\Type;
|
|
|
|
|
|
2017-01-07 20:35:07 +01:00
|
|
|
|
class AssertionFinder
|
2016-12-28 21:52:44 +01:00
|
|
|
|
{
|
|
|
|
|
const ASSIGNMENT_TO_RIGHT = 1;
|
|
|
|
|
const ASSIGNMENT_TO_LEFT = -1;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets all the type assertions in a conditional
|
|
|
|
|
*
|
|
|
|
|
* @param PhpParser\Node\Expr $conditional
|
2017-01-07 20:35:07 +01:00
|
|
|
|
* @param string|null $this_class_name
|
|
|
|
|
* @param StatementsSource $source
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return array<string, string>
|
|
|
|
|
*/
|
|
|
|
|
public static function getAssertions(
|
|
|
|
|
PhpParser\Node\Expr $conditional,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
StatementsSource $source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
) {
|
|
|
|
|
$if_types = [];
|
|
|
|
|
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$project_checker = $source->getFileChecker()->project_checker;
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\Instanceof_) {
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$instanceof_type = self::getInstanceOfTypes($conditional, $this_class_name, $source);
|
2016-12-28 21:52:44 +01:00
|
|
|
|
|
|
|
|
|
if ($instanceof_type) {
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->expr,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = $instanceof_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
)) {
|
2017-10-22 17:57:41 +02:00
|
|
|
|
$if_types[$var_name] = '!falsy';
|
2016-12-28 21:52:44 +01:00
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\Assign) {
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->var,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
2017-10-22 17:57:41 +02:00
|
|
|
|
$if_types[$var_name] = '!falsy';
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BooleanNot) {
|
|
|
|
|
$if_types_to_negate = self::getAssertions(
|
|
|
|
|
$conditional->expr,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return TypeChecker::negateTypes($if_types_to_negate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical ||
|
|
|
|
|
$conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
|
|
|
|
|
) {
|
|
|
|
|
$null_position = self::hasNullVariable($conditional);
|
|
|
|
|
$false_position = self::hasFalseVariable($conditional);
|
2017-10-22 18:09:22 +02:00
|
|
|
|
$true_position = self::hasTrueVariable($conditional);
|
2016-12-28 21:52:44 +01:00
|
|
|
|
$gettype_position = self::hasGetTypeCheck($conditional);
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$getclass_position = self::hasGetClassCheck($conditional);
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$typed_value_position = self::hasTypedValueComparison($conditional);
|
2016-12-28 21:52:44 +01:00
|
|
|
|
|
|
|
|
|
if ($null_position !== null) {
|
|
|
|
|
if ($null_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->left;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} elseif ($null_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->right;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$null_position value');
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$var_type = isset($base_conditional->inferredType) ? $base_conditional->inferredType : null;
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($var_name) {
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
|
|
|
|
$if_types[$var_name] = 'null';
|
|
|
|
|
} else {
|
2017-10-22 17:57:41 +02:00
|
|
|
|
$if_types[$var_name] = 'falsy';
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
2017-04-06 20:53:45 +02:00
|
|
|
|
} elseif ($var_type && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
2017-04-06 21:05:23 +02:00
|
|
|
|
$null_type = Type::getNull();
|
2017-04-06 20:53:45 +02:00
|
|
|
|
|
|
|
|
|
if (!TypeChecker::isContainedBy(
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$project_checker,
|
2017-04-06 20:53:45 +02:00
|
|
|
|
$var_type,
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$null_type
|
2017-04-06 20:53:45 +02:00
|
|
|
|
) && !TypeChecker::isContainedBy(
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$project_checker,
|
2017-04-06 20:53:45 +02:00
|
|
|
|
$null_type,
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$var_type
|
2017-04-06 20:53:45 +02:00
|
|
|
|
)) {
|
|
|
|
|
if (IssueBuffer::accepts(
|
2017-04-06 21:36:22 +02:00
|
|
|
|
new TypeDoesNotContainNull(
|
2017-04-06 20:53:45 +02:00
|
|
|
|
$var_type . ' does not contain ' . $null_type,
|
|
|
|
|
new CodeLocation($source, $conditional)
|
|
|
|
|
),
|
|
|
|
|
$source->getSuppressedIssues()
|
|
|
|
|
)) {
|
|
|
|
|
// fall through
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
if ($true_position) {
|
2017-10-22 18:09:22 +02:00
|
|
|
|
if ($true_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->left;
|
2017-10-22 18:09:22 +02:00
|
|
|
|
} elseif ($true_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->right;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('Unrecognised position');
|
|
|
|
|
}
|
2017-10-22 18:09:22 +02:00
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::processFunctionCall(
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source,
|
|
|
|
|
false
|
|
|
|
|
);
|
2017-10-22 18:09:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
2017-10-22 18:09:22 +02:00
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '!falsy';
|
2017-10-23 01:11:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
return self::getAssertions($base_conditional, $this_class_name, $source);
|
2017-10-22 18:09:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($false_position) {
|
|
|
|
|
if ($false_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->left;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} elseif ($false_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->right;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$false_position value');
|
|
|
|
|
}
|
2017-04-06 20:53:45 +02:00
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::processFunctionCall(
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source,
|
|
|
|
|
true
|
|
|
|
|
);
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$var_type = isset($base_conditional->inferredType) ? $base_conditional->inferredType : null;
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($var_name) {
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
|
|
|
|
$if_types[$var_name] = 'false';
|
|
|
|
|
} else {
|
2017-10-22 17:57:41 +02:00
|
|
|
|
$if_types[$var_name] = 'falsy';
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
2017-10-23 01:11:28 +02:00
|
|
|
|
} elseif ($var_type) {
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
|
|
|
|
$false_type = Type::getFalse();
|
2017-04-06 20:53:45 +02:00
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
if (!TypeChecker::isContainedBy(
|
|
|
|
|
$project_checker,
|
|
|
|
|
$var_type,
|
|
|
|
|
$false_type
|
|
|
|
|
) && !TypeChecker::isContainedBy(
|
|
|
|
|
$project_checker,
|
|
|
|
|
$false_type,
|
|
|
|
|
$var_type
|
2017-04-06 20:53:45 +02:00
|
|
|
|
)) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
|
new TypeDoesNotContainType(
|
|
|
|
|
$var_type . ' does not contain ' . $false_type,
|
|
|
|
|
new CodeLocation($source, $conditional)
|
|
|
|
|
),
|
|
|
|
|
$source->getSuppressedIssues()
|
|
|
|
|
)) {
|
|
|
|
|
// fall through
|
|
|
|
|
}
|
2017-04-06 20:53:45 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-23 01:11:28 +02:00
|
|
|
|
|
|
|
|
|
$notif_types = self::getAssertions($base_conditional, $this_class_name, $source);
|
|
|
|
|
|
|
|
|
|
if (count($notif_types) === 1) {
|
|
|
|
|
$if_types = TypeChecker::negateTypes($notif_types);
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($gettype_position) {
|
|
|
|
|
if ($gettype_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$string_expr = $conditional->left;
|
|
|
|
|
$gettype_expr = $conditional->right;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} elseif ($gettype_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$string_expr = $conditional->right;
|
|
|
|
|
$gettype_expr = $conditional->left;
|
2017-10-23 01:11:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$gettype_position value');
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:53:53 +02:00
|
|
|
|
/** @var PhpParser\Node\Expr\FuncCall $gettype_expr */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$gettype_expr->args[0]->value,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/** @var PhpParser\Node\Scalar\String_ $string_expr */
|
|
|
|
|
$var_type = $string_expr->value;
|
|
|
|
|
|
|
|
|
|
$file_checker = $source->getFileChecker();
|
|
|
|
|
|
|
|
|
|
if (!isset(ClassLikeChecker::$GETTYPE_TYPES[$var_type])) {
|
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
|
new UnevaluatedCode(
|
|
|
|
|
'gettype cannot return this value',
|
|
|
|
|
new CodeLocation($file_checker, $string_expr)
|
|
|
|
|
)
|
|
|
|
|
)) {
|
|
|
|
|
// fall through
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($var_name && $var_type) {
|
|
|
|
|
$if_types[$var_name] = $var_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($getclass_position) {
|
|
|
|
|
if ($getclass_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-26 21:07:36 +02:00
|
|
|
|
$whichclass_expr = $conditional->left;
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$getclass_expr = $conditional->right;
|
|
|
|
|
} elseif ($getclass_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-26 21:07:36 +02:00
|
|
|
|
$whichclass_expr = $conditional->right;
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$getclass_expr = $conditional->left;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$getclass_position value');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @var PhpParser\Node\Expr\FuncCall $getclass_expr */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$getclass_expr->args[0]->value,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
2017-10-26 21:07:36 +02:00
|
|
|
|
if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
$var_type = $whichclass_expr->value;
|
|
|
|
|
} elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
|
|
|
|
|
&& $whichclass_expr->class instanceof PhpParser\Node\Name
|
|
|
|
|
) {
|
|
|
|
|
$var_type = ClassLikeChecker::getFQCLNFromNameObject(
|
|
|
|
|
$whichclass_expr->class,
|
|
|
|
|
$source->getAliases()
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('Shouldn’t get here');
|
|
|
|
|
}
|
2017-10-23 01:53:53 +02:00
|
|
|
|
|
|
|
|
|
$file_checker = $source->getFileChecker();
|
|
|
|
|
|
|
|
|
|
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
2018-01-02 02:04:03 +01:00
|
|
|
|
$source,
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$var_type,
|
2017-10-26 21:07:36 +02:00
|
|
|
|
new CodeLocation($file_checker, $whichclass_expr),
|
2018-01-02 02:04:03 +01:00
|
|
|
|
$source->getSuppressedIssues(),
|
|
|
|
|
false
|
2017-10-23 01:53:53 +02:00
|
|
|
|
) === false
|
|
|
|
|
) {
|
|
|
|
|
// fall through
|
|
|
|
|
} else {
|
|
|
|
|
if ($var_name && $var_type) {
|
|
|
|
|
$if_types[$var_name] = $var_type;
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-07 20:35:07 +01:00
|
|
|
|
if ($typed_value_position) {
|
|
|
|
|
if ($typed_value_position === self::ASSIGNMENT_TO_RIGHT) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
/** @var PhpParser\Node\Expr $conditional->right */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->left,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
2017-04-06 20:57:00 +02:00
|
|
|
|
$other_type = isset($conditional->left->inferredType) ? $conditional->left->inferredType : null;
|
|
|
|
|
$var_type = isset($conditional->right->inferredType) ? $conditional->right->inferredType : null;
|
2017-01-07 20:35:07 +01:00
|
|
|
|
} elseif ($typed_value_position === self::ASSIGNMENT_TO_LEFT) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
/** @var PhpParser\Node\Expr $conditional->left */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->right,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
2017-04-06 20:57:00 +02:00
|
|
|
|
$var_type = isset($conditional->left->inferredType) ? $conditional->left->inferredType : null;
|
|
|
|
|
$other_type = isset($conditional->right->inferredType) ? $conditional->right->inferredType : null;
|
2017-10-23 01:11:28 +02:00
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$typed_value_position value');
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-06 20:53:45 +02:00
|
|
|
|
if ($var_type) {
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '^' . $var_type;
|
|
|
|
|
} elseif ($other_type && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
|
|
|
|
if (!TypeChecker::isContainedBy(
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$project_checker,
|
2017-04-06 20:53:45 +02:00
|
|
|
|
$var_type,
|
|
|
|
|
$other_type,
|
2017-04-07 00:11:09 +02:00
|
|
|
|
true
|
2017-04-06 20:53:45 +02:00
|
|
|
|
) && !TypeChecker::isContainedBy(
|
2017-07-29 21:05:06 +02:00
|
|
|
|
$project_checker,
|
2017-04-06 20:53:45 +02:00
|
|
|
|
$other_type,
|
|
|
|
|
$var_type,
|
2017-04-07 00:11:09 +02:00
|
|
|
|
true
|
2017-04-06 20:53:45 +02:00
|
|
|
|
)) {
|
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
|
new TypeDoesNotContainType(
|
|
|
|
|
$var_type . ' does not contain ' . $other_type,
|
|
|
|
|
new CodeLocation($source, $conditional)
|
|
|
|
|
),
|
|
|
|
|
$source->getSuppressedIssues()
|
|
|
|
|
)) {
|
|
|
|
|
// fall through
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-06 20:53:45 +02:00
|
|
|
|
$var_type = isset($conditional->left->inferredType) ? $conditional->left->inferredType : null;
|
|
|
|
|
$other_type = isset($conditional->right->inferredType) ? $conditional->right->inferredType : null;
|
|
|
|
|
|
|
|
|
|
if ($var_type && $other_type && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
|
2017-07-29 21:05:06 +02:00
|
|
|
|
if (!TypeChecker::canBeIdenticalTo($project_checker, $var_type, $other_type)) {
|
2017-04-06 20:53:45 +02:00
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
|
new TypeDoesNotContainType(
|
|
|
|
|
$var_type . ' does not contain ' . $other_type,
|
|
|
|
|
new CodeLocation($source, $conditional)
|
|
|
|
|
),
|
|
|
|
|
$source->getSuppressedIssues()
|
|
|
|
|
)) {
|
|
|
|
|
// fall through
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical ||
|
|
|
|
|
$conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
|
|
|
|
|
) {
|
|
|
|
|
$null_position = self::hasNullVariable($conditional);
|
|
|
|
|
$false_position = self::hasFalseVariable($conditional);
|
|
|
|
|
$true_position = self::hasTrueVariable($conditional);
|
2017-10-23 02:17:04 +02:00
|
|
|
|
$gettype_position = self::hasGetTypeCheck($conditional);
|
|
|
|
|
$getclass_position = self::hasGetClassCheck($conditional);
|
2016-12-28 21:52:44 +01:00
|
|
|
|
|
|
|
|
|
if ($null_position !== null) {
|
|
|
|
|
if ($null_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->left;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} elseif ($null_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->right;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} else {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
throw new \UnexpectedValueException('Bad null variable position');
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($var_name) {
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
|
|
|
|
|
$if_types[$var_name] = '!null';
|
|
|
|
|
} else {
|
2017-10-22 17:57:41 +02:00
|
|
|
|
$if_types[$var_name] = '!falsy';
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($false_position) {
|
|
|
|
|
if ($false_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->left;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} elseif ($false_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$base_conditional = $conditional->right;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
} else {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
throw new \UnexpectedValueException('Bad false variable position');
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:11:28 +02:00
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$base_conditional,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($var_name) {
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
|
|
|
|
|
$if_types[$var_name] = '!false';
|
|
|
|
|
} else {
|
2017-10-22 17:57:41 +02:00
|
|
|
|
$if_types[$var_name] = '!falsy';
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($true_position) {
|
|
|
|
|
if ($true_position === self::ASSIGNMENT_TO_RIGHT) {
|
|
|
|
|
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::processFunctionCall(
|
2016-12-28 21:52:44 +01:00
|
|
|
|
$conditional->left,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source,
|
2016-12-28 21:52:44 +01:00
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} elseif ($true_position === self::ASSIGNMENT_TO_LEFT) {
|
|
|
|
|
if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::processFunctionCall(
|
2016-12-28 21:52:44 +01:00
|
|
|
|
$conditional->right,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source,
|
2016-12-28 21:52:44 +01:00
|
|
|
|
true
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2017-10-23 01:11:28 +02:00
|
|
|
|
throw new \UnexpectedValueException('Bad null variable position');
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return [];
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 02:17:04 +02:00
|
|
|
|
if ($gettype_position) {
|
|
|
|
|
if ($gettype_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-10-26 21:07:36 +02:00
|
|
|
|
$whichclass_expr = $conditional->left;
|
2017-10-23 02:17:04 +02:00
|
|
|
|
$gettype_expr = $conditional->right;
|
|
|
|
|
} elseif ($gettype_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-10-26 21:07:36 +02:00
|
|
|
|
$whichclass_expr = $conditional->right;
|
2017-10-23 02:17:04 +02:00
|
|
|
|
$gettype_expr = $conditional->left;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$gettype_position value');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @var PhpParser\Node\Expr\FuncCall $gettype_expr */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$gettype_expr->args[0]->value,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
2017-10-26 21:07:36 +02:00
|
|
|
|
if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
$var_type = $whichclass_expr->value;
|
|
|
|
|
} elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
|
|
|
|
|
&& $whichclass_expr->class instanceof PhpParser\Node\Name
|
|
|
|
|
) {
|
|
|
|
|
$var_type = ClassLikeChecker::getFQCLNFromNameObject(
|
|
|
|
|
$whichclass_expr->class,
|
|
|
|
|
$source->getAliases()
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('Shouldn’t get here');
|
|
|
|
|
}
|
2017-10-23 02:17:04 +02:00
|
|
|
|
|
|
|
|
|
$file_checker = $source->getFileChecker();
|
|
|
|
|
|
|
|
|
|
if (!isset(ClassLikeChecker::$GETTYPE_TYPES[$var_type])) {
|
|
|
|
|
if (IssueBuffer::accepts(
|
|
|
|
|
new UnevaluatedCode(
|
|
|
|
|
'gettype cannot return this value',
|
2017-10-26 21:07:36 +02:00
|
|
|
|
new CodeLocation($file_checker, $whichclass_expr)
|
2017-10-23 02:17:04 +02:00
|
|
|
|
)
|
|
|
|
|
)) {
|
|
|
|
|
// fall through
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($var_name && $var_type) {
|
|
|
|
|
$if_types[$var_name] = '!' . $var_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($getclass_position) {
|
|
|
|
|
if ($getclass_position === self::ASSIGNMENT_TO_RIGHT) {
|
2017-11-06 18:04:38 +01:00
|
|
|
|
$whichclass_expr = $conditional->left;
|
2017-10-23 02:17:04 +02:00
|
|
|
|
$getclass_expr = $conditional->right;
|
|
|
|
|
} elseif ($getclass_position === self::ASSIGNMENT_TO_LEFT) {
|
2017-11-06 18:04:38 +01:00
|
|
|
|
$whichclass_expr = $conditional->right;
|
2017-10-23 02:17:04 +02:00
|
|
|
|
$getclass_expr = $conditional->left;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$getclass_position value');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @var PhpParser\Node\Expr\FuncCall $getclass_expr */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$getclass_expr->args[0]->value,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
2017-11-06 18:04:38 +01:00
|
|
|
|
if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
$var_type = $whichclass_expr->value;
|
|
|
|
|
} elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
|
|
|
|
|
&& $whichclass_expr->class instanceof PhpParser\Node\Name
|
|
|
|
|
) {
|
|
|
|
|
$var_type = ClassLikeChecker::getFQCLNFromNameObject(
|
|
|
|
|
$whichclass_expr->class,
|
|
|
|
|
$source->getAliases()
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('Shouldn’t get here');
|
|
|
|
|
}
|
2017-10-23 02:17:04 +02:00
|
|
|
|
|
|
|
|
|
$file_checker = $source->getFileChecker();
|
|
|
|
|
|
|
|
|
|
if (ClassLikeChecker::checkFullyQualifiedClassLikeName(
|
2018-01-02 02:04:03 +01:00
|
|
|
|
$source,
|
2017-10-23 02:17:04 +02:00
|
|
|
|
$var_type,
|
2017-11-06 18:04:38 +01:00
|
|
|
|
new CodeLocation($file_checker, $whichclass_expr),
|
2018-01-02 02:04:03 +01:00
|
|
|
|
$source->getSuppressedIssues(),
|
|
|
|
|
false
|
2017-10-23 02:17:04 +02:00
|
|
|
|
) === false
|
|
|
|
|
) {
|
|
|
|
|
// fall through
|
|
|
|
|
} else {
|
|
|
|
|
if ($var_name && $var_type) {
|
|
|
|
|
$if_types[$var_name] = '!' . $var_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-14 02:06:19 +01:00
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater) {
|
|
|
|
|
$typed_value_position = self::hasTypedValueComparison($conditional);
|
|
|
|
|
|
|
|
|
|
if ($typed_value_position) {
|
|
|
|
|
if ($typed_value_position === self::ASSIGNMENT_TO_RIGHT) {
|
|
|
|
|
/** @var PhpParser\Node\Expr $conditional->right */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->left,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
2017-12-14 02:48:01 +01:00
|
|
|
|
} elseif ($typed_value_position === self::ASSIGNMENT_TO_LEFT) {
|
|
|
|
|
$var_name = null;
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$typed_value_position value');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '^isset';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller) {
|
|
|
|
|
$typed_value_position = self::hasTypedValueComparison($conditional);
|
|
|
|
|
|
|
|
|
|
if ($typed_value_position) {
|
|
|
|
|
if ($typed_value_position === self::ASSIGNMENT_TO_RIGHT) {
|
|
|
|
|
$var_name = null;
|
2017-12-14 02:06:19 +01:00
|
|
|
|
} elseif ($typed_value_position === self::ASSIGNMENT_TO_LEFT) {
|
|
|
|
|
/** @var PhpParser\Node\Expr $conditional->left */
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->right,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
throw new \UnexpectedValueException('$typed_value_position value');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '^isset';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\FuncCall) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::processFunctionCall($conditional, $this_class_name, $source, false);
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\Empty_) {
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->expr,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
2017-12-05 18:14:10 +01:00
|
|
|
|
$if_types[$var_name] = 'empty';
|
|
|
|
|
} else {
|
|
|
|
|
// look for any variables we *can* use for an isset assertion
|
|
|
|
|
$array_root = $conditional->expr;
|
|
|
|
|
|
|
|
|
|
while ($array_root instanceof PhpParser\Node\Expr\ArrayDimFetch && !$var_name) {
|
|
|
|
|
$array_root = $array_root->var;
|
|
|
|
|
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$array_root,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '^empty';
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\Isset_) {
|
|
|
|
|
foreach ($conditional->vars as $isset_var) {
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$isset_var,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
2017-01-31 06:34:06 +01:00
|
|
|
|
$if_types[$var_name] = 'isset';
|
2017-01-31 07:35:44 +01:00
|
|
|
|
} else {
|
|
|
|
|
// look for any variables we *can* use for an isset assertion
|
|
|
|
|
$array_root = $isset_var;
|
|
|
|
|
|
|
|
|
|
while ($array_root instanceof PhpParser\Node\Expr\ArrayDimFetch && !$var_name) {
|
|
|
|
|
$array_root = $array_root->var;
|
|
|
|
|
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$array_root,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '^isset';
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-18 02:50:47 +01:00
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Coalesce) {
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$conditional->left,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = 'isset';
|
|
|
|
|
} else {
|
|
|
|
|
// look for any variables we *can* use for an isset assertion
|
|
|
|
|
$array_root = $conditional->left;
|
|
|
|
|
|
|
|
|
|
while ($array_root instanceof PhpParser\Node\Expr\ArrayDimFetch && !$var_name) {
|
|
|
|
|
$array_root = $array_root->var;
|
|
|
|
|
|
|
|
|
|
$var_name = ExpressionChecker::getArrayVarId(
|
|
|
|
|
$array_root,
|
|
|
|
|
$this_class_name,
|
|
|
|
|
$source
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($var_name) {
|
|
|
|
|
$if_types[$var_name] = '^isset';
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
|
|
|
|
|
return $if_types;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $expr
|
2017-01-07 20:35:07 +01:00
|
|
|
|
* @param string|null $this_class_name
|
|
|
|
|
* @param StatementsSource $source
|
2017-05-25 04:07:49 +02:00
|
|
|
|
* @param bool $negate
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2017-10-23 01:53:53 +02:00
|
|
|
|
* @return array<string, string>
|
2016-12-28 21:52:44 +01:00
|
|
|
|
*/
|
|
|
|
|
protected static function processFunctionCall(
|
|
|
|
|
PhpParser\Node\Expr\FuncCall $expr,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
StatementsSource $source,
|
2016-12-28 21:52:44 +01:00
|
|
|
|
$negate = false
|
|
|
|
|
) {
|
|
|
|
|
$prefix = $negate ? '!' : '';
|
|
|
|
|
|
|
|
|
|
$first_var_name = isset($expr->args[0]->value)
|
|
|
|
|
? ExpressionChecker::getArrayVarId(
|
|
|
|
|
$expr->args[0]->value,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
$source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
)
|
|
|
|
|
: null;
|
|
|
|
|
|
2017-10-23 01:53:53 +02:00
|
|
|
|
$if_types = [];
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
if (self::hasNullCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'null';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasIsACheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
2018-01-23 21:46:14 +01:00
|
|
|
|
$first_arg = $expr->args[1]->value;
|
|
|
|
|
|
|
|
|
|
if ($first_arg instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . $first_arg->value;
|
|
|
|
|
} elseif ($first_arg instanceof PhpParser\Node\Expr\ClassConstFetch
|
|
|
|
|
&& $first_arg->class instanceof PhpParser\Node\Name
|
|
|
|
|
&& is_string($first_arg->name)
|
|
|
|
|
&& strtolower($first_arg->name) === 'class'
|
|
|
|
|
) {
|
|
|
|
|
$class_node = $first_arg->class;
|
|
|
|
|
|
|
|
|
|
if ($class_node->parts === ['static'] || $class_node->parts === ['self']) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . $this_class_name;
|
|
|
|
|
} elseif ($class_node->parts === ['parent']) {
|
|
|
|
|
// do nothing
|
|
|
|
|
} else {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . ClassLikeChecker::getFQCLNFromNameObject(
|
|
|
|
|
$class_node,
|
|
|
|
|
$source->getAliases()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasArrayCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'array';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasBoolCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'bool';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasStringCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'string';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasObjectCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'object';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasNumericCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'numeric';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasIntCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'int';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasFloatCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'float';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasResourceCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'resource';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasScalarCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'scalar';
|
|
|
|
|
}
|
|
|
|
|
} elseif (self::hasCallableCheck($expr)) {
|
|
|
|
|
if ($first_var_name) {
|
|
|
|
|
$if_types[$first_var_name] = $prefix . 'callable';
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-23 01:53:53 +02:00
|
|
|
|
|
|
|
|
|
return $if_types;
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\Instanceof_ $stmt
|
2017-01-07 20:35:07 +01:00
|
|
|
|
* @param string|null $this_class_name
|
|
|
|
|
* @param StatementsSource $source
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return string|null
|
|
|
|
|
*/
|
|
|
|
|
protected static function getInstanceOfTypes(
|
|
|
|
|
PhpParser\Node\Expr\Instanceof_ $stmt,
|
|
|
|
|
$this_class_name,
|
2017-01-07 20:35:07 +01:00
|
|
|
|
StatementsSource $source
|
2016-12-28 21:52:44 +01:00
|
|
|
|
) {
|
|
|
|
|
if ($stmt->class instanceof PhpParser\Node\Name) {
|
2017-11-15 03:56:29 +01:00
|
|
|
|
if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
$instanceof_class = ClassLikeChecker::getFQCLNFromNameObject(
|
|
|
|
|
$stmt->class,
|
2017-07-25 22:11:02 +02:00
|
|
|
|
$source->getAliases()
|
2016-12-28 21:52:44 +01:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return $instanceof_class;
|
2018-01-23 20:46:46 +01:00
|
|
|
|
} elseif ($this_class_name
|
|
|
|
|
&& (in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true))
|
|
|
|
|
) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
return $this_class_name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\BinaryOp $conditional
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return int|null
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasNullVariable(PhpParser\Node\Expr\BinaryOp $conditional)
|
|
|
|
|
{
|
|
|
|
|
if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
|
$conditional->right->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->right->name->parts[0]) === 'null') {
|
|
|
|
|
return self::ASSIGNMENT_TO_RIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
|
$conditional->left->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->left->name->parts[0]) === 'null') {
|
|
|
|
|
return self::ASSIGNMENT_TO_LEFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\BinaryOp $conditional
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return int|null
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasFalseVariable(PhpParser\Node\Expr\BinaryOp $conditional)
|
|
|
|
|
{
|
|
|
|
|
if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
|
$conditional->right->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->right->name->parts[0]) === 'false') {
|
|
|
|
|
return self::ASSIGNMENT_TO_RIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
|
$conditional->left->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->left->name->parts[0]) === 'false') {
|
|
|
|
|
return self::ASSIGNMENT_TO_LEFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\BinaryOp $conditional
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return int|null
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasTrueVariable(PhpParser\Node\Expr\BinaryOp $conditional)
|
|
|
|
|
{
|
|
|
|
|
if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
|
$conditional->right->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->right->name->parts[0]) === 'true') {
|
|
|
|
|
return self::ASSIGNMENT_TO_RIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch &&
|
|
|
|
|
$conditional->left->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->left->name->parts[0]) === 'true') {
|
|
|
|
|
return self::ASSIGNMENT_TO_LEFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\BinaryOp $conditional
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return false|int
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasGetTypeCheck(PhpParser\Node\Expr\BinaryOp $conditional)
|
|
|
|
|
{
|
|
|
|
|
if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall &&
|
|
|
|
|
$conditional->right->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->right->name->parts[0]) === 'gettype' &&
|
|
|
|
|
$conditional->left instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
return self::ASSIGNMENT_TO_RIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall &&
|
|
|
|
|
$conditional->left->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->left->name->parts[0]) === 'gettype' &&
|
|
|
|
|
$conditional->right instanceof PhpParser\Node\Scalar\String_) {
|
|
|
|
|
return self::ASSIGNMENT_TO_LEFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-23 01:53:53 +02:00
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\BinaryOp $conditional
|
|
|
|
|
*
|
|
|
|
|
* @return false|int
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasGetClassCheck(PhpParser\Node\Expr\BinaryOp $conditional)
|
|
|
|
|
{
|
|
|
|
|
if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall &&
|
|
|
|
|
$conditional->right->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->right->name->parts[0]) === 'get_class' &&
|
2017-12-01 01:00:09 +01:00
|
|
|
|
(
|
|
|
|
|
$conditional->left instanceof PhpParser\Node\Scalar\String_
|
2017-10-26 21:07:36 +02:00
|
|
|
|
|| ($conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch
|
2017-11-06 18:04:38 +01:00
|
|
|
|
&& $conditional->left->class instanceof PhpParser\Node\Name
|
|
|
|
|
&& is_string($conditional->left->name)
|
|
|
|
|
&& strtolower($conditional->left->name) === 'class')
|
2017-10-26 21:07:36 +02:00
|
|
|
|
)
|
|
|
|
|
) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::ASSIGNMENT_TO_RIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall &&
|
|
|
|
|
$conditional->left->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
strtolower($conditional->left->name->parts[0]) === 'get_class' &&
|
2017-12-01 01:00:09 +01:00
|
|
|
|
(
|
|
|
|
|
$conditional->right instanceof PhpParser\Node\Scalar\String_
|
2017-10-26 21:07:36 +02:00
|
|
|
|
|| ($conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch
|
2017-11-06 18:04:38 +01:00
|
|
|
|
&& $conditional->right->class instanceof PhpParser\Node\Name
|
|
|
|
|
&& is_string($conditional->right->name)
|
|
|
|
|
&& strtolower($conditional->right->name) === 'class')
|
2017-10-26 21:07:36 +02:00
|
|
|
|
)
|
|
|
|
|
) {
|
2017-10-23 01:53:53 +02:00
|
|
|
|
return self::ASSIGNMENT_TO_LEFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-28 21:52:44 +01:00
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\BinaryOp $conditional
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return false|int
|
|
|
|
|
*/
|
2017-01-07 20:35:07 +01:00
|
|
|
|
protected static function hasTypedValueComparison(PhpParser\Node\Expr\BinaryOp $conditional)
|
2016-12-28 21:52:44 +01:00
|
|
|
|
{
|
2017-12-14 02:06:19 +01:00
|
|
|
|
if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-09 21:05:48 +01:00
|
|
|
|
if (isset($conditional->right->inferredType)
|
|
|
|
|
&& count($conditional->right->inferredType->getTypes()) === 1
|
|
|
|
|
) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
return self::ASSIGNMENT_TO_RIGHT;
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-09 21:05:48 +01:00
|
|
|
|
if (isset($conditional->left->inferredType)
|
|
|
|
|
&& count($conditional->left->inferredType->getTypes()) === 1
|
|
|
|
|
) {
|
2016-12-28 21:52:44 +01:00
|
|
|
|
return self::ASSIGNMENT_TO_LEFT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasNullCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'is_null') {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasIsACheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
2018-01-23 21:46:14 +01:00
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name
|
|
|
|
|
&& strtolower($stmt->name->parts[0]) === 'is_a'
|
|
|
|
|
&& isset($stmt->args[1])
|
|
|
|
|
) {
|
|
|
|
|
$first_arg = $stmt->args[1]->value;
|
|
|
|
|
|
|
|
|
|
if ($first_arg instanceof PhpParser\Node\Scalar\String_
|
|
|
|
|
|| (
|
|
|
|
|
$first_arg instanceof PhpParser\Node\Expr\ClassConstFetch
|
|
|
|
|
&& $first_arg->class instanceof PhpParser\Node\Name
|
|
|
|
|
&& is_string($first_arg->name)
|
|
|
|
|
&& strtolower($first_arg->name) === 'class'
|
|
|
|
|
)
|
|
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-12-28 21:52:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasArrayCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'is_array') {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasStringCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'is_string') {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasBoolCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'is_bool') {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasObjectCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && $stmt->name->parts === ['is_object']) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasNumericCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && $stmt->name->parts === ['is_numeric']) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasIntCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
($stmt->name->parts === ['is_int'] ||
|
2017-05-25 04:07:49 +02:00
|
|
|
|
$stmt->name->parts === ['is_integer'] ||
|
2016-12-28 21:52:44 +01:00
|
|
|
|
$stmt->name->parts === ['is_long'])
|
|
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasFloatCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name &&
|
|
|
|
|
($stmt->name->parts === ['is_float'] ||
|
|
|
|
|
$stmt->name->parts === ['is_real'] ||
|
|
|
|
|
$stmt->name->parts === ['is_double'])
|
|
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasResourceCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && $stmt->name->parts === ['is_resource']) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasScalarCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && $stmt->name->parts === ['is_scalar']) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param PhpParser\Node\Expr\FuncCall $stmt
|
2017-05-27 02:16:18 +02:00
|
|
|
|
*
|
2016-12-28 21:52:44 +01:00
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
protected static function hasCallableCheck(PhpParser\Node\Expr\FuncCall $stmt)
|
|
|
|
|
{
|
|
|
|
|
if ($stmt->name instanceof PhpParser\Node\Name && $stmt->name->parts === ['is_callable']) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|