From be03a29ee6813646f18c19a236c740728aa3bb73 Mon Sep 17 00:00:00 2001 From: ignace nyamagana butera Date: Sat, 11 Nov 2023 07:03:09 +0100 Subject: [PATCH] Improve Serializer performance with collection --- CHANGELOG.md | 1 + docs/9.0/reader/record-mapping.md | 35 ++++---- src/MapIterator.php | 9 ++ src/Reader.php | 11 +-- src/ResultSet.php | 31 ++++--- src/Serializer.php | 95 +++++++++++----------- src/Serializer/CastToArray.php | 18 ++-- src/Serializer/CastToBool.php | 6 +- src/Serializer/CastToDate.php | 6 +- src/Serializer/CastToEnum.php | 6 +- src/Serializer/CastToFloat.php | 6 +- src/Serializer/CastToInt.php | 6 +- src/Serializer/CastToString.php | 4 +- src/Serializer/PropertySetter.php | 2 +- src/Serializer/{BasicType.php => Type.php} | 4 +- src/SerializerTest.php | 8 +- 16 files changed, 134 insertions(+), 114 deletions(-) rename src/Serializer/{BasicType.php => Type.php} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0e918b0..8a447599 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ It's usage will trigger a `E_USER_DEPRECATED` call. - The internal `Stream` object it will throw a `RuntimeException` if the rewind action fails - if calls to `fseek` fails (returns `-1` ) a new `RuntimeException` will be thrown too. - `Stream` can iterate and return the full line respecting `SplFielObject` flags. Previously it only returned the CSV records. +- `MapIterator::fromIterable` to instantiate a `MapIterator` object from any iterable structure. ### Removed diff --git a/docs/9.0/reader/record-mapping.md b/docs/9.0/reader/record-mapping.md index e47c8180..5939d2aa 100644 --- a/docs/9.0/reader/record-mapping.md +++ b/docs/9.0/reader/record-mapping.md @@ -38,7 +38,7 @@ foreach ($serializer->deserializeAll($collection) as $weather) { If you are working with a class which implements the `TabularDataReader` interface you can use this functionality directly by calling the `TabularDataReader::getObjects` method. -Here's an example using the `Reader` class: +Here's an example using the `Reader` class which implements the `TabularDataReader` interface: ```php use League\Csv\Reader; @@ -65,7 +65,7 @@ the mechanism may either fail or produced unexpected results.

