-
-
Notifications
You must be signed in to change notification settings - Fork 328
/
Copy pathSymfonyPropertyAccessorHydrator.php
131 lines (111 loc) · 4.86 KB
/
SymfonyPropertyAccessorHydrator.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
<?php
/*
* This file is part of the Alice package.
*
* (c) Nelmio <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Nelmio\Alice\Generator\Hydrator\Property;
use Nelmio\Alice\Definition\Object\SimpleObject;
use Nelmio\Alice\Definition\Property;
use Nelmio\Alice\Generator\GenerationContext;
use Nelmio\Alice\Generator\Hydrator\PropertyHydratorInterface;
use Nelmio\Alice\IsAServiceTrait;
use Nelmio\Alice\ObjectInterface;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\HydrationException;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\HydrationExceptionFactory;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\InaccessiblePropertyException;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\InvalidArgumentException;
use Nelmio\Alice\Throwable\Exception\Generator\Hydrator\NoSuchPropertyException;
use ReflectionEnum;
use ReflectionException;
use ReflectionNamedType;
use ReflectionProperty;
use ReflectionType;
use Symfony\Component\PropertyAccess\Exception\AccessException as SymfonyAccessException;
use Symfony\Component\PropertyAccess\Exception\ExceptionInterface as SymfonyPropertyAccessException;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException as SymfonyInvalidArgumentException;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException as SymfonyNoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use TypeError;
use function enum_exists;
final class SymfonyPropertyAccessorHydrator implements PropertyHydratorInterface
{
use IsAServiceTrait;
/**
* @var PropertyAccessorInterface
*/
private $propertyAccessor;
public function __construct(PropertyAccessorInterface $propertyAccessor)
{
$this->propertyAccessor = $propertyAccessor;
}
/**
* @throws NoSuchPropertyException
* @throws InaccessiblePropertyException
* @throws InvalidArgumentException When the typehint does not match for example
* @throws HydrationException
*/
public function hydrate(ObjectInterface $object, Property $property, GenerationContext $context): ObjectInterface
{
$instance = $object->getInstance();
try {
$this->propertyAccessor->setValue($instance, $property->getName(), $property->getValue());
} catch (SymfonyNoSuchPropertyException $exception) {
throw HydrationExceptionFactory::createForCouldNotHydrateObjectWithProperty($object, $property, 0, $exception);
} catch (SymfonyAccessException $exception) {
throw HydrationExceptionFactory::createForInaccessibleProperty($object, $property, 0, $exception);
} catch (SymfonyInvalidArgumentException $exception) {
// as a fallback check if the property might be an enum
if (
null !== ($enumType = self::getEnumType($instance, $property))
&& null !== $newProperty = self::castValueToEnum($enumType, $property)
) {
return $this->hydrate($object, $newProperty, $context);
}
throw HydrationExceptionFactory::createForInvalidProperty($object, $property, 0, $exception);
} catch (SymfonyPropertyAccessException $exception) {
throw HydrationExceptionFactory::create($object, $property, 0, $exception);
} catch (TypeError $error) {
throw HydrationExceptionFactory::createForInvalidProperty($object, $property, 0, $error);
}
return new SimpleObject($object->getId(), $instance);
}
private static function getEnumType($instance, Property $property): ?ReflectionType
{
try {
$enumType = (new ReflectionProperty($instance, $property->getName()))->getType();
} catch (ReflectionException) {
// property might not exist
return null;
}
if (null === $enumType) {
// might not have a type
return null;
}
if ($enumType instanceof ReflectionNamedType
&& !enum_exists($enumType->getName())
) {
// might not be an enum
return null;
}
return $enumType;
}
private static function castValueToEnum(ReflectionType $enumType, Property $property): ?Property
{
if (!$enumType instanceof ReflectionNamedType) {
return null;
}
$reflectionEnumBackedCases = (new ReflectionEnum($enumType->getName()))->getCases();
foreach ($reflectionEnumBackedCases as $reflectionCase) {
$caseValue = $reflectionCase->getValue()->value ?? $reflectionCase->getValue()->name;
if ($property->getValue() === $caseValue) {
return $property->withValue($reflectionCase->getValue());
}
}
return null;
}
}