Completed
Push — master ( 3a811f...16f30f )
by Asmir
10:09 queued 10:06
created

TypedPropertiesDriver::shouldTypeHint()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 15
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 ReflectionProperty;
19
20
class TypedPropertiesDriver implements DriverInterface
21
{
22
    /**
23
     * @var DriverInterface
24
     */
25
    protected $delegate;
26
27
    /**
28
     * @var ParserInterface
29
     */
30
    protected $typeParser;
31
32
    /**
33
     * @var string[]
34
     */
35
    private $whiteList;
36
37
    /**
38
     * @param string[] $whiteList
39
     */
40
    public function __construct(DriverInterface $delegate, ?ParserInterface $typeParser = null, array $whiteList = [])
41
    {
42
        $this->delegate = $delegate;
43
        $this->typeParser = $typeParser ?: new Parser();
44
        $this->whiteList = array_merge($whiteList, $this->getDefaultWhiteList());
45
    }
46
47
    private function getDefaultWhiteList(): array
48
    {
49
        return [
50
            'int',
51
            'float',
52
            'bool',
53
            'boolean',
54
            'string',
55
            'double',
56
            'iterable',
57
            'resource',
58
        ];
59
    }
60
61
    public function loadMetadataForClass(ReflectionClass $class): ?ClassMetadata
62
    {
63
        /** @var SerializerClassMetadata $classMetadata */
64
        $classMetadata = $this->delegate->loadMetadataForClass($class);
65
66
        if (null === $classMetadata) {
67
            return null;
68
        }
69
        // We base our scan on the internal driver's property list so that we
70
        // respect any internal white/blacklisting like in the AnnotationDriver
71
        foreach ($classMetadata->propertyMetadata as $key => $propertyMetadata) {
72
            /** @var $propertyMetadata PropertyMetadata */
73
74
            // If the inner driver provides a type, don't guess anymore.
75
            if ($propertyMetadata->type || $this->isVirtualProperty($propertyMetadata)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $propertyMetadata->type of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
76
                continue;
77
            }
78
79
            try {
80
                $propertyReflection = $this->getReflection($propertyMetadata);
81
                if ($this->shouldTypeHint($propertyReflection)) {
82
                    $propertyMetadata->setType($this->typeParser->parse($propertyReflection->getType()->getName()));
0 ignored issues
show
Bug introduced by
The method getType() does not exist on ReflectionProperty. ( Ignorable by Annotation )

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

82
                    $propertyMetadata->setType($this->typeParser->parse($propertyReflection->/** @scrutinizer ignore-call */ getType()->getName()));

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
83
                }
84
            } catch (ReflectionException $e) {
85
                continue;
86
            }
87
        }
88
89
        return $classMetadata;
90
    }
91
92
    private function shouldTypeHint(ReflectionProperty $propertyReflection): bool
93
    {
94
        if (null === $propertyReflection->getType()) {
95
            return false;
96
        }
97
98
        if (in_array($propertyReflection->getType()->getName(), $this->whiteList, true)) {
99
            return true;
100
        }
101
102
        if (class_exists($propertyReflection->getType()->getName())) {
103
            return true;
104
        }
105
106
        return false;
107
    }
108
109
    private function getReflection(PropertyMetadata $propertyMetadata): ReflectionProperty
110
    {
111
        return new ReflectionProperty($propertyMetadata->class, $propertyMetadata->name);
112
    }
113
114
    private function isVirtualProperty(PropertyMetadata $propertyMetadata): bool
115
    {
116
        return $propertyMetadata instanceof VirtualPropertyMetadata
117
            || $propertyMetadata instanceof StaticPropertyMetadata
118
            || $propertyMetadata instanceof ExpressionPropertyMetadata;
119
    }
120
}
121