To work as intended the mechanism expects the following: - A target class where the array will be deserialized in; -- information on how to convert cell value into object properties using dedicated attributes; +- information on how to convert cell values into object properties; As an example if we assume we have the following CSV document: @@ -79,7 +79,7 @@ date,temperature,place 2011-01-03,5,Berkeley ``` -We can define a PHP DTO using the following class and the attributes. +We can define a PHP DTO using the following properties. ```php deserializeAll($csv) as $weather) { } ``` +

The code above is similar to using TabularDataReader::getObject method.

+ ## Defining the mapping rules By default, the deserialization engine will convert public properties using their name. In other words, -if there is a class property, which name is the same as a column name, the column value will be assigned -to this property. The appropriate type used for the record cell value is a `string` or `null` and -the object public properties ares typed with +if there is a public class property, which name is the same as a column name, the column +value will be assigned to that property. The appropriate type used for the record +cell value is a `string` or `null` and the object public properties must be typed with - a scalar type (`string`, `int`, `float`, `bool`) - any `Enum` object (backed or not) - `DateTime`, `DateTimeImmuntable` or any class that extends those two classes. - an `array` -the `nullable` aspect of the property is also handled. +the `nullable` aspect of the property is also automatically handled. -To fine tune the conversion you are require to use the `Cell` attribute. This attribute will +It is possible to improve the conversion using the `Cell` attribute. This attribute will override the automatic resolution and enable fine-tuning type casting on the property level. The `Cell` attribute can be used on class properties and methods regardless of their visibility. The attribute can take up to three (3) arguments which are all optional: -- The `offset` argument tells the engine which cell to use via its numeric or name offset. If not present -the property name or the name of the first argument of the `setter` method will be used. In such case, -you are required to specify the property names information. -- The `cast` argument which accept the name of a class implementing the `TypeCasting` interface and responsible -for type casting the cell value. -- The `castArguments` which enable controlling typecasting by providing extra arguments to the `TypeCasting` class constructor +- The `offset` argument tells the engine which cell to use via its numeric or name offset. If not present the property name or the name of the first argument of the `setter` method will be used. In such case, you are required to specify the property names information. +- The `cast` argument which accept the name of a class implementing the `TypeCasting` interface and responsible for type casting the cell value. +- The `castArguments` argument enables controlling typecasting by providing extra arguments to the `TypeCasting` class constructor -In any cases, if type casting fails, an exception will be thrown. +In any case, if type casting fails, an exception will be thrown. Here's an example of how the attribute could be used: @@ -169,9 +168,9 @@ The above rule can be translated in plain english like this: > using the date format `!Y-m-d` and the `Africa/Nairobi` timezone. Once created, > inject the date instance into the `observedOn` property of the class. -## Type casting the record value +## Type casting -The library comes bundles with seven (7) type casting classes which relies on the property type information. All the +The library comes bundled with seven (7) type casting classes which relies on the property type information. All the built-in methods support the `nullable` and the `mixed` types. - They will return `null` or a specified default value, if the cell value is `null` and the type is `nullable` @@ -198,7 +197,7 @@ optional argument `default` which is the default `int` or `float` value to retur ### CastToEnum -Convert the array value to a PHP `Enum` it supports both "real" and backed enumeration. The class takes on +Convert the array value to a PHP `Enum`, it supports both "real" and backed enumeration. The class takes on optional argument `default` which is the default Enum value to return if the value is `null`. If the `Enum` is backed the cell value will be considered as one of the Enum value; otherwise it will be used as one the `Enum` name. Likewise, the `default` value will also be considered the same way. If the default value diff --git a/src/MapIterator.php b/src/MapIterator.php index 05347c09..058dfdd2 100644 --- a/src/MapIterator.php +++ b/src/MapIterator.php @@ -13,6 +13,7 @@ namespace League\Csv; +use ArrayIterator; use IteratorIterator; use Traversable; @@ -32,6 +33,14 @@ public function __construct(Traversable $iterator, callable $callable) $this->callable = $callable; } + public static function fromIterable(iterable $iterator, callable $callable): self + { + return match (true) { + is_array($iterator) => new self(new ArrayIterator($iterator), $callable), + default => new self($iterator, $callable), + }; + } + public function current(): mixed { return ($this->callable)(parent::current(), parent::key()); diff --git a/src/Reader.php b/src/Reader.php index edfd18a2..f68f1a0a 100644 --- a/src/Reader.php +++ b/src/Reader.php @@ -80,7 +80,7 @@ public function getHeaderOffset(): ?int } /** - * @throws Exception + * @throws SyntaxError * * Returns the header record. */ @@ -96,7 +96,7 @@ public function getHeader(): array /** * Determines the CSV record header. * - * @throws Exception If the header offset is set and no record is found or is the empty array + * @throws SyntaxError If the header offset is set and no record is found or is the empty array * * @return array */ @@ -424,11 +424,12 @@ public function getRecords(array $header = []): Iterator /** * @param class-string $className + * @param array $header * - * @throws TypeCastingFailed - * @throws MappingFailed * @throws Exception + * @throws MappingFailed * @throws ReflectionException + * @throws TypeCastingFailed */ public function getObjects(string $className, array $header = []): Iterator { @@ -448,7 +449,7 @@ public function getObjects(string $className, array $header = []): Iterator * * @param array $header * - * @throws Exception If the header contains non unique column name + * @throws SyntaxError If the header contains non unique column name * * @return array */ diff --git a/src/ResultSet.php b/src/ResultSet.php index e42f76f3..d0599a46 100644 --- a/src/ResultSet.php +++ b/src/ResultSet.php @@ -22,6 +22,7 @@ use League\Csv\Serializer\MappingFailed; use League\Csv\Serializer\TypeCastingFailed; use LimitIterator; +use ReflectionException; use function array_filter; use function array_flip; @@ -235,25 +236,34 @@ public function matchingFirstOrFail(string $expression): TabularDataReader */ public function getRecords(array $header = []): Iterator { - if ($header !== array_filter($header, is_string(...))) { - throw SyntaxError::dueToInvalidHeaderColumnNames(); - } - - $header = $this->validateHeader($header); - if ([] === $header) { - $header = $this->header; - } + $header = $this->prepareHeader($header); return $this->combineHeader($header); } /** * @param class-string $className + * @param array $header * - * @throws TypeCastingFailed + * @throws Exception * @throws MappingFailed + * @throws ReflectionException + * @throws TypeCastingFailed */ public function getObjects(string $className, array $header = []): Iterator + { + $header = $this->prepareHeader($header); + + return (new Serializer($className, $header))->deserializeAll($this->combineHeader($header)); + } + + /** + * @param array $header + * + * @throws SyntaxError + * @return array + */ + protected function prepareHeader(array $header): array { if ($header !== array_filter($header, is_string(...))) { throw SyntaxError::dueToInvalidHeaderColumnNames(); @@ -263,8 +273,7 @@ public function getObjects(string $className, array $header = []): Iterator if ([] === $header) { $header = $this->header; } - - return (new Serializer($className, $header))->deserializeAll($this->combineHeader($header)); + return $header; } /** diff --git a/src/Serializer.php b/src/Serializer.php index 791402d0..ba30ec0e 100644 --- a/src/Serializer.php +++ b/src/Serializer.php @@ -13,9 +13,7 @@ namespace League\Csv; -use ArrayIterator; use Iterator; -use League\Csv\Serializer\BasicType; use League\Csv\Serializer\CastToArray; use League\Csv\Serializer\CastToBool; use League\Csv\Serializer\CastToDate; @@ -26,22 +24,21 @@ use League\Csv\Serializer\Cell; use League\Csv\Serializer\MappingFailed; use League\Csv\Serializer\PropertySetter; +use League\Csv\Serializer\Type; use League\Csv\Serializer\TypeCasting; use League\Csv\Serializer\TypeCastingFailed; use ReflectionAttribute; use ReflectionClass; +use ReflectionException; use ReflectionMethod; use ReflectionNamedType; use ReflectionProperty; use ReflectionType; -use RuntimeException; use Throwable; -use TypeError; use function array_reduce; use function array_search; use function array_values; -use function is_array; use function is_int; final class Serializer @@ -57,8 +54,7 @@ final class Serializer * @param array $propertyNames * * @throws MappingFailed - * @throws RuntimeException - * @throws TypeError + * @throws ReflectionException */ public function __construct(string $className, array $propertyNames = []) { @@ -67,56 +63,68 @@ public function __construct(string $className, array $propertyNames = []) $this->propertySetters = $this->findPropertySetters($propertyNames); //if converters is empty it means the Serializer - //was unable to detect properties to map + //was unable to detect properties to assign if ([] === $this->propertySetters) { throw new MappingFailed('No properties or method setters were found eligible on the class `'.$className.'` to be used for type casting.'); } } /** + * @param class-string $className + * @param array $record + * * @throws MappingFailed + * @throws ReflectionException * @throws TypeCastingFailed */ + public static function assign(string $className, array $record): object + { + return (new self($className, array_keys($record)))->deserialize($record); + } + public function deserializeAll(iterable $records): Iterator { - $threshold = 50; - $deserialize = fn (array $record, int $offset): object => match (0) { - $offset % $threshold => $this->deserialize($record), - default => $this->createInstance($record), + $check = true; + $assign = function (array $record) use (&$check) { + $object = $this->class->newInstanceWithoutConstructor(); + $this->hydrate($object, $record); + + if ($check) { + $check = false; + $this->assertObjectIsInValidState($object); + } + + return $object; }; - return new MapIterator( - is_array($records) ? new ArrayIterator($records) : $records, - $deserialize - ); + return MapIterator::fromIterable($records, $assign); } /** - * @throws MappingFailed + * @throws ReflectionException * @throws TypeCastingFailed */ public function deserialize(array $record): object { - $object = $this->createInstance($record); + $object = $this->class->newInstanceWithoutConstructor(); + $this->hydrate($object, $record); $this->assertObjectIsInValidState($object); return $object; } - private function createInstance(array $record): object + /** + * @param array $record + */ + private function hydrate(object $object, array $record): void { - $object = $this->class->newInstanceWithoutConstructor(); - $record = array_values($record); foreach ($this->propertySetters as $propertySetter) { - $propertySetter->setValue($object, $record[$propertySetter->offset]); + $propertySetter($object, $record[$propertySetter->offset]); } - - return $object; } - /** * @throws TypeCastingFailed */ @@ -129,17 +137,6 @@ private function assertObjectIsInValidState(object $object): void } } - /** - * @param class-string $className - * - * @throws MappingFailed - * @throws TypeCastingFailed - */ - public static function map(string $className, array $record): object - { - return (new self($className, array_keys($record)))->deserialize($record); - } - /** * @param array $propertyNames * @@ -152,6 +149,10 @@ private function findPropertySetters(array $propertyNames): array $check = []; $propertySetters = []; foreach ($this->class->getProperties(ReflectionProperty::IS_PUBLIC) as $property) { + if ($property->isStatic()) { + continue; + } + $propertyName = $property->getName(); /** @var int|false $offset */ @@ -279,17 +280,17 @@ private function resolveTypeCasting(ReflectionType $reflectionType, array $argum } try { - return match (BasicType::tryFromPropertyType($type)) { - BasicType::Mixed, - BasicType::String => new CastToString($type, ...$arguments), /* @phpstan-ignore-line */ - BasicType::Iterable, - BasicType::Array => new CastToArray($type, ...$arguments), /* @phpstan-ignore-line */ - BasicType::Float => new CastToFloat($type, ...$arguments), /* @phpstan-ignore-line */ - BasicType::Int => new CastToInt($type, ...$arguments), /* @phpstan-ignore-line */ - BasicType::Bool => new CastToBool($type, ...$arguments), /* @phpstan-ignore-line */ - BasicType::Date => new CastToDate($type, ...$arguments), /* @phpstan-ignore-line */ - BasicType::Enum => new CastToEnum($type, ...$arguments), /* @phpstan-ignore-line */ - default => null, + return match (Type::tryFromPropertyType($type)) { + Type::Mixed, + Type::String => new CastToString($type, ...$arguments), /* @phpstan-ignore-line */ + Type::Iterable, + Type::Array => new CastToArray($type, ...$arguments), /* @phpstan-ignore-line */ + Type::Float => new CastToFloat($type, ...$arguments), /* @phpstan-ignore-line */ + Type::Int => new CastToInt($type, ...$arguments), /* @phpstan-ignore-line */ + Type::Bool => new CastToBool($type, ...$arguments), /* @phpstan-ignore-line */ + Type::Date => new CastToDate($type, ...$arguments), /* @phpstan-ignore-line */ + Type::Enum => new CastToEnum($type, ...$arguments), /* @phpstan-ignore-line */ + null => null, }; } catch (Throwable $exception) { if ($exception instanceof MappingFailed) { diff --git a/src/Serializer/CastToArray.php b/src/Serializer/CastToArray.php index e0c9f2ac..3e5e23c6 100644 --- a/src/Serializer/CastToArray.php +++ b/src/Serializer/CastToArray.php @@ -50,10 +50,10 @@ public function __construct( private readonly string $enclosure = '"', private readonly int $jsonDepth = 512, private readonly int $jsonFlags = 0, - BasicType|string $type = BasicType::String, + Type|string $type = Type::String, ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::Array, BasicType::Iterable)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::Array, Type::Iterable)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; an `array` or an `iterable` structure is required.'); } @@ -79,7 +79,7 @@ public function toVariable(?string $value): ?array if (null === $value) { return match (true) { $this->isNullable, - BasicType::tryFrom($this->class)?->equals(BasicType::Mixed) => $this->default, + Type::tryFrom($this->class)?->equals(Type::Mixed) => $this->default, default => throw new TypeCastingFailed('The `null` value can not be cast to an `array`; the property type is not nullable.'), }; } @@ -109,18 +109,18 @@ public function toVariable(?string $value): ?array /** * @throws MappingFailed if the type is not supported */ - private function resolveFilterFlag(BasicType|string $type): int + private function resolveFilterFlag(Type|string $type): int { if ($this->shape->equals(ArrayShape::Json)) { - return BasicType::String->filterFlag(); + return Type::String->filterFlag(); } - if (!$type instanceof BasicType) { - $type = BasicType::tryFrom($type); + if (!$type instanceof Type) { + $type = Type::tryFrom($type); } return match (true) { - !$type instanceof BasicType, + !$type instanceof Type, !$type->isScalar() => throw new MappingFailed('Only scalar type are supported for `array` value casting.'), default => $type->filterFlag(), }; diff --git a/src/Serializer/CastToBool.php b/src/Serializer/CastToBool.php index d7d3cf76..a9e85c1f 100644 --- a/src/Serializer/CastToBool.php +++ b/src/Serializer/CastToBool.php @@ -24,8 +24,8 @@ public function __construct( string $propertyType, private readonly ?bool $default = null ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::Bool)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::Bool)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; a `bool` type is required.'); } @@ -38,7 +38,7 @@ public function __construct( public function toVariable(?string $value): ?bool { return match(true) { - null !== $value => filter_var($value, BasicType::Bool->filterFlag()), + null !== $value => filter_var($value, Type::Bool->filterFlag()), $this->isNullable => $this->default, default => throw new TypeCastingFailed('The `null` value can not be cast to a boolean value.'), }; diff --git a/src/Serializer/CastToDate.php b/src/Serializer/CastToDate.php index 4c61eb9e..5dea6235 100644 --- a/src/Serializer/CastToDate.php +++ b/src/Serializer/CastToDate.php @@ -42,13 +42,13 @@ public function __construct( private readonly ?string $format = null, DateTimeZone|string|null $timezone = null, ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::Date)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::Date)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; an class implementing the `'.DateTimeInterface::class.'` interface is required.'); } $class = ltrim($propertyType, '?'); - if (BasicType::Mixed->equals($baseType) || DateTimeInterface::class === $class) { + if (Type::Mixed->equals($baseType) || DateTimeInterface::class === $class) { $class = DateTimeImmutable::class; } diff --git a/src/Serializer/CastToEnum.php b/src/Serializer/CastToEnum.php index 9f85c2fe..ccd9059f 100644 --- a/src/Serializer/CastToEnum.php +++ b/src/Serializer/CastToEnum.php @@ -37,8 +37,8 @@ public function __construct( string $propertyType, ?string $default = null, ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::Enum)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::Enum)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; an `Enum` is required.'); } @@ -74,7 +74,7 @@ private function cast(string $value): BackedEnum|UnitEnum return $enum->getCase($value)->getValue(); } - $backedValue = 'int' === $enum->getBackingType()?->getName() ? filter_var($value, BasicType::Int->filterFlag()) : $value; + $backedValue = 'int' === $enum->getBackingType()?->getName() ? filter_var($value, Type::Int->filterFlag()) : $value; return $this->class::from($backedValue); } catch (Throwable $exception) { diff --git a/src/Serializer/CastToFloat.php b/src/Serializer/CastToFloat.php index b22ffc1c..12cb5129 100644 --- a/src/Serializer/CastToFloat.php +++ b/src/Serializer/CastToFloat.php @@ -27,8 +27,8 @@ public function __construct( string $propertyType, private readonly ?float $default = null, ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::Float)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::Float)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; a `float` type is required.'); } @@ -47,7 +47,7 @@ public function toVariable(?string $value): ?float }; } - $float = filter_var($value, BasicType::Float->filterFlag()); + $float = filter_var($value, Type::Float->filterFlag()); return match ($float) { false => throw new TypeCastingFailed('The `'.$value.'` value can not be cast to a float.'), diff --git a/src/Serializer/CastToInt.php b/src/Serializer/CastToInt.php index 4892b21c..f6b49879 100644 --- a/src/Serializer/CastToInt.php +++ b/src/Serializer/CastToInt.php @@ -27,8 +27,8 @@ public function __construct( string $propertyType, private readonly ?int $default = null, ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::Int, BasicType::Float)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::Int, Type::Float)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; a `int` type is required.'); } @@ -47,7 +47,7 @@ public function toVariable(?string $value): ?int }; } - $int = filter_var($value, BasicType::Int->filterFlag()); + $int = filter_var($value, Type::Int->filterFlag()); return match ($int) { false => throw new TypeCastingFailed('The `'.$value.'` value can not be cast to an integer.'), diff --git a/src/Serializer/CastToString.php b/src/Serializer/CastToString.php index ea4f95cb..221984da 100644 --- a/src/Serializer/CastToString.php +++ b/src/Serializer/CastToString.php @@ -26,8 +26,8 @@ public function __construct( string $propertyType, private readonly ?string $default = null ) { - $baseType = BasicType::tryFromPropertyType($propertyType); - if (null === $baseType || !$baseType->isOneOf(BasicType::Mixed, BasicType::String)) { + $baseType = Type::tryFromPropertyType($propertyType); + if (null === $baseType || !$baseType->isOneOf(Type::Mixed, Type::String)) { throw new MappingFailed('The property type `'.$propertyType.'` is not supported; a `string` type is required.'); } diff --git a/src/Serializer/PropertySetter.php b/src/Serializer/PropertySetter.php index 8e6ab29c..b67dc1dc 100644 --- a/src/Serializer/PropertySetter.php +++ b/src/Serializer/PropertySetter.php @@ -28,7 +28,7 @@ public function __construct( ) { } - public function setValue(object $object, ?string $value): void + public function __invoke(object $object, ?string $value): void { $value = $this->cast->toVariable($value); diff --git a/src/Serializer/BasicType.php b/src/Serializer/Type.php similarity index 97% rename from src/Serializer/BasicType.php rename to src/Serializer/Type.php index 82c16451..d9446b69 100644 --- a/src/Serializer/BasicType.php +++ b/src/Serializer/Type.php @@ -25,10 +25,10 @@ use const FILTER_VALIDATE_FLOAT; use const FILTER_VALIDATE_INT; -enum BasicType: string +enum Type: string { case Bool = 'bool'; - case Int = 'int'; + case Int = 'int'; case Float = 'float'; case String = 'string'; case Mixed = 'mixed'; diff --git a/src/SerializerTest.php b/src/SerializerTest.php index 58e3121c..596bd3a9 100644 --- a/src/SerializerTest.php +++ b/src/SerializerTest.php @@ -57,7 +57,7 @@ public function testItConvertsARecordsToAnObjectUsingRecordAttribute(): void 'place' => 'Berkeley', ]; - $weather = Serializer::map(WeatherWithRecordAttribute::class, $record); + $weather = Serializer::assign(WeatherWithRecordAttribute::class, $record); self::assertInstanceOf(WeatherWithRecordAttribute::class, $weather); self::assertInstanceOf(DateTimeImmutable::class, $weather->observedOn); @@ -73,7 +73,7 @@ public function testItConvertsARecordsToAnObjectUsingProperties(): void 'place' => 'Berkeley', ]; - $weather = Serializer::map(WeatherProperty::class, $record); + $weather = Serializer::assign(WeatherProperty::class, $record); self::assertInstanceOf(WeatherProperty::class, $weather); self::assertInstanceOf(DateTimeImmutable::class, $weather->observedOn); @@ -89,7 +89,7 @@ public function testItConvertsARecordsToAnObjectUsingMethods(): void 'place' => 'Berkeley', ]; - $weather = Serializer::map(WeatherSetterGetter::class, $record); + $weather = Serializer::assign(WeatherSetterGetter::class, $record); self::assertInstanceOf(WeatherSetterGetter::class, $weather); self::assertSame('2023-10-30', $weather->getObservedOn()->format('Y-m-d')); @@ -102,7 +102,7 @@ public function testMapingFailBecauseTheRecordAttributeIsMissing(): void $this->expectException(MappingFailed::class); $this->expectExceptionMessage('No properties or method setters were found eligible on the class `stdClass` to be used for type casting.'); - Serializer::map(stdClass::class, ['foo' => 'bar']); + Serializer::assign(stdClass::class, ['foo' => 'bar']); } public function testItWillThrowIfTheHeaderIsMissingAndTheColumnOffsetIsAString(): void