Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php namespace Psalm\Internal\Codebase; use InvalidArgumentException; use Psalm\Excepti..
Decoded Output download
<?php
namespace Psalm\Internal\Codebase;
use InvalidArgumentException;
use Psalm\Exception\CircularReferenceException;
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Scanner\UnresolvedConstant\ArrayOffsetFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\ArraySpread;
use Psalm\Internal\Scanner\UnresolvedConstant\ArrayValue;
use Psalm\Internal\Scanner\UnresolvedConstant\ClassConstant;
use Psalm\Internal\Scanner\UnresolvedConstant\Constant;
use Psalm\Internal\Scanner\UnresolvedConstant\EnumNameFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\EnumPropertyFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\EnumValueFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\ScalarValue;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedAdditionOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBinaryOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseAnd;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseOr;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseXor;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedConcatOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedDivisionOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedMultiplicationOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedSubtractionOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedTernary;
use Psalm\Internal\Scanner\UnresolvedConstantComponent;
use Psalm\Type;
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TLiteralClassString;
use Psalm\Type\Atomic\TLiteralFloat;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTrue;
use Psalm\Type\Union;
use ReflectionProperty;
use function ctype_digit;
use function is_array;
use function is_float;
use function is_int;
use function is_string;
use function spl_object_id;
/**
* @internal
*/
final class ConstantTypeResolver
{
public static function resolve(
ClassLikes $classlikes,
UnresolvedConstantComponent $c,
?StatementsAnalyzer $statements_analyzer = null,
array $visited_constant_ids = []
): Atomic {
$c_id = spl_object_id($c);
if (isset($visited_constant_ids[$c_id])) {
throw new CircularReferenceException('Found a circular reference');
}
if ($c instanceof ScalarValue) {
return self::getLiteralTypeFromScalarValue($c->value);
}
if ($c instanceof UnresolvedBinaryOp) {
$left = self::resolve(
$classlikes,
$c->left,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
$right = self::resolve(
$classlikes,
$c->right,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($left instanceof TMixed || $right instanceof TMixed) {
return new TMixed;
}
if ($c instanceof UnresolvedConcatOp) {
if (($left instanceof TLiteralString
|| $left instanceof TLiteralFloat
|| $left instanceof TLiteralInt)
&& ($right instanceof TLiteralString
|| $right instanceof TLiteralFloat
|| $right instanceof TLiteralInt)
) {
return Type::getAtomicStringFromLiteral($left->value . $right->value);
}
return new TString();
}
if ($c instanceof UnresolvedAdditionOp
|| $c instanceof UnresolvedSubtractionOp
|| $c instanceof UnresolvedDivisionOp
|| $c instanceof UnresolvedMultiplicationOp
|| $c instanceof UnresolvedBitwiseOr
|| $c instanceof UnresolvedBitwiseXor
|| $c instanceof UnresolvedBitwiseAnd
) {
if (($left instanceof TLiteralFloat || $left instanceof TLiteralInt)
&& ($right instanceof TLiteralFloat || $right instanceof TLiteralInt)
) {
if ($c instanceof UnresolvedAdditionOp) {
return self::getLiteralTypeFromScalarValue($left->value + $right->value);
}
if ($c instanceof UnresolvedSubtractionOp) {
return self::getLiteralTypeFromScalarValue($left->value - $right->value);
}
if ($c instanceof UnresolvedDivisionOp) {
return self::getLiteralTypeFromScalarValue($left->value / $right->value);
}
if ($c instanceof UnresolvedBitwiseOr) {
return self::getLiteralTypeFromScalarValue($left->value | $right->value);
}
if ($c instanceof UnresolvedBitwiseXor) {
return self::getLiteralTypeFromScalarValue($left->value ^ $right->value);
}
if ($c instanceof UnresolvedBitwiseAnd) {
return self::getLiteralTypeFromScalarValue($left->value & $right->value);
}
return self::getLiteralTypeFromScalarValue($left->value * $right->value);
}
if ($left instanceof TKeyedArray && $right instanceof TKeyedArray) {
$type = new TKeyedArray(
$left->properties + $right->properties,
null,
);
return $type;
}
return new TMixed;
}
return new TMixed;
}
if ($c instanceof UnresolvedTernary) {
$cond = self::resolve(
$classlikes,
$c->cond,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
$if = $c->if ? self::resolve(
$classlikes,
$c->if,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
) : null;
$else = self::resolve(
$classlikes,
$c->else,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($cond instanceof TLiteralFloat
|| $cond instanceof TLiteralInt
|| $cond instanceof TLiteralString
) {
if ($cond->value) {
return $if ?? $cond;
}
} elseif ($cond instanceof TFalse || $cond instanceof TNull) {
return $else;
} elseif ($cond instanceof TTrue) {
return $if ?? $cond;
}
}
if ($c instanceof ArrayValue) {
$properties = [];
$auto_key = 0;
if (!$c->entries) {
return new TArray([Type::getNever(), Type::getNever()]);
}
$is_list = true;
foreach ($c->entries as $entry) {
if ($entry instanceof ArraySpread) {
$spread_array = self::resolve(
$classlikes,
$entry->array,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($spread_array instanceof TArray && $spread_array->isEmptyArray()) {
continue;
}
if (!$spread_array instanceof TKeyedArray) {
return new TArray([Type::getArrayKey(), Type::getMixed()]);
}
foreach ($spread_array->properties as $k => $spread_array_type) {
$properties[is_string($k) ? $k : $auto_key++] = $spread_array_type;
}
continue;
}
if ($entry->key) {
$key_type = self::resolve(
$classlikes,
$entry->key,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if (!$key_type instanceof TLiteralInt
|| $key_type->value !== $auto_key
) {
$is_list = false;
}
} else {
$key_type = new TLiteralInt($auto_key);
}
if ($key_type instanceof TLiteralInt
|| $key_type instanceof TLiteralString
) {
$key_value = $key_type->value;
if ($key_type instanceof TLiteralInt) {
$auto_key = $key_type->value + 1;
} elseif (ctype_digit($key_type->value)) {
$auto_key = ((int) $key_type->value) + 1;
}
} else {
return new TArray([Type::getArrayKey(), Type::getMixed()]);
}
$value_type = new Union([self::resolve(
$classlikes,
$entry->value,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
)]);
$properties[$key_value] = $value_type;
}
if (empty($properties)) {
$resolved_type = new TArray([
new Union([new TNever()]),
new Union([new TNever()]),
]);
} else {
$resolved_type = new TKeyedArray($properties, null, null, $is_list);
}
return $resolved_type;
}
if ($c instanceof ClassConstant) {
if ($c->name === 'class') {
return new TLiteralClassString($c->fqcln);
}
$found_type = $classlikes->getClassConstantType(
$c->fqcln,
$c->name,
ReflectionProperty::IS_PRIVATE,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($found_type) {
return $found_type->getSingleAtomic();
}
}
if ($c instanceof ArrayOffsetFetch) {
$var_type = self::resolve(
$classlikes,
$c->array,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
$offset_type = self::resolve(
$classlikes,
$c->offset,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($var_type instanceof TKeyedArray
&& ($offset_type instanceof TLiteralInt
|| $offset_type instanceof TLiteralString)
) {
$union = $var_type->properties[$offset_type->value] ?? null;
if ($union && $union->isSingle()) {
return $union->getSingleAtomic();
}
}
}
if ($c instanceof Constant) {
if ($statements_analyzer) {
$found_type = ConstFetchAnalyzer::getConstType(
$statements_analyzer,
$c->name,
$c->is_fully_qualified,
null,
);
if ($found_type) {
return $found_type->getSingleAtomic();
}
}
}
if ($c instanceof EnumPropertyFetch) {
if ($classlikes->enumExists($c->fqcln)) {
$enum_storage = $classlikes->getStorageFor($c->fqcln);
if (isset($enum_storage->enum_cases[$c->case])) {
if ($c instanceof EnumValueFetch) {
$value = $enum_storage->enum_cases[$c->case]->value;
if (is_string($value)) {
return Type::getString($value)->getSingleAtomic();
} elseif (is_int($value)) {
return Type::getInt(false, $value)->getSingleAtomic();
} elseif ($value instanceof UnresolvedConstantComponent) {
return self::resolve(
$classlikes,
$value,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
}
} elseif ($c instanceof EnumNameFetch) {
return Type::getString($c->case)->getSingleAtomic();
}
}
}
}
return new TMixed;
}
/**
* Note: This takes an array, but any array should only contain other arrays and scalars.
*
* @param array|string|int|float|bool|null $value
*/
public static function getLiteralTypeFromScalarValue($value): Atomic
{
if (is_array($value)) {
if (empty($value)) {
return Type::getEmptyArray()->getSingleAtomic();
}
$types = [];
/** @var array|scalar|null $val */
foreach ($value as $key => $val) {
$types[$key] = new Union([self::getLiteralTypeFromScalarValue($val)]);
}
return new TKeyedArray($types, null);
}
if (is_string($value)) {
return Type::getAtomicStringFromLiteral($value);
}
if (is_int($value)) {
return new TLiteralInt($value);
}
if (is_float($value)) {
return new TLiteralFloat($value);
}
if ($value === false) {
return new TFalse();
}
if ($value === true) {
return new TTrue();
}
if ($value === null) {
return new TNull();
}
throw new InvalidArgumentException('$value must be a scalar.');
}
}
?>
Did this file decode correctly?
Original Code
<?php
namespace Psalm\Internal\Codebase;
use InvalidArgumentException;
use Psalm\Exception\CircularReferenceException;
use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
use Psalm\Internal\Analyzer\StatementsAnalyzer;
use Psalm\Internal\Scanner\UnresolvedConstant\ArrayOffsetFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\ArraySpread;
use Psalm\Internal\Scanner\UnresolvedConstant\ArrayValue;
use Psalm\Internal\Scanner\UnresolvedConstant\ClassConstant;
use Psalm\Internal\Scanner\UnresolvedConstant\Constant;
use Psalm\Internal\Scanner\UnresolvedConstant\EnumNameFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\EnumPropertyFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\EnumValueFetch;
use Psalm\Internal\Scanner\UnresolvedConstant\ScalarValue;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedAdditionOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBinaryOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseAnd;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseOr;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseXor;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedConcatOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedDivisionOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedMultiplicationOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedSubtractionOp;
use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedTernary;
use Psalm\Internal\Scanner\UnresolvedConstantComponent;
use Psalm\Type;
use Psalm\Type\Atomic;
use Psalm\Type\Atomic\TArray;
use Psalm\Type\Atomic\TFalse;
use Psalm\Type\Atomic\TKeyedArray;
use Psalm\Type\Atomic\TLiteralClassString;
use Psalm\Type\Atomic\TLiteralFloat;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
use Psalm\Type\Atomic\TMixed;
use Psalm\Type\Atomic\TNever;
use Psalm\Type\Atomic\TNull;
use Psalm\Type\Atomic\TString;
use Psalm\Type\Atomic\TTrue;
use Psalm\Type\Union;
use ReflectionProperty;
use function ctype_digit;
use function is_array;
use function is_float;
use function is_int;
use function is_string;
use function spl_object_id;
/**
* @internal
*/
final class ConstantTypeResolver
{
public static function resolve(
ClassLikes $classlikes,
UnresolvedConstantComponent $c,
?StatementsAnalyzer $statements_analyzer = null,
array $visited_constant_ids = []
): Atomic {
$c_id = spl_object_id($c);
if (isset($visited_constant_ids[$c_id])) {
throw new CircularReferenceException('Found a circular reference');
}
if ($c instanceof ScalarValue) {
return self::getLiteralTypeFromScalarValue($c->value);
}
if ($c instanceof UnresolvedBinaryOp) {
$left = self::resolve(
$classlikes,
$c->left,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
$right = self::resolve(
$classlikes,
$c->right,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($left instanceof TMixed || $right instanceof TMixed) {
return new TMixed;
}
if ($c instanceof UnresolvedConcatOp) {
if (($left instanceof TLiteralString
|| $left instanceof TLiteralFloat
|| $left instanceof TLiteralInt)
&& ($right instanceof TLiteralString
|| $right instanceof TLiteralFloat
|| $right instanceof TLiteralInt)
) {
return Type::getAtomicStringFromLiteral($left->value . $right->value);
}
return new TString();
}
if ($c instanceof UnresolvedAdditionOp
|| $c instanceof UnresolvedSubtractionOp
|| $c instanceof UnresolvedDivisionOp
|| $c instanceof UnresolvedMultiplicationOp
|| $c instanceof UnresolvedBitwiseOr
|| $c instanceof UnresolvedBitwiseXor
|| $c instanceof UnresolvedBitwiseAnd
) {
if (($left instanceof TLiteralFloat || $left instanceof TLiteralInt)
&& ($right instanceof TLiteralFloat || $right instanceof TLiteralInt)
) {
if ($c instanceof UnresolvedAdditionOp) {
return self::getLiteralTypeFromScalarValue($left->value + $right->value);
}
if ($c instanceof UnresolvedSubtractionOp) {
return self::getLiteralTypeFromScalarValue($left->value - $right->value);
}
if ($c instanceof UnresolvedDivisionOp) {
return self::getLiteralTypeFromScalarValue($left->value / $right->value);
}
if ($c instanceof UnresolvedBitwiseOr) {
return self::getLiteralTypeFromScalarValue($left->value | $right->value);
}
if ($c instanceof UnresolvedBitwiseXor) {
return self::getLiteralTypeFromScalarValue($left->value ^ $right->value);
}
if ($c instanceof UnresolvedBitwiseAnd) {
return self::getLiteralTypeFromScalarValue($left->value & $right->value);
}
return self::getLiteralTypeFromScalarValue($left->value * $right->value);
}
if ($left instanceof TKeyedArray && $right instanceof TKeyedArray) {
$type = new TKeyedArray(
$left->properties + $right->properties,
null,
);
return $type;
}
return new TMixed;
}
return new TMixed;
}
if ($c instanceof UnresolvedTernary) {
$cond = self::resolve(
$classlikes,
$c->cond,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
$if = $c->if ? self::resolve(
$classlikes,
$c->if,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
) : null;
$else = self::resolve(
$classlikes,
$c->else,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($cond instanceof TLiteralFloat
|| $cond instanceof TLiteralInt
|| $cond instanceof TLiteralString
) {
if ($cond->value) {
return $if ?? $cond;
}
} elseif ($cond instanceof TFalse || $cond instanceof TNull) {
return $else;
} elseif ($cond instanceof TTrue) {
return $if ?? $cond;
}
}
if ($c instanceof ArrayValue) {
$properties = [];
$auto_key = 0;
if (!$c->entries) {
return new TArray([Type::getNever(), Type::getNever()]);
}
$is_list = true;
foreach ($c->entries as $entry) {
if ($entry instanceof ArraySpread) {
$spread_array = self::resolve(
$classlikes,
$entry->array,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($spread_array instanceof TArray && $spread_array->isEmptyArray()) {
continue;
}
if (!$spread_array instanceof TKeyedArray) {
return new TArray([Type::getArrayKey(), Type::getMixed()]);
}
foreach ($spread_array->properties as $k => $spread_array_type) {
$properties[is_string($k) ? $k : $auto_key++] = $spread_array_type;
}
continue;
}
if ($entry->key) {
$key_type = self::resolve(
$classlikes,
$entry->key,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if (!$key_type instanceof TLiteralInt
|| $key_type->value !== $auto_key
) {
$is_list = false;
}
} else {
$key_type = new TLiteralInt($auto_key);
}
if ($key_type instanceof TLiteralInt
|| $key_type instanceof TLiteralString
) {
$key_value = $key_type->value;
if ($key_type instanceof TLiteralInt) {
$auto_key = $key_type->value + 1;
} elseif (ctype_digit($key_type->value)) {
$auto_key = ((int) $key_type->value) + 1;
}
} else {
return new TArray([Type::getArrayKey(), Type::getMixed()]);
}
$value_type = new Union([self::resolve(
$classlikes,
$entry->value,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
)]);
$properties[$key_value] = $value_type;
}
if (empty($properties)) {
$resolved_type = new TArray([
new Union([new TNever()]),
new Union([new TNever()]),
]);
} else {
$resolved_type = new TKeyedArray($properties, null, null, $is_list);
}
return $resolved_type;
}
if ($c instanceof ClassConstant) {
if ($c->name === 'class') {
return new TLiteralClassString($c->fqcln);
}
$found_type = $classlikes->getClassConstantType(
$c->fqcln,
$c->name,
ReflectionProperty::IS_PRIVATE,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($found_type) {
return $found_type->getSingleAtomic();
}
}
if ($c instanceof ArrayOffsetFetch) {
$var_type = self::resolve(
$classlikes,
$c->array,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
$offset_type = self::resolve(
$classlikes,
$c->offset,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
if ($var_type instanceof TKeyedArray
&& ($offset_type instanceof TLiteralInt
|| $offset_type instanceof TLiteralString)
) {
$union = $var_type->properties[$offset_type->value] ?? null;
if ($union && $union->isSingle()) {
return $union->getSingleAtomic();
}
}
}
if ($c instanceof Constant) {
if ($statements_analyzer) {
$found_type = ConstFetchAnalyzer::getConstType(
$statements_analyzer,
$c->name,
$c->is_fully_qualified,
null,
);
if ($found_type) {
return $found_type->getSingleAtomic();
}
}
}
if ($c instanceof EnumPropertyFetch) {
if ($classlikes->enumExists($c->fqcln)) {
$enum_storage = $classlikes->getStorageFor($c->fqcln);
if (isset($enum_storage->enum_cases[$c->case])) {
if ($c instanceof EnumValueFetch) {
$value = $enum_storage->enum_cases[$c->case]->value;
if (is_string($value)) {
return Type::getString($value)->getSingleAtomic();
} elseif (is_int($value)) {
return Type::getInt(false, $value)->getSingleAtomic();
} elseif ($value instanceof UnresolvedConstantComponent) {
return self::resolve(
$classlikes,
$value,
$statements_analyzer,
$visited_constant_ids + [$c_id => true],
);
}
} elseif ($c instanceof EnumNameFetch) {
return Type::getString($c->case)->getSingleAtomic();
}
}
}
}
return new TMixed;
}
/**
* Note: This takes an array, but any array should only contain other arrays and scalars.
*
* @param array|string|int|float|bool|null $value
*/
public static function getLiteralTypeFromScalarValue($value): Atomic
{
if (is_array($value)) {
if (empty($value)) {
return Type::getEmptyArray()->getSingleAtomic();
}
$types = [];
/** @var array|scalar|null $val */
foreach ($value as $key => $val) {
$types[$key] = new Union([self::getLiteralTypeFromScalarValue($val)]);
}
return new TKeyedArray($types, null);
}
if (is_string($value)) {
return Type::getAtomicStringFromLiteral($value);
}
if (is_int($value)) {
return new TLiteralInt($value);
}
if (is_float($value)) {
return new TLiteralFloat($value);
}
if ($value === false) {
return new TFalse();
}
if ($value === true) {
return new TTrue();
}
if ($value === null) {
return new TNull();
}
throw new InvalidArgumentException('$value must be a scalar.');
}
}
Function Calls
None |
Stats
MD5 | 4e3f3cb0f37cc7db585ff48c63ce81f0 |
Eval Count | 0 |
Decode Time | 93 ms |