src/Phan/AST/InferValue.php
<?php
declare(strict_types=1);
namespace Phan\AST;
use ast\flags;
use ast\Node;
use Closure;
use Error;
/**
* Utilities for inferring the value of operations in the analyzed code.
*/
class InferValue
{
/**
* @param Closure():(array|int|string|float|bool|null) $cb
* @return Node|array|int|string|float|bool|null
* @throws Error should be handled by caller, e.g. for `+[]`.
*/
private static function evalSuppressingErrors(Closure $cb)
{
return @\with_disabled_phan_error_handler($cb);
}
/**
* Compute result of a binary operator
* @param int|string|float|bool|null $left left hand side of operation
* @param int|string|float|bool|null $right right hand side of operation
* @param int $flags the flags on the node
* @return Node|array|int|string|float|bool|null
* Node is returned to indicate that the result could not be computed
* @throws Error that should be handled by caller, e.g. for `+[]`.
*/
public static function computeBinaryOpResult($left, $right, int $flags)
{
// Don't make errors in the analyzed code crash Phan (e.g. converting arrays to strings).
return self::evalSuppressingErrors(/** @return Node|array|int|string|float|bool|null */ static function () use ($left, $right, $flags) {
switch ($flags) {
case flags\BINARY_CONCAT:
return $left . $right;
case flags\BINARY_ADD:
return $left + $right;
case flags\BINARY_SUB:
return $left - $right;
case flags\BINARY_POW:
return $left ** $right;
case flags\BINARY_MUL:
return $left * $right;
case flags\BINARY_DIV:
return $left / $right;
case flags\BINARY_BOOL_AND:
// @phan-suppress-next-line PhanSuspiciousTruthyString this is deliberate
return $left && $right;
case flags\BINARY_BOOL_OR:
// @phan-suppress-next-line PhanSuspiciousTruthyString this is deliberate
return $left || $right;
case flags\BINARY_BOOL_XOR:
return $left xor $right;
case flags\BINARY_IS_IDENTICAL:
return $left === $right;
case flags\BINARY_IS_NOT_IDENTICAL:
return $left !== $right;
case flags\BINARY_IS_EQUAL:
return $left == $right;
case flags\BINARY_IS_NOT_EQUAL:
return $left != $right;
case flags\BINARY_IS_SMALLER:
return $left < $right;
case flags\BINARY_IS_SMALLER_OR_EQUAL:
return $left <= $right;
case flags\BINARY_IS_GREATER:
return $left > $right;
case flags\BINARY_IS_GREATER_OR_EQUAL:
return $left >= $right;
case flags\BINARY_SPACESHIP:
return $left <=> $right;
case flags\BINARY_COALESCE:
return $left ?? $right;
default:
return new Node();
}
});
}
/**
* Compute result of a unary operator for a PhanPluginBothLiteralsBinaryOp warning
* @param int|array|string|float|bool|null $operand operand of operation
* @param int $flags the flags on the node
* @return Node|array|int|string|float|bool|null
* Node is returned to indicate that the result could not be computed
* @throws Error that should be handled by caller, e.g. for `+[]`.
*/
public static function computeUnaryOpResult($operand, int $flags)
{
// Don't make errors in the analyzed code crash Phan (e.g. converting arrays to strings).
return self::evalSuppressingErrors(/** @return Node|array|int|string|float|bool|null */ static function () use ($operand, $flags) {
switch ($flags) {
case flags\UNARY_BOOL_NOT:
return !$operand;
case flags\UNARY_BITWISE_NOT:
// TODO: Check for acting on arrays
return ~$operand;
case flags\UNARY_MINUS:
return -$operand;
case flags\UNARY_PLUS:
return +$operand;
case flags\UNARY_SILENCE:
return $operand;
default:
return new Node();
}
});
}
}