From 4947919c93fdfd0eee9ec22becc6cc165d2eb527 Mon Sep 17 00:00:00 2001 From: Vasily Kartashov Date: Tue, 6 Feb 2024 11:48:00 +1100 Subject: [PATCH] Psalm fix --- src/Parser/Cache.php | 6 +- src/Parser/DocBlockAstTraverser.php | 3 + src/Resolvers/MappingUtils.php | 3 + src/Types/LiteralType.php | 4 +- src/Types/MixedType.php | 3 - src/Types/NonEmptyStringType.php | 17 ++-- src/Types/NumericStringType.php | 20 +++-- src/Types/ObjectLikeType.php | 116 ---------------------------- src/Types/StringType.php | 21 ++--- src/Types/TupleType.php | 23 +++++- src/types.php | 6 +- 11 files changed, 76 insertions(+), 146 deletions(-) delete mode 100644 src/Types/ObjectLikeType.php diff --git a/src/Parser/Cache.php b/src/Parser/Cache.php index c4d7ecf..09594ea 100644 --- a/src/Parser/Cache.php +++ b/src/Parser/Cache.php @@ -56,7 +56,11 @@ public static function remove(string $key): void public static function purge(): void { - foreach (glob(sys_get_temp_dir() . '/type-cache.*') as $fileName) { + $fileNames = glob(sys_get_temp_dir() . '/type-cache.*'); + if ($fileNames === false) { + return; + } + foreach ($fileNames as $fileName) { unlink($fileName); } } diff --git a/src/Parser/DocBlockAstTraverser.php b/src/Parser/DocBlockAstTraverser.php index 88dc00b..c147f81 100644 --- a/src/Parser/DocBlockAstTraverser.php +++ b/src/Parser/DocBlockAstTraverser.php @@ -79,6 +79,9 @@ private function traverseTupleNode(ArrayShapeNode $node, ?NameContext $nameConte } $fields[] = $this->traverse($item->valueType, $nameContext); } + /** + * @psalm-suppress MixedArgumentTypeCoercion + */ return new Types\TupleType($fields); } diff --git a/src/Resolvers/MappingUtils.php b/src/Resolvers/MappingUtils.php index 7cd562f..841895a 100644 --- a/src/Resolvers/MappingUtils.php +++ b/src/Resolvers/MappingUtils.php @@ -20,7 +20,10 @@ class MappingUtils private static array $properties = []; /** + * @template T + * @param mixed $value * @param array $mappedProperties + * @param Type $context */ public static function checkMapping(mixed $value, array $mappedProperties, Type $context): void { diff --git a/src/Types/LiteralType.php b/src/Types/LiteralType.php index 9552c63..402cd19 100644 --- a/src/Types/LiteralType.php +++ b/src/Types/LiteralType.php @@ -9,7 +9,7 @@ /** * @psalm-internal Hamlet\Type - * @template T of scalar + * @template T of scalar|null * @extends Type */ readonly class LiteralType extends Type @@ -56,7 +56,7 @@ public function __construct(mixed ...$values) #[Override] public function __toString(): string { $escape = - function ($a): string { + function (int|bool|float|string|null $a): string { if (is_string($a)) { return "'$a'"; } diff --git a/src/Types/MixedType.php b/src/Types/MixedType.php index ff0b742..bcda50a 100644 --- a/src/Types/MixedType.php +++ b/src/Types/MixedType.php @@ -12,9 +12,6 @@ */ readonly class MixedType extends Type { - /** - * @psalm-assert-if-true mixed $value - */ #[Override] public function matches(mixed $value): bool { return true; diff --git a/src/Types/NonEmptyStringType.php b/src/Types/NonEmptyStringType.php index d8b50f9..b14aa33 100644 --- a/src/Types/NonEmptyStringType.php +++ b/src/Types/NonEmptyStringType.php @@ -26,14 +26,21 @@ if ($this->matches($value)) { return $value; } - if (is_object($value) && !method_exists($value, '__toString')) { - throw new CastException($value, $this); - } - if (is_array($value)) { + + if (is_object($value)) { + if (method_exists($value, '__toString')) { + $stringValue = (string)$value; + } else { + throw new CastException($value, $this); + } + } elseif (is_array($value)) { $stringValue = 'Array'; - } else { + } elseif (is_scalar($value) || is_resource($value) || is_null($value)) { $stringValue = (string)$value; + } else { + throw new CastException($value, $this); } + if ($stringValue === '') { throw new CastException($value, $this); } diff --git a/src/Types/NumericStringType.php b/src/Types/NumericStringType.php index b2e108b..9b680b6 100644 --- a/src/Types/NumericStringType.php +++ b/src/Types/NumericStringType.php @@ -25,18 +25,28 @@ { if ($this->matches($value)) { return $value; + } + + if (is_string($value)) { + $stringValue = $value; } elseif (is_array($value) || is_null($value)) { throw new CastException($value, $this); } elseif (is_scalar($value) || is_resource($value)) { - $value = (string)$value; - } elseif (is_object($value) && method_exists($value, '__toString')) { - $value = (string)$value; + $stringValue = (string)$value; + } elseif (is_object($value)) { + if (method_exists($value, '__toString')) { + $stringValue = (string)$value; + } else { + throw new CastException($value, $this); + } + } else { + throw new CastException($value, $this); } - if (!is_string($value) || !is_numeric($value)) { + if (!is_numeric($stringValue)) { throw new CastException($value, $this); } - return $value; + return $stringValue; } #[Override] public function __toString(): string diff --git a/src/Types/ObjectLikeType.php b/src/Types/ObjectLikeType.php deleted file mode 100644 index 94bbf03..0000000 --- a/src/Types/ObjectLikeType.php +++ /dev/null @@ -1,116 +0,0 @@ -> - */ -readonly class ObjectLikeType extends Type -{ - /** - * @var array> - */ - private array $fields; - - /** - * @param array> $fields - */ - public function __construct(array $fields) - { - $this->fields = $fields; - } - - #[Override] public function matches(mixed $value): bool - { - if (!is_array($value)) { - return false; - } - foreach ($this->fields as $field => $type) { - $tokens = explode('?', $field, 2); - if (!array_key_exists($tokens[0], $value)) { - if (count($tokens) == 1) { - return false; - } else { - continue; - } - } - if (!$type->matches($value[$tokens[0]])) { - return false; - } - } - return true; - } - - /** - * @return array - */ - #[Override] public function resolveAndCast(mixed $value, Resolver $resolver): array - { - if ($this->matches($value)) { - return $value; - } - if (!(is_array($value) || is_object($value) && is_a($value, stdClass::class))) { - throw new CastException($value, $this); - } - $validateUnmappedProperties = !$resolver->ignoreUnmappedProperties(); - $mappedProperties = []; - - foreach ($this->fields as $field => $fieldType) { - $tokens = explode('?', $field, 2); - $fieldName = $tokens[0]; - $required = count($tokens) == 1; - - /** - * @psalm-suppress ArgumentTypeCoercion - */ - $resolution = $resolver->getValue(null, $fieldName, $value); - if ($resolution->successful()) { - if ($validateUnmappedProperties) { - $sourceFieldName = $resolution->sourceFieldName(); - if ($sourceFieldName !== null) { - $mappedProperties[$sourceFieldName] = 1; - } - } - } elseif (!$required) { - $mappedProperties[$fieldName] = 1; - continue; - } else { - throw new CastException($value, $this); - } - - $fieldValue = $fieldType->resolveAndCast($resolution->value(), $resolver); - $value = $resolver->setValue($value, $fieldName, $fieldValue); - } - if ($validateUnmappedProperties) { - MappingUtils::checkMapping($value, $mappedProperties, $this); - } - return (array)$value; - } - - #[Override] public function __toString(): string - { - $keys = []; - foreach ($this->fields as $name => $type) { - $keys[] = $name . ':' . $type; - } - return 'array{' . join(',', $keys) . '}'; - } - - #[Override] public function serialize(): string - { - $arguments = []; - foreach ($this->fields as $name => $type) { - $arguments[] = var_export($name, true) . ' => ' . $type->serialize(); - } - return 'new ' . static::class . '([' . join(', ', $arguments) . '])'; - } -} diff --git a/src/Types/StringType.php b/src/Types/StringType.php index 4f35c35..561466b 100644 --- a/src/Types/StringType.php +++ b/src/Types/StringType.php @@ -26,19 +26,22 @@ if ($this->matches($value)) { return $value; } - if (is_scalar($value) || is_resource($value)) { - return (string)$value; - } - if (is_object($value) && method_exists($value, '__toString')) { + + if (is_object($value)) { + if (method_exists($value, '__toString')) { + return (string)$value; + } else { + throw new CastException($value, $this); + } + } elseif (is_scalar($value) || is_resource($value)) { return (string)$value; - } - if (is_array($value)) { + } elseif (is_array($value)) { return 'Array'; - } - if ($value === null) { + } elseif ($value === null) { return ''; + } else { + throw new CastException($value, $this); } - throw new CastException($value, $this); } #[Override] public function __toString(): string diff --git a/src/Types/TupleType.php b/src/Types/TupleType.php index cf4680e..abfc508 100644 --- a/src/Types/TupleType.php +++ b/src/Types/TupleType.php @@ -9,18 +9,18 @@ /** * @psalm-internal Hamlet\Type - * @template A + * @template A of list * @extends Type */ readonly class TupleType extends Type { /** - * @var list> + * @var list */ private array $fields; /** - * @param list> $fields + * @param list $fields */ public function __construct(array $fields) { @@ -28,7 +28,7 @@ public function __construct(array $fields) } /** - * @psalm-assert-if-true list $value + * @psalm-assert-if-true A $value */ #[Override] public function matches(mixed $value): bool { @@ -36,7 +36,13 @@ public function __construct(array $fields) return false; } for ($i = 0; $i < count($this->fields); $i++) { + /** + * @psalm-suppress MixedAssignment + */ $v = $value[$i]; + /** + * @psalm-suppress TypeDoesNotContainType + */ if (!$this->fields[$i]->matches($v)) { return false; } @@ -44,6 +50,9 @@ public function __construct(array $fields) return true; } + /** + * @psalm-suppress InvalidReturnType + */ #[Override] public function resolveAndCast(mixed $value, Resolver $resolver): array { if ($this->matches($value)) { @@ -64,8 +73,14 @@ public function __construct(array $fields) $result = []; $value = array_values($value); foreach ($this->fields as $i => $field) { + /** + * @psalm-suppress MixedAssignment + */ $result[] = $field->resolveAndCast($value[$i], $resolver); } + /** + * @psalm-suppress InvalidReturnStatement + */ return $result; } diff --git a/src/types.php b/src/types.php index 6a231d1..c1e23ec 100644 --- a/src/types.php +++ b/src/types.php @@ -110,7 +110,7 @@ function _null(): Type /** * // @todo literal types should be checked, cannot be just anything. - * @template A of scalar + * @template A of scalar|null * @param array $as * @return Type */ @@ -205,6 +205,7 @@ function _union(Type $a, Type $b, Type $c = null, Type $d = null, Type $e = null * @param Type|null $h * @return Type * @psalm-return (func_num_args() is 2 ? Type : (func_num_args() is 3 ? Type : (func_num_args() is 4 ? Type : (func_num_args() is 5 ? Type : (func_num_args() is 6 ? Type : (func_num_args() is 7 ? Type : Type)))))) + * @psalm-suppress InvalidReturnType * @psalm-suppress MixedReturnTypeCoercion */ function _tuple(Type $a, Type $b, Type $c = null, Type $d = null, Type $e = null, Type $f = null, Type $g = null, Type $h = null): Type @@ -224,6 +225,9 @@ function _tuple(Type $a, Type $b, Type $c = null, Type $d = null, Type $e = null $fields[] = $arg; } } + /** + * @psalm-suppress InvalidReturnStatement + */ return new Types\TupleType($fields); }