Passed
Pull Request — master (#1487)
by Martin Poirier
03:02
created

TypedPropertiesDriver::getReflection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer\Metadata\Driver;
6
7
use JMS\Serializer\Metadata\ClassMetadata as SerializerClassMetadata;
8
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
9
use JMS\Serializer\Metadata\PropertyMetadata;
10
use JMS\Serializer\Metadata\StaticPropertyMetadata;
11
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
12
use JMS\Serializer\Type\Parser;
13
use JMS\Serializer\Type\ParserInterface;
14
use Metadata\ClassMetadata;
15
use Metadata\Driver\DriverInterface;
16
use ReflectionClass;
17
use ReflectionException;
18
use ReflectionMethod;
19
use ReflectionNamedType;
20
use ReflectionProperty;
21
use ReflectionType;
22
23
class TypedPropertiesDriver implements DriverInterface
24
{
25
    /**
26
     * @var DriverInterface
27
     */
28
    protected $delegate;
29
30
    /**
31
     * @var ParserInterface
32
     */
33
    protected $typeParser;
34
35
    /**
36
     * @var string[]
37
     */
38
    private $allowList;
39
40
    /**
41
     * @param string[] $allowList
42
     */
43
    public function __construct(DriverInterface $delegate, ?ParserInterface $typeParser = null, array $allowList = [])
44
    {
45
        $this->delegate = $delegate;
46
        $this->typeParser = $typeParser ?: new Parser();
47
        $this->allowList = array_merge($allowList, $this->getDefaultWhiteList());
48
    }
49
50
    private function getDefaultWhiteList(): array
51
    {
52
        return [
53
            'int',
54
            'float',
55
            'bool',
56
            'boolean',
57
            'string',
58
            'double',
59
            'iterable',
60
            'resource',
61
        ];
62
    }
63
64
    /**
65
     * @return SerializerClassMetadata|null
66
     */
67
    public function loadMetadataForClass(ReflectionClass $class): ?ClassMetadata
68
    {
69
        $classMetadata = $this->delegate->loadMetadataForClass($class);
70
        \assert($classMetadata instanceof SerializerClassMetadata);
71
72
        if (PHP_VERSION_ID <= 70400) {
73
            return $classMetadata;
74
        }
75
76
        // We base our scan on the internal driver's property list so that we
77
        // respect any internal allow/blocklist like in the AnnotationDriver
78
        foreach ($classMetadata->propertyMetadata as $propertyMetadata) {
79
            // If the inner driver provides a type, don't guess anymore.
80
            if ($propertyMetadata->type) {
81
                continue;
82
            }
83
84
            try {
85
                $reflectionType = $this->getReflectionType($propertyMetadata);
86
87
                if ($this->shouldTypeHint($reflectionType)) {
88
                    $type = $reflectionType->getName();
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

88
                    /** @scrutinizer ignore-call */ 
89
                    $type = $reflectionType->getName();
Loading history...
89
90
                    $propertyMetadata->setType($this->typeParser->parse($type));
91
                }
92
            } catch (ReflectionException $e) {
93
                continue;
94
            }
95
        }
96
97
        return $classMetadata;
98
    }
99
100
    private function getReflectionType(PropertyMetadata $propertyMetadata): ?ReflectionType
101
    {
102
        if ($this->isNotSupportedVirtualProperty($propertyMetadata)) {
103
            return null;
104
        }
105
106
        if ($propertyMetadata instanceof VirtualPropertyMetadata) {
107
            return (new ReflectionMethod($propertyMetadata->class, $propertyMetadata->getter))
108
                ->getReturnType();
109
        }
110
111
        return (new ReflectionProperty($propertyMetadata->class, $propertyMetadata->name))
112
            ->getType();
113
    }
114
115
    private function isNotSupportedVirtualProperty(PropertyMetadata $propertyMetadata): bool
116
    {
117
        return $propertyMetadata instanceof StaticPropertyMetadata
118
            || $propertyMetadata instanceof ExpressionPropertyMetadata;
119
    }
120
121
    /**
122
     * @phpstan-assert-if-true \ReflectionNamedType $reflectionType
123
     */
124
    private function shouldTypeHint(?ReflectionType $reflectionType): bool
125
    {
126
        if (!$reflectionType instanceof ReflectionNamedType) {
127
            return false;
128
        }
129
130
        if (in_array($reflectionType->getName(), $this->allowList, true)) {
131
            return true;
132
        }
133
134
        return class_exists($reflectionType->getName())
135
            || interface_exists($reflectionType->getName());
136
    }
137
}
138