Skip to content

Commit

Permalink
Set dynamic properties with predicate
Browse files Browse the repository at this point in the history
  • Loading branch information
veewee committed Jun 12, 2024
1 parent 34b3778 commit ed58316
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 5 deletions.
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@
"./vendor/bin/infection --show-mutations -v"
],
"ci": [
"@autoload",
"@parallel cs psalm tests",
"@parallel infection"
]
Expand Down
3 changes: 3 additions & 0 deletions docs/reflect.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
14 changes: 10 additions & 4 deletions src/Reflect/properties_set.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -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
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/Reflect/PropertiesSetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
}
}

0 comments on commit ed58316

Please sign in to comment.