Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions TASKS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Follow-up tasks

## PHPStan maximum-level review (composer ci:test:php:phpstan)

### MagicSunday\\JsonMapper
- [x] Resolve PHPStan: `Property MagicSunday\\JsonMapper::$collectionFactory with generic interface MagicSunday\\JsonMapper\\Collection\\CollectionFactoryInterface does not specify its types: TKey, TValue` (`src/JsonMapper.php:101`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper::convertUnionValue() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\UnionType but does not specify its types: T` (`src/JsonMapper.php:497`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper::describeUnionType() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\UnionType but does not specify its types: T` (`src/JsonMapper.php:586`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper::unionAllowsNull() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\UnionType but does not specify its types: T` (`src/JsonMapper.php:597`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper::getReflectionClass() return type with generic class ReflectionClass does not specify its types: T` (`src/JsonMapper.php:735`).

### MagicSunday\\JsonMapper\\Collection\\CollectionDocBlockTypeResolver
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Collection\\CollectionDocBlockTypeResolver::resolve() return type with generic class Symfony\\Component\\TypeInfo\\Type\\CollectionType does not specify its types: T` (`src/JsonMapper/Collection/CollectionDocBlockTypeResolver.php:53`).

### MagicSunday\\JsonMapper\\Collection\\CollectionFactory
- [x] Resolve PHPStan: `Class MagicSunday\\JsonMapper\\Collection\\CollectionFactory implements generic interface MagicSunday\\JsonMapper\\Collection\\CollectionFactoryInterface but does not specify its types: TKey, TValue` (`src/JsonMapper/Collection/CollectionFactory.php:35`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Collection\\CollectionFactory::fromCollectionType() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\CollectionType but does not specify its types: T` (`src/JsonMapper/Collection/CollectionFactory.php:94`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Collection\\CollectionFactory::resolveWrappedClass() has parameter $objectType with generic class Symfony\\Component\\TypeInfo\\Type\\ObjectType but does not specify its types: T` (`src/JsonMapper/Collection/CollectionFactory.php:120`).

### MagicSunday\\JsonMapper\\Collection\\CollectionFactoryInterface
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Collection\\CollectionFactoryInterface::fromCollectionType() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\CollectionType but does not specify its types: T` (`src/JsonMapper/Collection/CollectionFactoryInterface.php:42`).

### MagicSunday\\JsonMapper\\Type\\TypeResolver
- [x] Resolve PHPStan: `Property MagicSunday\\JsonMapper\\Type\\TypeResolver::$defaultType with generic class Symfony\\Component\\TypeInfo\\Type\\BuiltinType does not specify its types: T` (`src/JsonMapper/Type/TypeResolver.php:33`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Type\\TypeResolver::normalizeUnionType() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\UnionType but does not specify its types: T` (`src/JsonMapper/Type/TypeResolver.php:224`).

### MagicSunday\\JsonMapper\\Value\\Strategy\\BuiltinValueConversionStrategy
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\BuiltinValueConversionStrategy::normalizeValue() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\BuiltinType but does not specify its types: T` (`src/JsonMapper/Value/Strategy/BuiltinValueConversionStrategy.php:66`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\BuiltinValueConversionStrategy::guardCompatibility() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\BuiltinType but does not specify its types: T` (`src/JsonMapper/Value/Strategy/BuiltinValueConversionStrategy.php:125`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\BuiltinValueConversionStrategy::allowsNull() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\BuiltinType but does not specify its types: T` (`src/JsonMapper/Value/Strategy/BuiltinValueConversionStrategy.php:156`).

### MagicSunday\\JsonMapper\\Value\\Strategy\\CollectionValueConversionStrategy
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\CollectionValueConversionStrategy::__construct() has parameter $collectionFactory with generic interface MagicSunday\\JsonMapper\\Collection\\CollectionFactoryInterface but does not specify its types: TKey, TValue` (`src/JsonMapper/Value/Strategy/CollectionValueConversionStrategy.php:26`).

### MagicSunday\\JsonMapper\\Value\\Strategy\\DateTimeValueConversionStrategy
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\DateTimeValueConversionStrategy::extractObjectType() return type with generic class Symfony\\Component\\TypeInfo\\Type\\ObjectType does not specify its types: T` (`src/JsonMapper/Value/Strategy/ObjectTypeConversionGuardTrait.php:27`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\DateTimeValueConversionStrategy::guardNullableValue() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\ObjectType but does not specify its types: T` (`src/JsonMapper/Value/Strategy/ObjectTypeConversionGuardTrait.php:43`).

### MagicSunday\\JsonMapper\\Value\\Strategy\\EnumValueConversionStrategy
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\EnumValueConversionStrategy::extractObjectType() return type with generic class Symfony\\Component\\TypeInfo\\Type\\ObjectType does not specify its types: T` (`src/JsonMapper/Value/Strategy/ObjectTypeConversionGuardTrait.php:27`).
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\EnumValueConversionStrategy::guardNullableValue() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\ObjectType but does not specify its types: T` (`src/JsonMapper/Value/Strategy/ObjectTypeConversionGuardTrait.php:43`).

### MagicSunday\\JsonMapper\\Value\\Strategy\\ObjectValueConversionStrategy
- [x] Resolve PHPStan: `Method MagicSunday\\JsonMapper\\Value\\Strategy\\ObjectValueConversionStrategy::resolveClassName() has parameter $type with generic class Symfony\\Component\\TypeInfo\\Type\\ObjectType but does not specify its types: T` (`src/JsonMapper/Value/Strategy/ObjectValueConversionStrategy.php:73`).

### MagicSunday\\Test\\Classes\\Base
- [x] Resolve PHPStan: `Property MagicSunday\\Test\\Classes\\Base::$simpleCollection with generic class MagicSunday\\Test\\Classes\\Collection does not specify its types: TKey, TValue` (`tests/Classes/Base.php:54`).

### MagicSunday\\Test\\Classes\\ClassMap\\CollectionSource
- [x] Resolve PHPStan: `Class MagicSunday\\Test\\Classes\\ClassMap\\CollectionSource extends generic class MagicSunday\\Test\\Classes\\Collection but does not specify its types: TKey, TValue` (`tests/Classes/ClassMap/CollectionSource.php:23`).

### MagicSunday\\Test\\Classes\\ClassMap\\CollectionTarget
- [x] Resolve PHPStan: `Class MagicSunday\\Test\\Classes\\ClassMap\\CollectionTarget extends generic class ArrayObject but does not specify its types: TKey, TValue` (`tests/Classes/ClassMap/CollectionTarget.php:23`).
12 changes: 12 additions & 0 deletions src/JsonMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ class JsonMapper

private ValueConverter $valueConverter;

/**
* @var CollectionFactoryInterface<array-key, mixed>
*/
private CollectionFactoryInterface $collectionFactory;

private CollectionDocBlockTypeResolver $collectionDocBlockTypeResolver;
Expand Down Expand Up @@ -493,6 +496,8 @@ private function convertValue(mixed $json, Type $type, MappingContext $context):

/**
* Converts the value according to the provided union type.
*
* @param UnionType<Type> $type
*/
private function convertUnionValue(mixed $json, UnionType $type, MappingContext $context): mixed
{
Expand Down Expand Up @@ -582,6 +587,8 @@ private function describeType(Type $type): string

/**
* Returns a textual representation of the union type.
*
* @param UnionType<Type> $type
*/
private function describeUnionType(UnionType $type): string
{
Expand All @@ -594,6 +601,9 @@ private function describeUnionType(UnionType $type): string
return implode('|', $parts);
}

/**
* @param UnionType<Type> $type
*/
private function unionAllowsNull(UnionType $type): bool
{
foreach ($type->getTypes() as $candidate) {
Expand Down Expand Up @@ -731,6 +741,8 @@ private function getReflectionProperty(string $className, string $propertyName):
* Returns the specified reflection class.
*
* @param class-string $className
*
* @return ReflectionClass<object>|null
*/
private function getReflectionClass(string $className): ?ReflectionClass
{
Expand Down
8 changes: 8 additions & 0 deletions src/JsonMapper/Collection/CollectionDocBlockTypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@
use phpDocumentor\Reflection\Types\ContextFactory;
use ReflectionClass;
use Symfony\Component\PropertyInfo\Util\PhpDocTypeHelper;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\Type\CollectionType;
use Symfony\Component\TypeInfo\Type\GenericType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\TypeIdentifier;

/**
* Resolves collection value types from PHPDoc annotations on collection classes.
*
* @phpstan-type CollectionWrappedType BuiltinType<TypeIdentifier::ARRAY>|BuiltinType<TypeIdentifier::ITERABLE>|ObjectType<class-string>
*/
final class CollectionDocBlockTypeResolver
{
Expand All @@ -49,6 +55,8 @@ public function __construct(
* Attempts to resolve a {@see CollectionType} from the collection class PHPDoc.
*
* @param class-string $collectionClassName
*
* @return CollectionType<CollectionWrappedType|GenericType<CollectionWrappedType>>|null
*/
public function resolve(string $collectionClassName): ?CollectionType
{
Expand Down
11 changes: 11 additions & 0 deletions src/JsonMapper/Collection/CollectionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
use MagicSunday\JsonMapper\Resolver\ClassResolver;
use MagicSunday\JsonMapper\Value\ValueConverter;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\Type\CollectionType;
use Symfony\Component\TypeInfo\Type\GenericType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\Type\WrappingTypeInterface;
use Symfony\Component\TypeInfo\TypeIdentifier;
use Traversable;

use function get_debug_type;
Expand All @@ -31,6 +34,10 @@

/**
* Creates collections and hydrates wrapping collection classes.
*
* @phpstan-type CollectionWrappedType BuiltinType<TypeIdentifier::ARRAY>|BuiltinType<TypeIdentifier::ITERABLE>|ObjectType<class-string>
*
* @implements CollectionFactoryInterface<array-key, mixed>
*/
final readonly class CollectionFactory implements CollectionFactoryInterface
{
Expand Down Expand Up @@ -89,6 +96,8 @@ public function mapIterable(mixed $json, Type $valueType, MappingContext $contex
/**
* Builds a collection based on the specified collection type description.
*
* @param CollectionType<CollectionWrappedType|GenericType<CollectionWrappedType>> $type
*
* @return array<array-key, mixed>|object|null
*/
public function fromCollectionType(CollectionType $type, mixed $json, MappingContext $context): mixed
Expand All @@ -113,6 +122,8 @@ public function fromCollectionType(CollectionType $type, mixed $json, MappingCon
/**
* Resolves the wrapped collection class name.
*
* @param ObjectType<class-string> $objectType
*
* @return class-string
*
* @throws DomainException
Expand Down
8 changes: 7 additions & 1 deletion src/JsonMapper/Collection/CollectionFactoryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,19 @@

use MagicSunday\JsonMapper\Context\MappingContext;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\Type\CollectionType;
use Symfony\Component\TypeInfo\Type\GenericType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\TypeIdentifier;

/**
* Describes the operations required to materialize collection values.
*
* @template TKey of array-key
* @template TValue
*
* @phpstan-type CollectionWrappedType BuiltinType<TypeIdentifier::ARRAY>|BuiltinType<TypeIdentifier::ITERABLE>|ObjectType<class-string>
*/
interface CollectionFactoryInterface
{
Expand All @@ -35,7 +41,7 @@ public function mapIterable(mixed $json, Type $valueType, MappingContext $contex
/**
* Builds a collection based on the specified collection type description.
*
* @param CollectionType $type The collection type metadata extracted from PHPStan/Psalm annotations.
* @param CollectionType<CollectionWrappedType|GenericType<CollectionWrappedType>> $type The collection type metadata extracted from PHPStan/Psalm annotations.
*
* @return array<TKey, TValue>|object|null
*/
Expand Down
6 changes: 6 additions & 0 deletions src/JsonMapper/Type/TypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ final class TypeResolver
{
private const string CACHE_KEY_PREFIX = 'jsonmapper.property_type.';

/**
* @var BuiltinType<TypeIdentifier::STRING>
*/
private BuiltinType $defaultType;

public function __construct(
Expand Down Expand Up @@ -221,6 +224,9 @@ private function createTypeFromNamedReflection(ReflectionNamedType $type, ?bool
return $resolved;
}

/**
* @param UnionType<Type> $type
*/
private function normalizeUnionType(UnionType $type): Type
{
$types = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ public function convert(mixed $value, Type $type, MappingContext $context): mixe
return $converted;
}

/**
* @param BuiltinType<TypeIdentifier> $type
*/
private function normalizeValue(mixed $value, BuiltinType $type): mixed
{
if ($value === null) {
Expand Down Expand Up @@ -122,6 +125,9 @@ private function normalizeValue(mixed $value, BuiltinType $type): mixed
return $value;
}

/**
* @param BuiltinType<TypeIdentifier> $type
*/
private function guardCompatibility(mixed $value, BuiltinType $type, MappingContext $context): void
{
$identifier = $type->getTypeIdentifier();
Expand Down Expand Up @@ -153,6 +159,9 @@ private function guardCompatibility(mixed $value, BuiltinType $type, MappingCont
}
}

/**
* @param BuiltinType<TypeIdentifier> $type
*/
private function allowsNull(BuiltinType $type): bool
{
return $type->isNullable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
*/
final readonly class CollectionValueConversionStrategy implements ValueConversionStrategyInterface
{
/**
* @param CollectionFactoryInterface<array-key, mixed> $collectionFactory
*/
public function __construct(
private CollectionFactoryInterface $collectionFactory,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ trait ObjectTypeConversionGuardTrait
{
/**
* Returns the provided type when it represents an object with a class name.
*
* @return ObjectType<class-string>|null
*/
private function extractObjectType(Type $type): ?ObjectType
{
Expand All @@ -39,6 +41,8 @@ private function extractObjectType(Type $type): ?ObjectType

/**
* Ensures null values comply with the target object's nullability.
*
* @param ObjectType<class-string> $type
*/
private function guardNullableValue(mixed $value, ObjectType $type, MappingContext $context): void
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ public function convert(mixed $value, Type $type, MappingContext $context): mixe
/**
* Resolves the class name from the provided object type.
*
* @param ObjectType<class-string> $type
*
* @return class-string
*/
private function resolveClassName(ObjectType $type): string
Expand Down
2 changes: 1 addition & 1 deletion tests/Classes/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Base
/**
* A collection of Simple instances.
*
* @var Collection<Simple>|Simple[]
* @var Collection<int, Simple>|array<int, Simple>
*/
public $simpleCollection;

Expand Down
3 changes: 3 additions & 0 deletions tests/Classes/ClassMap/CollectionSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
* @license https://opensource.org/licenses/MIT
* @link https://github.com/magicsunday/jsonmapper/
*/
/**
* @extends Collection<int, mixed>
*/
class CollectionSource extends Collection
{
}
3 changes: 3 additions & 0 deletions tests/Classes/ClassMap/CollectionTarget.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
* @license https://opensource.org/licenses/MIT
* @link https://github.com/magicsunday/jsonmapper/
*/
/**
* @extends ArrayObject<int, mixed>
*/
class CollectionTarget extends ArrayObject
{
}