Skip to content

Commit 889fb47

Browse files
committed
Move class components to runtime
1 parent d682fe3 commit 889fb47

File tree

11 files changed

+162
-168
lines changed

11 files changed

+162
-168
lines changed

src/Type/ClassType/ClassInstantiator/ClassInstantiatorInterface.php renamed to src/Runtime/ClassInstantiator/ClassInstantiatorInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace TypeLang\Mapper\Type\ClassType\ClassInstantiator;
5+
namespace TypeLang\Mapper\Runtime\ClassInstantiator;
66

77
use TypeLang\Mapper\Exception\Mapping\NonInstantiatableObjectException;
88
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;

src/Type/ClassType/ClassInstantiator/ReflectionClassInstantiator.php renamed to src/Runtime/ClassInstantiator/ReflectionClassInstantiator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace TypeLang\Mapper\Type\ClassType\ClassInstantiator;
5+
namespace TypeLang\Mapper\Runtime\ClassInstantiator;
66

77
use TypeLang\Mapper\Exception\Mapping\NonInstantiatableObjectException;
88
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Runtime\PropertyAccessor;
6+
7+
class NullPropertyAccessor implements PropertyAccessorInterface
8+
{
9+
public function getValue(object $object, string $property): mixed
10+
{
11+
throw new \LogicException(\sprintf(
12+
'The %s::$%s property is not readable',
13+
$object::class,
14+
$property,
15+
));
16+
}
17+
18+
public function isReadable(object $object, string $property): bool
19+
{
20+
return false;
21+
}
22+
23+
public function setValue(object $object, string $property, mixed $value): void
24+
{
25+
throw new \LogicException(\sprintf(
26+
'The %s::$%s property is not writable',
27+
$object::class,
28+
$property,
29+
));
30+
}
31+
32+
public function isWritable(object $object, string $property): bool
33+
{
34+
return false;
35+
}
36+
}

src/Type/ClassType/PropertyAccessor/PropertyAccessorInterface.php renamed to src/Runtime/PropertyAccessor/PropertyAccessorInterface.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace TypeLang\Mapper\Type\ClassType\PropertyAccessor;
6-
7-
use TypeLang\Mapper\Mapping\Metadata\PropertyMetadata;
5+
namespace TypeLang\Mapper\Runtime\PropertyAccessor;
86

