From d92c841a7ba566b4fc3bd2c660b47419776d8fb0 Mon Sep 17 00:00:00 2001 From: Fa-BRAIK Date: Fri, 27 Dec 2024 19:52:13 -0500 Subject: [PATCH 1/4] Added cloneable data contract and trait --- src/Concerns/CloneableData.php | 79 +++++++++++ src/Contracts/CloneableData.php | 13 ++ src/Data.php | 6 +- src/Exceptions/DataCreationException.php | 12 ++ tests/Unit/Concerns/CloneableDataTest.php | 158 ++++++++++++++++++++++ 5 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 src/Concerns/CloneableData.php create mode 100644 src/Contracts/CloneableData.php create mode 100644 tests/Unit/Concerns/CloneableDataTest.php diff --git a/src/Concerns/CloneableData.php b/src/Concerns/CloneableData.php new file mode 100644 index 0000000..f9ba8ec --- /dev/null +++ b/src/Concerns/CloneableData.php @@ -0,0 +1,79 @@ + $context */ + $context = ClassContext::getInstance(new ReflectionClass(static::class)); + + return $context->hasComputedProperties + ? $this->cloneInstanceWithConstructorCall($context, $value) + : $this->cloneInstanceWithoutConstructorCall($context, $value); + } catch (Throwable $t) { + throw DataCreationException::unableToCloneInstanceWithNewData($t); + } + } + + /** + * @param ClassContext $context + * @param array $value + * + * @throws Throwable + */ + protected function cloneInstanceWithConstructorCall(ClassContext $context, array $value): static + { + /** @var array $args */ + $args = []; + + foreach ($context->constructorParams as $paramName) { + $propertyContext = $context->properties[$paramName] ?? null; + + if (!$propertyContext) { + DataCreationException::invalidProperty(); + } + + $args[$paramName] = array_key_exists($propertyContext->propertyName, $value) + ? $value[$paramName] + : $this->{$propertyContext->propertyName}; + } + + return $context->newInstanceWithConstructorCall(...$args); + } + + /** + * @param ClassContext $context + * @param array $value + * + * @throws Throwable + */ + protected function cloneInstanceWithoutConstructorCall(ClassContext $context, array $value): static + { + $instance = $context->newInstanceWithoutConstructor(); + + foreach ($context->properties as $propertyContext) { + $instance->{$propertyContext->propertyName} = + array_key_exists($propertyContext->propertyName, $value) + ? $value[$propertyContext->propertyName] + : $this->{$propertyContext->propertyName}; + } + + return $instance; + } +} diff --git a/src/Contracts/CloneableData.php b/src/Contracts/CloneableData.php new file mode 100644 index 0000000..6d5e4c7 --- /dev/null +++ b/src/Contracts/CloneableData.php @@ -0,0 +1,13 @@ +with(firstName: new DateTimeImmutable()); + } + + /** + * @throws Throwable + */ + #[Test] + public function will_throw_an_exception_if_dto_declaration_is_invalid(): void + { + $object = new readonly class ('firstName', 'lastName') extends Data { + #[Computed] + public string $fullName; + public string $familyName; + + public function __construct( + public string $firstName, + string $lastName, + ) { + $this->familyName = $lastName; + $this->fullName = $this->firstName . ' ' . $this->familyName; + } + }; + + self::expectException(DataCreationException::class); + $object->with(lastName: 'Doe'); + } + + /** + * @param array $args + * @param array $expectedProperties + * + * @throws Throwable + */ + #[Test] + #[DataProvider('will_be_able_to_clone_data_provider')] + public function will_be_able_to_clone_data( + CloneableDataContract $object, + array $args, + array $expectedProperties + ): void { + $newObject = $object->with(...$args); + + self::assertNotSame($object, $newObject); + + foreach ($expectedProperties as $propertyName => $value) { + self::assertObjectHasProperty($propertyName, $newObject); + self::assertEquals($value, $newObject->{$propertyName}); + } + } + + /** + * @return array + */ + public static function will_be_able_to_clone_data_provider(): array + { + return [ + 'Will be able to clone scalar type properties' => [ + 'object' => new PersonData('John', 'Doe'), + 'args' => [ + 'firstName' => 'Jane', + 'lastName' => 'Doe', + ], + 'expectedProperties' => [ + 'firstName' => 'Jane', + 'lastName' => 'Doe', + ], + ], + 'Will be able to clone data type properties' => [ + 'object' => new AddressData( + '1234 Elm St', + 'City', + 'State', + '12345', + new CountryData( + 'XX', + 'Country' + ), + null + ), + 'args' => [ + 'country' => new CountryData( + 'YY', + 'Country 2' + ), + 'coordinates' => new CoordinatesData( + 1.234, + 5.678 + ) + ], + 'expectedProperties' => [ + 'street' => '1234 Elm St', + 'city' => 'City', + 'state' => 'State', + 'zip' => '12345', + 'country' => new CountryData( + 'YY', + 'Country 2' + ), + 'coordinates' => new CoordinatesData( + 1.234, + 5.678 + ) + ], + ], + 'Will be able to update computed properties' => [ + 'object' => new PersonData('John', 'Doe'), + 'args' => [ + 'firstName' => 'Jane', + 'lastName' => 'Doe', + ], + 'expectedProperties' => [ + 'firstName' => 'Jane', + 'lastName' => 'Doe', + 'fullName' => 'Jane Doe', + ], + ] + ]; + } +} From 7ff39e6f994e226f13ca101563e8dad98e82d518 Mon Sep 17 00:00:00 2001 From: Fa-BRAIK Date: Fri, 27 Dec 2024 20:36:07 -0500 Subject: [PATCH 2/4] Add Cloneable Data feature with documentation and tests Introduced the Cloneable Data feature to allow `Data` objects to support cloning with updated values using the `with` method. Added documentation, unit tests, examples, and updated related components to reflect this enhancement. --- docs/CloneableData.md | 91 +++++++++++++++++++ docs/EmptyData.md | 2 + docs/Quickstart.md | 1 + src/Concerns/CloneableData.php | 6 +- src/Exceptions/DataCreationException.php | 12 +++ .../DocsDummies/{Todo.php => TodoData.php} | 2 +- tests/Unit/Concerns/CloneableDataTest.php | 13 +++ .../CloneableDataExampleTest.php | 69 ++++++++++++++ .../Documentation/NormalizersExampleTest.php | 6 +- .../Documentation/QuickStartExampleTest.php | 8 +- 10 files changed, 201 insertions(+), 9 deletions(-) create mode 100644 docs/CloneableData.md rename tests/Dummies/DocsDummies/{Todo.php => TodoData.php} (93%) create mode 100644 tests/Unit/Documentation/CloneableDataExampleTest.php diff --git a/docs/CloneableData.md b/docs/CloneableData.md new file mode 100644 index 0000000..6707344 --- /dev/null +++ b/docs/CloneableData.md @@ -0,0 +1,91 @@ +Cloneable Data += + +Sometimes we may want to alter the data of a `Data` object (Partially or completely). +And since `Data` objects are immutable by default, we can't change the data directly. + +To solve this, we can use the `with` function that will return a new instance of the `Data` object with the new data. +Let take the `TodoData` class as an example: + +```php +use Nuxtifyts\PhpDto\Data; +use DateTimeImmutable; + +final readonly class TodoData extends Data +{ + public function __construct( + public string $title, + public string $content, + public Status $status, + public ?DateTimeImmutable $dueDate + ) {} +} +``` + +The `Status` enum is defined as follows: + +```php +enum Status: string +{ + case DEFAULT = 'default'; + case IN_PROGRESS = 'in_progress'; + case DONE = 'done'; +} +``` + +Using `with` function, we can easily create new instances of the `TodoData` class with the new data: + +```php +$emptyTodo = Todo::empty(); + +// ... + +$todo = $emptyTodo->with( + title: 'Learn PHP DTO', + content: 'Learn how to use PHP DTO', + status: Status::IN_PROGRESS +); + +// ... + +$todoWithDueDate = $todo->with( + dueDate: new DateTimeImmutable('2025-01-06') +); +``` + +> We are using the `empty` method +> from [Empty Data](https://github.com/nuxtifyts/php-dto/blob/main/docs/EmptyData.md) +> here + +> `emptyTodo`, `todo` and `todoWithDueDate` are all different instances. + +Computed properties +- + +When cloning a `Data` object, computed properties are automatically updated with the new data. + +```php +use Nuxtifyts\PhpDto\Data; +use Nuxtifyts\PhpDto\Attributes\Property\Computed; + +final readonly class PersonData extends Data +{ + #[Computed] + public string $fullName; + + public function __construct( + public string $firstName, + public string $lastName + ) {} +} +``` + +For example: + +```php +$johnDoe = new PersonData(firstName: 'John', lastName: 'Doe'); + +$janeDoe = $johnDoe->with(firstName: 'Jane'); + +$janeDoe->fullName; // 'Jane Doe' +``` diff --git a/docs/EmptyData.md b/docs/EmptyData.md index 0472617..cb6b39a 100644 --- a/docs/EmptyData.md +++ b/docs/EmptyData.md @@ -36,6 +36,8 @@ By calling the `empty()` method, we can create a new instance of the `Todo` clas $emptyTodo = Todo::empty(); ``` +> This is really useful with [Cloneable Data](https://github.com/nuxtifyts/php-dto/blob/main/docs/CloneableData.md) + The `$emptyTodo` variable will contain the following data: ``` diff --git a/docs/Quickstart.md b/docs/Quickstart.md index e796df2..c3434bc 100644 --- a/docs/Quickstart.md +++ b/docs/Quickstart.md @@ -81,3 +81,4 @@ can be found here: - [Property Attributes](https://github.com/nuxtifyts/php-dto/blob/main/docs/PropertyAttributes.md) - [Data Refiners](https://github.com/nuxtifyts/php-dto/blob/main/docs/DataRefiners.md) - [Empty Data](https://github.com/nuxtifyts/php-dto/blob/main/docs/EmptyData.md) +- [Cloneable Data](https://github.com/nuxtifyts/php-dto/blob/main/docs/CloneableData.md) diff --git a/src/Concerns/CloneableData.php b/src/Concerns/CloneableData.php index f9ba8ec..004fdb8 100644 --- a/src/Concerns/CloneableData.php +++ b/src/Concerns/CloneableData.php @@ -20,6 +20,10 @@ public function with(mixed ...$args): static try { $value = static::normalizeValue($args, static::class); + if ($value === false) { + throw DataCreationException::invalidParamsPassed(static::class); + } + /** @var ClassContext $context */ $context = ClassContext::getInstance(new ReflectionClass(static::class)); @@ -46,7 +50,7 @@ protected function cloneInstanceWithConstructorCall(ClassContext $context, array $propertyContext = $context->properties[$paramName] ?? null; if (!$propertyContext) { - DataCreationException::invalidProperty(); + throw DataCreationException::invalidProperty(); } $args[$paramName] = array_key_exists($propertyContext->propertyName, $value) diff --git a/src/Exceptions/DataCreationException.php b/src/Exceptions/DataCreationException.php index 6e62793..20798ef 100644 --- a/src/Exceptions/DataCreationException.php +++ b/src/Exceptions/DataCreationException.php @@ -11,6 +11,7 @@ class DataCreationException extends Exception protected const int INVALID_PROPERTY = 1; protected const int UNABLE_TO_CREATE_EMPTY_INSTANCE = 2; protected const int UNABLE_TO_CLONE_INSTANCE_WITH_NEW_DATA = 3; + protected const int INVALID_PARAMS_PASSED = 4; public static function unableToCreateInstance( string $class, @@ -52,4 +53,15 @@ public static function unableToCloneInstanceWithNewData( previous: $previous ); } + + public static function invalidParamsPassed( + string $class, + ?Throwable $previous = null + ): self { + return new self( + message: "Invalid params passed to create method of class {$class}", + code: self::INVALID_PARAMS_PASSED, + previous: $previous + ); + } } diff --git a/tests/Dummies/DocsDummies/Todo.php b/tests/Dummies/DocsDummies/TodoData.php similarity index 93% rename from tests/Dummies/DocsDummies/Todo.php rename to tests/Dummies/DocsDummies/TodoData.php index c7c3866..6828acf 100644 --- a/tests/Dummies/DocsDummies/Todo.php +++ b/tests/Dummies/DocsDummies/TodoData.php @@ -8,7 +8,7 @@ use Nuxtifyts\PhpDto\Tests\Dummies\Enums\Todo\Status; use Nuxtifyts\PhpDto\Tests\Dummies\Normalizers\GoalTodoNormalizer; -final readonly class Todo extends Data +final readonly class TodoData extends Data { public function __construct( public string $title, diff --git a/tests/Unit/Concerns/CloneableDataTest.php b/tests/Unit/Concerns/CloneableDataTest.php index ca2bda4..5be710a 100644 --- a/tests/Unit/Concerns/CloneableDataTest.php +++ b/tests/Unit/Concerns/CloneableDataTest.php @@ -40,6 +40,19 @@ public function will_throw_an_exception_if_invalid_value_for_property_is_passed( $person->with(firstName: new DateTimeImmutable()); } + /** + * @throws Throwable + */ + #[Test] + public function will_throw_an_exception_if_invalid_arguments_are_passed_using_with_function(): void + { + $person = new PersonData(firstName: 'John', lastName: 'Doe'); + + self::expectException(DataCreationException::class); + + $person->with('{firstName: "Jane"'); + } + /** * @throws Throwable */ diff --git a/tests/Unit/Documentation/CloneableDataExampleTest.php b/tests/Unit/Documentation/CloneableDataExampleTest.php new file mode 100644 index 0000000..e704cc9 --- /dev/null +++ b/tests/Unit/Documentation/CloneableDataExampleTest.php @@ -0,0 +1,69 @@ + '', + 'content' => '', + 'status' => Status::BACKLOG->value, + 'dueDate' => null + ], $emptyTodo->toArray()); + + $todo = $emptyTodo->with( + title: 'Learn PHP DTO', + content: 'Learn how to use PHP DTO', + status: Status::IN_PROGRESS + ); + + self::assertEquals([ + 'title' => 'Learn PHP DTO', + 'content' => 'Learn how to use PHP DTO', + 'status' => Status::IN_PROGRESS->value, + 'dueDate' => null + ], $todo->toArray()); + + $dueDate = new DateTimeImmutable('2021-10-10'); + $todoWithDueDate = $todo->with(dueDate: $dueDate); + + self::assertEquals( + $dueDate->format('Y-m-d H:i'), + $todoWithDueDate->dueDate?->format('Y-m-d H:i') + ); + } + + /** + * @throws Throwable + */ + #[Test] + public function test_is_can_clone_dts_with_computed_properties(): void + { + $person = new PersonData(firstName: 'John', lastName: 'Doe'); + + self::assertEquals('John Doe', $person->fullName); + + $personWithFirstName = $person->with(firstName: 'Jane'); + + self::assertEquals('Jane Doe', $personWithFirstName->fullName); + } +} diff --git a/tests/Unit/Documentation/NormalizersExampleTest.php b/tests/Unit/Documentation/NormalizersExampleTest.php index f749615..5762c74 100644 --- a/tests/Unit/Documentation/NormalizersExampleTest.php +++ b/tests/Unit/Documentation/NormalizersExampleTest.php @@ -6,13 +6,13 @@ use Nuxtifyts\PhpDto\Tests\Unit\UnitCase; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; -use Nuxtifyts\PhpDto\Tests\Dummies\DocsDummies\Todo; +use Nuxtifyts\PhpDto\Tests\Dummies\DocsDummies\TodoData; use Nuxtifyts\PhpDto\Tests\Dummies\DocsDummies\NonData\Goal; use DateTimeImmutable; use DateTimeInterface; use Throwable; -#[UsesClass(Todo::class)] +#[UsesClass(TodoData::class)] #[UsesClass(Goal::class)] final class NormalizersExampleTest extends UnitCase { @@ -30,7 +30,7 @@ public function will_be_able_to_normalize_instance_of_goal_class_to_todo_class() dueDate: $now ); - $todo = Todo::from($goal); + $todo = TodoData::from($goal); self::assertEquals( [ diff --git a/tests/Unit/Documentation/QuickStartExampleTest.php b/tests/Unit/Documentation/QuickStartExampleTest.php index e2d7778..bb3fb29 100644 --- a/tests/Unit/Documentation/QuickStartExampleTest.php +++ b/tests/Unit/Documentation/QuickStartExampleTest.php @@ -2,7 +2,7 @@ namespace Nuxtifyts\PhpDto\Tests\Unit\Documentation; -use Nuxtifyts\PhpDto\Tests\Dummies\DocsDummies\Todo; +use Nuxtifyts\PhpDto\Tests\Dummies\DocsDummies\TodoData; use Nuxtifyts\PhpDto\Tests\Unit\UnitCase; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\UsesClass; @@ -11,7 +11,7 @@ use DateTimeInterface; use Throwable; -#[UsesClass(Todo::class)] +#[UsesClass(TodoData::class)] final class QuickStartExampleTest extends UnitCase { /** @@ -29,14 +29,14 @@ public function will_perform_serialize_and_deserialize_on_data_transfer_objects_ 'dueDate' => $now->format(DateTimeInterface::ATOM) ]; - $todo = new Todo( + $todo = new TodoData( title: 'Learn PHP DTO', content: 'Learn how to use PHP DTO', status: Status::READY, dueDate: $now ); - $todoFrom = Todo::from([ + $todoFrom = TodoData::from([ 'title' => 'Learn PHP DTO', 'content' => 'Learn how to use PHP DTO', 'status' => 'ready', From 839fc908defe8c9ea6028fdaf22165180f160f8f Mon Sep 17 00:00:00 2001 From: Fa-BRAIK Date: Fri, 27 Dec 2024 20:53:52 -0500 Subject: [PATCH 3/4] Enhance `with` method to support multiple input formats. Added support for cloning `Data` instances using JSON, `stdClass`, and other normalized formats. Updated tests and documentation to reflect the changes, and ensured the method throws an exception when no parameters are passed. --- docs/CloneableData.md | 28 +++++++++++++++ src/Concerns/CloneableData.php | 7 +++- tests/Unit/Concerns/CloneableDataTest.php | 13 +++++++ .../CloneableDataExampleTest.php | 34 +++++++++++++++++-- 4 files changed, 79 insertions(+), 3 deletions(-) diff --git a/docs/CloneableData.md b/docs/CloneableData.md index 6707344..545bf90 100644 --- a/docs/CloneableData.md +++ b/docs/CloneableData.md @@ -89,3 +89,31 @@ $janeDoe = $johnDoe->with(firstName: 'Jane'); $janeDoe->fullName; // 'Jane Doe' ``` + +Normalizers +- + +When cloning a `Data` object, normalizers that are typically used when hydrating a `Data` object +using `from` method are also used. + +This will allow the ability to pass `json` data, `ArrayAccess` or `stdClass` objects for example to the `with` method. +If a custom normalizer is implemented for the `Data` class, it can be used as well. + +```php +$johnDoe = new PersonDaa('John', 'Doe'); + +$janeDoe = $johnDoe->with('{"firstName": "Jane"}'); + +$janeDoe->fullName; // 'Jane Doe' +``` + +Using an `stdClass` object: + +```php +$object = new stdClass(); +$object->firstName = 'Jake'; + +$jakeDoe = $janeDoe->with($object); + +$jakeDoe->fullName; // 'Jake Doe' +``` diff --git a/src/Concerns/CloneableData.php b/src/Concerns/CloneableData.php index 004fdb8..44fbb57 100644 --- a/src/Concerns/CloneableData.php +++ b/src/Concerns/CloneableData.php @@ -18,7 +18,12 @@ trait CloneableData public function with(mixed ...$args): static { try { - $value = static::normalizeValue($args, static::class); + if (empty($args)) { + throw DataCreationException::invalidParamsPassed(static::class); + } + + $value = static::normalizeValue($args, static::class) + ?: static::normalizeValue($args[0], static::class); if ($value === false) { throw DataCreationException::invalidParamsPassed(static::class); diff --git a/tests/Unit/Concerns/CloneableDataTest.php b/tests/Unit/Concerns/CloneableDataTest.php index 5be710a..5cd89ef 100644 --- a/tests/Unit/Concerns/CloneableDataTest.php +++ b/tests/Unit/Concerns/CloneableDataTest.php @@ -77,6 +77,19 @@ public function __construct( $object->with(lastName: 'Doe'); } + /** + * @throws Throwable + */ + #[Test] + public function will_throw_an_exception_if_no_param_is_passed(): void + { + $person = new PersonData(firstName: 'John', lastName: 'Doe'); + + self::expectException(DataCreationException::class); + + $person->with(); + } + /** * @param array $args * @param array $expectedProperties diff --git a/tests/Unit/Documentation/CloneableDataExampleTest.php b/tests/Unit/Documentation/CloneableDataExampleTest.php index e704cc9..25709a4 100644 --- a/tests/Unit/Documentation/CloneableDataExampleTest.php +++ b/tests/Unit/Documentation/CloneableDataExampleTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\Attributes\UsesClass; use PHPUnit\Framework\Attributes\Test; use Nuxtifyts\PhpDto\Tests\Dummies\Enums\Todo\Status; +use stdClass; use DateTimeImmutable; use Throwable; @@ -19,7 +20,7 @@ final class CloneableDataExampleTest extends UnitCase * @throws Throwable */ #[Test] - public function test_it_can_clone_data_as_example_in_documentation(): void + public function it_can_clone_data_as_example_in_documentation(): void { $emptyTodo = TodoData::empty(); @@ -56,7 +57,7 @@ public function test_it_can_clone_data_as_example_in_documentation(): void * @throws Throwable */ #[Test] - public function test_is_can_clone_dts_with_computed_properties(): void + public function it_can_clone_data_instances_with_computed_properties(): void { $person = new PersonData(firstName: 'John', lastName: 'Doe'); @@ -66,4 +67,33 @@ public function test_is_can_clone_dts_with_computed_properties(): void self::assertEquals('Jane Doe', $personWithFirstName->fullName); } + + /** + * @throws Throwable + */ + #[Test] + public function it_can_clone_data_instances_by_passing_json(): void + { + $person = new PersonData(firstName: 'John', lastName: 'Doe'); + + $personWithFirstName = $person->with('{"firstName": "Jane"}'); + + self::assertEquals('Jane Doe', $personWithFirstName->fullName); + } + + /** + * @throws Throwable + */ + #[Test] + public function it_can_clone_data_instances_by_passing_std_object(): void + { + $person = new PersonData(firstName: 'John', lastName: 'Doe'); + + $object = new stdClass(); + $object->firstName = 'Jane'; + + $personWithFirstName = $person->with($object); + + self::assertEquals('Jane Doe', $personWithFirstName->fullName); + } } From 9d637189bcdb970ff3f2e528857609edb4ef5915 Mon Sep 17 00:00:00 2001 From: Fa-BRAIK Date: Sat, 28 Dec 2024 08:04:30 -0500 Subject: [PATCH 4/4] Add validation and tests for invalid data creation and serialization Refactor the `BaseData::create` method to handle invalid input more effectively, using a specific exception for clarity. Introduce `UselessDataCipher` as a test dummy and add unit tests to ensure proper exception handling for invalid serialization and data creation. --- src/Concerns/BaseData.php | 14 ++------ .../Dummies/DataCiphers/UselessDataCipher.php | 26 ++++++++++++++ tests/Unit/Concerns/BaseDataTest.php | 34 +++++++++++++++++++ 3 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 tests/Dummies/DataCiphers/UselessDataCipher.php diff --git a/src/Concerns/BaseData.php b/src/Concerns/BaseData.php index 0475adb..d5cc8c7 100644 --- a/src/Concerns/BaseData.php +++ b/src/Concerns/BaseData.php @@ -21,20 +21,12 @@ trait BaseData */ final public static function create(mixed ...$args): static { - if (array_any( - array_keys($args), - static fn (string|int $arg) => is_numeric($arg) - )) { - throw DataCreationException::invalidProperty(); - } - try { - $value = static::normalizeValue($args, static::class); + $value = static::normalizeValue($args, static::class) + ?: static::normalizeValue($args[0] ?? [], static::class); if ($value === false) { - throw new DeserializeException( - code: DeserializeException::INVALID_VALUE_ERROR_CODE - ); + throw DataCreationException::invalidParamsPassed(static::class); } /** @var ClassContext $context */ diff --git a/tests/Dummies/DataCiphers/UselessDataCipher.php b/tests/Dummies/DataCiphers/UselessDataCipher.php new file mode 100644 index 0000000..28906f7 --- /dev/null +++ b/tests/Dummies/DataCiphers/UselessDataCipher.php @@ -0,0 +1,26 @@ +jsonSerialize(); + } + + #[Test] + public function will_throw_an_exception_when_invalid_data_is_passed_to_create_function(): void + { + self::expectException(DataCreationException::class); + + PointData::create('{"x: 1, "y": 2}'); + } + /** * @throws Throwable */ @@ -606,6 +634,12 @@ public function will_be_able_to_create_an_instance_using_create(): void self::assertEquals(1, $point->x); self::assertEquals(2, $point->y); + $point = PointData::create('{"x": 3, "y": 4}'); + + self::assertInstanceOf(PointData::class, $point); + self::assertEquals(3, $point->x); + self::assertEquals(4, $point->y); + // Make sure we skip deciphering the key $pointGroup = PointGroupData::create( key: 'random-key'