From 05ad673028627c226a3ae2eb0e375366f5b912c9 Mon Sep 17 00:00:00 2001 From: Toon Verwerft Date: Wed, 12 Jun 2024 09:34:04 +0200 Subject: [PATCH] Set dynamic properties with predicate --- composer.json | 1 - docs/reflect.md | 3 +++ src/Reflect/properties_set.php | 14 +++++++--- tests/unit/Iso/ObjectDataTest.php | 5 ++-- tests/unit/Reflect/PropertiesSetTest.php | 34 ++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 08d31f5..89f33e3 100644 --- a/composer.json +++ b/composer.json @@ -67,7 +67,6 @@ "./vendor/bin/infection --show-mutations -v" ], "ci": [ - "@autoload", "@parallel cs psalm tests", "@parallel infection" ] diff --git a/docs/reflect.md b/docs/reflect.md index 36afb3f..b8d5552 100644 --- a/docs/reflect.md +++ b/docs/reflect.md @@ -191,6 +191,9 @@ try { } ``` +**Note:** When setting a predicate on a dynamic class, new properties will always be added. +The next time you call `properties_get` on the same object, the predicate knows about the new property and will take it into account whilst filtering. + #### property_get Detects the value of a property for a given object. diff --git a/src/Reflect/properties_set.php b/src/Reflect/properties_set.php index d881edb..6759233 100644 --- a/src/Reflect/properties_set.php +++ b/src/Reflect/properties_set.php @@ -6,7 +6,10 @@ use VeeWee\Reflecta\Reflect\Exception\UnreflectableException; use VeeWee\Reflecta\Reflect\Type\ReflectedClass; use VeeWee\Reflecta\Reflect\Type\ReflectedProperty; +use function Psl\Dict\diff_by_key; +use function Psl\Dict\filter; use function Psl\Dict\intersect_by_key; +use function Psl\Dict\merge; use function Psl\Iter\reduce_with_keys; /** @@ -15,17 +18,20 @@ * @param null|Closure(ReflectedProperty): bool $predicate * * @return T - *@throws UnreflectableException + * @throws UnreflectableException * * @template T of object - * */ function properties_set(object $object, array $values, Closure|null $predicate = null): object { - $properties = ReflectedClass::fromObject($object)->properties($predicate); + $class = ReflectedClass::fromObject($object); + + $allProperties = $class->properties(); + $filteredProperties = $predicate ? filter($allProperties, $predicate) : $allProperties; + $newValues = $class->isDynamic() ? diff_by_key($values, $allProperties) : []; return reduce_with_keys( - intersect_by_key($values, $properties), + merge($newValues, intersect_by_key($values, $filteredProperties)), /** * @param T $object * @return T diff --git a/tests/unit/Iso/ObjectDataTest.php b/tests/unit/Iso/ObjectDataTest.php index e23bf6b..5b72387 100644 --- a/tests/unit/Iso/ObjectDataTest.php +++ b/tests/unit/Iso/ObjectDataTest.php @@ -36,11 +36,10 @@ public function test_it_can_use_alternate_lens(): void private int $y = 200; }; - $iso = object_data(X::class, properties(property_visibility(Visibility::Public))); + $iso = object_data($x::class, properties(property_visibility(Visibility::Public))); $expectedData = ['z' => 100]; - $expectedInstance = new X(); - $expectedInstance->z = 100; + $expectedInstance = clone $x; $instance = $iso->from($expectedData); $actualData = $iso->to($instance); diff --git a/tests/unit/Reflect/PropertiesSetTest.php b/tests/unit/Reflect/PropertiesSetTest.php index 514727e..ec72700 100644 --- a/tests/unit/Reflect/PropertiesSetTest.php +++ b/tests/unit/Reflect/PropertiesSetTest.php @@ -4,6 +4,7 @@ namespace VeeWee\Reflecta\UnitTests\Reflect; use PHPUnit\Framework\TestCase; +use stdClass; use VeeWee\Reflecta\Reflect\Type\ReflectedProperty; use VeeWee\Reflecta\TestFixtures\Dynamic; use VeeWee\Reflecta\TestFixtures\X; @@ -34,4 +35,37 @@ public function test_it_can_set_with_predicate_filter(): void static::assertSame($actual->x, '456'); static::assertSame($actual->y, '123'); } + + public function test_it_can_hydrate_new_props_on_std_class(): void + { + $x = new stdClass(); + $x->foo = 'foo'; + + $actual = properties_set($x, ['bar' => 'bar']); + + static::assertNotSame($x, $actual); + static::assertInstanceOf(stdClass::class, $actual); + static::assertSame($actual->foo, 'foo'); + static::assertSame($actual->bar, 'bar'); + } + + public function test_it_can_hydrate_new_props_on_std_class_with_predicate(): void + { + $x = new stdClass(); + $x->foo = 'foo'; + + $actual = properties_set( + $x, + [ + 'foo' => 'baz', + 'bar' => 'bar', + ], + static fn (ReflectedProperty $property): bool => false + ); + + static::assertNotSame($x, $actual); + static::assertInstanceOf(stdClass::class, $actual); + static::assertSame($actual->foo, 'foo'); + static::assertSame($actual->bar, 'bar'); + } }