97
interface PropertyAccessorInterface
108
{
@@ -13,28 +11,28 @@ interface PropertyAccessorInterface
1311
*
1412
* @throws \Throwable occurs in case of value cannot be reads
1513
*/
16-
public function getValue(object $object, PropertyMetadata $meta): mixed;
14+
public function getValue(object $object, string $property): mixed;
1715

1816
/**
1917
* Returns {@see true} if the field is readable by the implementation,
2018
* and {@see false} otherwise.
2119
*
2220
* This method must not return any exceptions.
2321
*/
24-
public function isReadable(object $object, PropertyMetadata $meta): bool;
22+
public function isReadable(object $object, string $property): bool;
2523

2624
/**
2725
* Updates the value of the specified field inside the passed object.
2826
*
2927
* @throws \Throwable occurs in case of value cannot be writes
3028
*/
31-
public function setValue(object $object, PropertyMetadata $meta, mixed $value): void;
29+
public function setValue(object $object, string $property, mixed $value): void;
3230

3331
/**
3432
* Returns {@see true} if the field is writable by the implementation,
3533
* and {@see false} otherwise.
3634
*
3735
* This method must not return any exceptions.
3836
*/
39-
public function isWritable(object $object, PropertyMetadata $meta): bool;
37+
public function isWritable(object $object, string $property): bool;
4038
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TypeLang\Mapper\Runtime\PropertyAccessor;
6+
7+
final class ReflectionPropertyAccessor implements PropertyAccessorInterface
8+
{
9+
/**
10+
* @throws \ReflectionException
11+
*/
12+
private function getPropertyForGet(object $object, string $property): \ReflectionProperty
13+
{
14+
return new \ReflectionProperty($object, $property);
15+
}
16+
17+
/**
18+
* @throws \ReflectionException
19+
*/
20+
private function getPropertyForSet(object $object, string $property): \ReflectionProperty
21+
{
22+
$reflection = new \ReflectionProperty($object, $property);
23+
24+
$context = $reflection->getDeclaringClass();
25+
26+
return $context->getProperty($property);
27+
}
28+
29+
public function getValue(object $object, string $property): mixed
30+
{
31+
try {
32+
$reflection = $this->getPropertyForGet($object, $property);
33+
34+
return $reflection->getValue($object);
35+
} catch (\ReflectionException) {
36+
return null;
37+
}
38+
}
39+
40+
public function isReadable(object $object, string $property): bool
41+
{
42+
if (!\property_exists($object, $property)) {
43+
return false;
44+
}
45+
46+
if (\PHP_VERSION_ID >= 80400) {
47+
try {
48+
return $this->isReadableUsingHooks($object, $property);
49+
} catch (\ReflectionException) {
50+
return false;
51+
}
52+
}
53+
54+
return true;
55+
}
56+
57+
/**
58+
* @throws \ReflectionException
59+
*/
60+
private function isReadableUsingHooks(object $object, string $property): bool
61+
{
62+
$reflection = $this->getPropertyForSet($object, $property);
63+
64+
// @phpstan-ignore-next-line : Requires PHPStan-compatible version for PHP 8.4
65+
return $reflection->getHook(\PropertyHookType::Get) !== null
66+
// @phpstan-ignore-next-line : Requires PHPStan-compatible version for PHP 8.4
67+
|| $reflection->getHook(\PropertyHookType::Set) === null;
68+
}
69+
70+
public function setValue(object $object, string $property, mixed $value): void
71+
{
72+
try {
73+
$reflection = $this->getPropertyForSet($object, $property);
74+
75+
$reflection->setValue($object, $value);
76+
} catch (\ReflectionException) {
77+
return;
78+
}
79+
}
80+
81+
public function isWritable(object $object, string $property): bool
82+
{
83+
if (\PHP_VERSION_ID >= 80400) {
84+
try {
85+
return $this->isWritableUsingHooks($object, $property);
86+
} catch (\ReflectionException) {
87+
return false;
88+
}
89+
}
90+
91+
return true;
92+
}
93+
94+
/**
95+
* @throws \ReflectionException
96+
*/
97+
private function isWritableUsingHooks(object $object, string $property): bool
98+
{
99+
$reflection = $this->getPropertyForSet($object, $property);
100+
101+
// @phpstan-ignore-next-line : Requires PHPStan-compatible version for PHP 8.4
102+
return $reflection->getHook(\PropertyHookType::Get) === null
103+
// @phpstan-ignore-next-line : Requires PHPStan-compatible version for PHP 8.4
104+
|| $reflection->getHook(\PropertyHookType::Set) !== null;
105+
}
106+
}

src/Type/Builder/ClassTypeBuilder.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
use TypeLang\Mapper\Mapping\Driver\DriverInterface;
88
use TypeLang\Mapper\Mapping\Driver\ReflectionDriver;
9+
use TypeLang\Mapper\Runtime\ClassInstantiator\ClassInstantiatorInterface;
10+
use TypeLang\Mapper\Runtime\ClassInstantiator\ReflectionClassInstantiator;
911
use TypeLang\Mapper\Runtime\Parser\TypeParserInterface;
12+
use TypeLang\Mapper\Runtime\PropertyAccessor\PropertyAccessorInterface;
13+
use TypeLang\Mapper\Runtime\PropertyAccessor\ReflectionPropertyAccessor;
1014
use TypeLang\Mapper\Runtime\Repository\TypeRepositoryInterface;
1115
use TypeLang\Mapper\Type\ClassType;
12-
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ClassInstantiatorInterface;
13-
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ReflectionClassInstantiator;
14-
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\PropertyAccessorInterface;
15-
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\ReflectionPropertyAccessor;
1616
use TypeLang\Parser\Node\Stmt\NamedTypeNode;
1717
use TypeLang\Parser\Node\Stmt\TypeStatement;
1818

src/Type/ClassType.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
namespace TypeLang\Mapper\Type;
66

77
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
8-
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ClassInstantiatorInterface;
8+
use TypeLang\Mapper\Runtime\ClassInstantiator\ClassInstantiatorInterface;
9+
use TypeLang\Mapper\Runtime\PropertyAccessor\PropertyAccessorInterface;
910
use TypeLang\Mapper\Type\ClassType\ClassTypeDenormalizer;
1011
use TypeLang\Mapper\Type\ClassType\ClassTypeNormalizer;
11-
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\PropertyAccessorInterface;
1212

1313
/**
1414
* @template T of object

src/Type/ClassType/ClassTypeDenormalizer.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
use TypeLang\Mapper\Mapping\Metadata\ClassMetadata;
1313
use TypeLang\Mapper\Mapping\Metadata\DiscriminatorMapMetadata;
1414
use TypeLang\Mapper\Mapping\Metadata\PropertyMetadata;
15+
use TypeLang\Mapper\Runtime\ClassInstantiator\ClassInstantiatorInterface;
1516
use TypeLang\Mapper\Runtime\Context;
1617
use TypeLang\Mapper\Runtime\Path\Entry\ObjectEntry;
1718
use TypeLang\Mapper\Runtime\Path\Entry\ObjectPropertyEntry;
18-
use TypeLang\Mapper\Type\ClassType\ClassInstantiator\ClassInstantiatorInterface;
19-
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\PropertyAccessorInterface;
19+
use TypeLang\Mapper\Runtime\PropertyAccessor\PropertyAccessorInterface;
2020
use TypeLang\Mapper\Type\TypeInterface;
2121

2222
/**
@@ -206,7 +206,7 @@ private function denormalizeObject(array $value, object $object, Context $contex
206206
$entrance = $context->enter($value, new ObjectPropertyEntry($meta->alias));
207207

208208
// Skip the property when not writable
209-
if (!$this->accessor->isWritable($object, $meta)) {
209+
if (!$this->accessor->isWritable($object, $meta->name)) {
210210
continue;
211211
}
212212

@@ -246,7 +246,7 @@ private function denormalizeObject(array $value, object $object, Context $contex
246246
);
247247
}
248248

249-
$this->accessor->setValue($object, $meta, $element);
249+
$this->accessor->setValue($object, $meta->name, $element);
250250
}
251251
}
252252
}

src/Type/ClassType/ClassTypeNormalizer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use TypeLang\Mapper\Runtime\Context;
1212
use TypeLang\Mapper\Runtime\Path\Entry\ObjectEntry;
1313
use TypeLang\Mapper\Runtime\Path\Entry\ObjectPropertyEntry;
14-
use TypeLang\Mapper\Type\ClassType\PropertyAccessor\PropertyAccessorInterface;
14+
use TypeLang\Mapper\Runtime\PropertyAccessor\PropertyAccessorInterface;
1515
use TypeLang\Mapper\Type\TypeInterface;
1616

1717
/**
@@ -84,11 +84,11 @@ protected function normalizeObject(object $object, Context $context): array
8484
$entrance = $context->enter($object, new ObjectPropertyEntry($meta->name));
8585

8686
// Skip the property when not readable
87-
if (!$this->accessor->isReadable($object, $meta)) {
87+
if (!$this->accessor->isReadable($object, $meta->name)) {
8888
continue;
8989
}
9090

91-
$element = $this->accessor->getValue($object, $meta);
91+
$element = $this->accessor->getValue($object, $meta->name);
9292

9393
// Skip the property when condition is matched
9494
foreach ($meta->getSkipConditions() as $condition) {

src/Type/ClassType/PropertyAccessor/NullPropertyAccessor.php

Lines changed: 0 additions & 38 deletions
This file was deleted.

0 commit comments

Comments
 (0)