Passed
Pull Request — 1.x (#3)
by Kevin
01:17
created

Argument::supports()   B

Complexity

Conditions 11
Paths 7

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 13
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 28
rs 7.3166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Zenstruck\Callback;
4
5
/**
6
 * @author Kevin Bond <[email protected]>
7
 */
8
final class Argument
9
{
10
    public const EXACT = 2;
11
    public const COVARIANCE = 4;
12
    public const CONTRAVARIANCE = 8;
13
14
    private const TYPE_NORMALIZE_MAP = [
15
        'boolean' => 'bool',
16
        'integer' => 'int',
17
        'resource (closed)' => 'resource',
18
    ];
19
20
    /** @var \ReflectionParameter */
21
    private $parameter;
22
23
    /** @var \ReflectionNamedType[] */
24
    private $types = [];
0 ignored issues
show
introduced by
The private property $types is not used, and could be removed.
Loading history...
25
26
    public function __construct(\ReflectionParameter $parameter)
27
    {
28
        $this->parameter = $parameter;
29
    }
30
31
    public function type(): ?string
32
    {
33
        return $this->hasType() ? \implode('|', $this->types()) : null;
34
    }
35
36
    /**
37
     * @return string[]
38
     */
39
    public function types(): array
40
    {
41
        return \array_map(static function(\ReflectionNamedType $type) { return $type->getName(); }, $this->reflectionTypes());
42
    }
43
44
    public function hasType(): bool
45
    {
46
        return !empty($this->types());
47
    }
48
49
    public function isUnionType(): bool
50
    {
51
        return \count($this->types()) > 1;
52
    }
53
54
    /**
55
     * @param string   $type    The type to check if this argument supports
56
     * @param int|null $options {@see EXACT} to only check if exact match
57
     *                          {@see COVARIANCE} to check if exact or, if class, is instanceof argument type
58
     *                          {@see CONTRAVARIANCE} to check if exact or, if class, argument type is instance of class
59
     *                          Bitwise disjunction of above is allowed
60
     */
61
    public function supports(string $type, int $options = self::EXACT|self::COVARIANCE): bool
62
    {
63
        if (!$this->hasType()) {
64
            // no type-hint so any type is supported
65
            return true;
66
        }
67
68
        if ('null' === \mb_strtolower($type) && $this->parameter->allowsNull()) {
69
            return true;
70
        }
71
72
        $type = self::TYPE_NORMALIZE_MAP[$type] ?? $type;
73
74
        foreach ($this->types() as $supportedType) {
75
            if ($options & self::EXACT && $supportedType === $type) {
76
                return true;
77
            }
78
79
            if ($options & self::COVARIANCE && \is_a($type, $supportedType, true)) {
80
                return true;
81
            }
82
83
            if ($options & self::CONTRAVARIANCE && \is_a($supportedType, $type, true)) {
84
                return true;
85
            }
86
        }
87
88
        return false;
89
    }
90
91
    /**
92
     * @param mixed $value
93
     */
94
    public function allows($value): bool
95
    {
96
        return $this->supports(\is_object($value) ? \get_class($value) : \gettype($value));
97
    }
98
99
    /**
100
     * @return \ReflectionNamedType[]
101
     */
102
    private function reflectionTypes(): array
103
    {
104
        if (!$type = $this->parameter->getType()) {
105
            return [];
106
        }
107
108
        if ($type instanceof \ReflectionNamedType) {
109
            return [$type];
110
        }
111
112
        /** @var \ReflectionUnionType $type */
113
        return $type->getTypes();
114
    }
115
}
116