Skip to content

Commit a4dac97

Browse files
authored
Fix __call called before setting/getting the property directly (#642)
1 parent e8d570a commit a4dac97

File tree

4 files changed

+183
-10
lines changed

4 files changed

+183
-10
lines changed

src/Utils/PropertyAccessor.php

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,14 @@ class PropertyAccessor
2222
*/
2323
public static function findGetter(string $class, string $propertyName): string|null
2424
{
25-
$name = ucfirst($propertyName);
26-
2725
foreach (['get', 'is'] as $prefix) {
28-
$methodName = $prefix . $name;
26+
$methodName = self::propertyToMethodName($prefix, $propertyName);
27+
2928
if (self::isPublicMethod($class, $methodName)) {
3029
return $methodName;
3130
}
3231
}
3332

34-
if (method_exists($class, '__call')) {
35-
return 'get' . $name;
36-
}
37-
3833
return null;
3934
}
4035

@@ -43,10 +38,9 @@ public static function findGetter(string $class, string $propertyName): string|n
4338
*/
4439
public static function findSetter(string $class, string $propertyName): string|null
4540
{
46-
$name = ucfirst($propertyName);
41+
$methodName = self::propertyToMethodName('set', $propertyName);
4742

48-
$methodName = 'set' . $name;
49-
if (self::isPublicMethod($class, $methodName) || method_exists($class, '__call')) {
43+
if (self::isPublicMethod($class, $methodName)) {
5044
return $methodName;
5145
}
5246

@@ -66,6 +60,12 @@ public static function getValue(object $object, string $propertyName, mixed ...$
6660
return $object->$propertyName;
6761
}
6862

63+
if (method_exists($class, '__call')) {
64+
$method = self::propertyToMethodName('get', $propertyName);
65+
66+
return $object->$method(...$args);
67+
}
68+
6969
throw AccessPropertyException::createForUnreadableProperty($class, $propertyName);
7070
}
7171

@@ -84,6 +84,13 @@ public static function setValue(object $instance, string $propertyName, mixed $v
8484
return;
8585
}
8686

87+
if (method_exists($class, '__call')) {
88+
$method = self::propertyToMethodName('set', $propertyName);
89+
90+
$instance->$method($value);
91+
return;
92+
}
93+
8794
throw AccessPropertyException::createForUnwritableProperty($class, $propertyName);
8895
}
8996

@@ -117,4 +124,9 @@ private static function isValidGetter(string $class, string $methodName): bool
117124

118125
return true;
119126
}
127+
128+
private static function propertyToMethodName(string $prefix, string $propertyName): string
129+
{
130+
return $prefix . ucfirst($propertyName);
131+
}
120132
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace TheCodingMachine\GraphQLite\Fixtures\Types;
4+
5+
use TheCodingMachine\GraphQLite\Annotations\Field;
6+
7+
class GetterSetterType
8+
{
9+
public function __construct(
10+
#[Field]
11+
public string $one = '',
12+
#[Field]
13+
public string $two = '',
14+
#[Field]
15+
public bool $three = false,
16+
#[Field]
17+
public string $four = '',
18+
)
19+
{
20+
}
21+
22+
public function getTwo(string $arg = ''): string
23+
{
24+
return $arg;
25+
}
26+
27+
public function setTwo(string $value): void
28+
{
29+
$this->two = $value . ' set';
30+
}
31+
32+
public function isThree(string $arg = ''): bool
33+
{
34+
return $arg === 'foo';
35+
}
36+
37+
private function getFour(string $arg = ''): string
38+
{
39+
throw new \RuntimeException('Should not be called');
40+
}
41+
42+
private function setFour(string $value, string $arg): void
43+
{
44+
throw new \RuntimeException('Should not be called');
45+
}
46+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace TheCodingMachine\GraphQLite\Fixtures\Types;
4+
5+
class MagicGetterSetterType extends GetterSetterType
6+
{
7+
private string $magic;
8+
9+
public function __get(string $name)
10+
{
11+
return $this->magic;
12+
}
13+
14+
public function __call(string $name, array $arguments)
15+
{
16+
$this->magic = 'magic';
17+
18+
return 'magic';
19+
}
20+
}

tests/Utils/PropertyAccessorTest.php

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
namespace TheCodingMachine\GraphQLite\Utils;
4+
5+
use Exception;
6+
use PHPUnit\Framework\TestCase;
7+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Contact;
8+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Post;
9+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Preferences;
10+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Product;
11+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\TrickyProduct;
12+
use TheCodingMachine\GraphQLite\Fixtures\Types\GetterSetterType;
13+
use TheCodingMachine\GraphQLite\Fixtures\Types\MagicGetterSetterType;
14+
15+
class PropertyAccessorTest extends TestCase
16+
{
17+
/**
18+
* @dataProvider findGetterProvider
19+
*/
20+
public function testFindGetter(mixed $expected, string $class, string $propertyName): void
21+
{
22+
self::assertSame($expected, PropertyAccessor::findGetter($class, $propertyName));
23+
}
24+
25+
public static function findGetterProvider(): iterable
26+
{
27+
yield 'regular property' => [null, MagicGetterSetterType::class, 'one'];
28+
yield 'getter' => ['getTwo', MagicGetterSetterType::class, 'two'];
29+
yield 'isser' => ['isThree', MagicGetterSetterType::class, 'three'];
30+
yield 'private getter' => [null, MagicGetterSetterType::class, 'four'];
31+
yield 'undefined property' => [null, MagicGetterSetterType::class, 'twenty'];
32+
}
33+
34+
/**
35+
* @dataProvider findSetterProvider
36+
*/
37+
public function testFindSetter(mixed $expected, string $class, string $propertyName): void
38+
{
39+
self::assertSame($expected, PropertyAccessor::findSetter($class, $propertyName));
40+
}
41+
42+
public static function findSetterProvider(): iterable
43+
{
44+
yield 'regular property' => [null, MagicGetterSetterType::class, 'one'];
45+
yield 'setter' => ['setTwo', MagicGetterSetterType::class, 'two'];
46+
yield 'private setter' => [null, MagicGetterSetterType::class, 'four'];
47+
yield 'undefined property' => [null, MagicGetterSetterType::class, 'twenty'];
48+
}
49+
50+
/**
51+
* @dataProvider getValueProvider
52+
*/
53+
public function testGetValue(mixed $expected, object $object, string $propertyName, array $args = []): void
54+
{
55+
if ($expected instanceof Exception) {
56+
$this->expectExceptionObject($expected);
57+
}
58+
59+
self::assertSame($expected, PropertyAccessor::getValue($object, $propertyName, ...$args));
60+
}
61+
62+
public static function getValueProvider(): iterable
63+
{
64+
yield 'regular property' => ['result', new MagicGetterSetterType(one: 'result'), 'one'];
65+
yield 'getter' => ['result', new MagicGetterSetterType(), 'two', ['result']];
66+
yield 'isser #1' => [true, new MagicGetterSetterType(), 'three', ['foo']];
67+
yield 'isser #2' => [false, new MagicGetterSetterType(), 'three', ['bar']];
68+
yield 'private getter' => ['result', new MagicGetterSetterType(four: 'result'), 'four'];
69+
yield 'magic getter' => ['magic', new MagicGetterSetterType(), 'twenty'];
70+
yield 'undefined property' => [AccessPropertyException::createForUnreadableProperty(GetterSetterType::class, 'twenty'), new GetterSetterType(), 'twenty'];
71+
}
72+
73+
/**
74+
* @dataProvider setValueProvider
75+
*/
76+
public function testSetValue(mixed $expected, object $object, string $propertyName, mixed $value): void
77+
{
78+
if ($expected instanceof Exception) {
79+
$this->expectExceptionObject($expected);
80+
}
81+
82+
PropertyAccessor::setValue($object, $propertyName, $value);
83+
84+
self::assertSame($expected, $object->{$propertyName});
85+
}
86+
87+
public static function setValueProvider(): iterable
88+
{
89+
yield 'regular property' => ['result', new MagicGetterSetterType(one: 'result'), 'one', 'result'];
90+
yield 'setter' => ['result set', new MagicGetterSetterType(), 'two', 'result'];
91+
yield 'private setter' => ['result', new MagicGetterSetterType(four: 'result'), 'four', 'result'];
92+
yield 'magic setter' => ['magic', new MagicGetterSetterType(), 'twenty', 'result'];
93+
yield 'undefined property' => [AccessPropertyException::createForUnwritableProperty(GetterSetterType::class, 'twenty'), new GetterSetterType(), 'twenty', 'result'];
94+
}
95+
}

0 commit comments

Comments
 (0)