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/Reflect/PropertiesSetTest.php b/tests/unit/Reflect/PropertiesSetTest.php index 514727e..ecc4359 100644 --- a/tests/unit/Reflect/PropertiesSetTest.php +++ b/tests/unit/Reflect/PropertiesSetTest.php @@ -34,4 +34,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'); + } }