Completed
Push — master ( 8507e2...2d694e )
by Lars
01:39
created

TypeCheckPhpDoc   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 144
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 4

Test Coverage

Coverage 79.31%

Importance

Changes 0
Metric Value
dl 0
loc 144
ccs 46
cts 58
cp 0.7931
rs 10
c 0
b 0
f 0
wmc 24
lcom 2
cbo 4

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A fromPhpDocumentorProperty() 0 29 5
C parseDocTypeObject() 0 71 17
A throwException() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arrayy\TypeCheck;
6
7
/**
8
 * inspired by https://github.com/spatie/value-object
9
 *
10
 * @internal
11
 */
12
final class TypeCheckPhpDoc extends AbstractTypeCheck implements TypeCheckInterface
13
{
14
    /**
15
     * @var bool
16
     */
17
    private $hasTypeDeclaration = false;
18
19
    /**
20
     * @var string
21
     */
22
    private $property_name;
23
24
    /**
25
     * @param string $reflectionPropertyName
26
     */
27 2
    public function __construct($reflectionPropertyName)
28
    {
29 2
        $this->property_name = $reflectionPropertyName;
30 2
    }
31
32
    /**
33
     * @param \phpDocumentor\Reflection\DocBlock\Tags\Property $phpDocumentorReflectionProperty
34
     *
35
     * @return static
36
     */
37 2
    public static function fromPhpDocumentorProperty(\phpDocumentor\Reflection\DocBlock\Tags\Property $phpDocumentorReflectionProperty): self
38
    {
39 2
        $tmpProperty = $phpDocumentorReflectionProperty->getVariableName();
40 2
        $tmpObject = new \stdClass();
41 2
        $tmpObject->{$tmpProperty} = null;
42
43 2
        $tmpReflection = new self((new \ReflectionProperty($tmpObject, $tmpProperty))->getName());
44
45 2
        $type = $phpDocumentorReflectionProperty->getType();
46
47 2
        if ($type) {
48 2
            $tmpReflection->hasTypeDeclaration = true;
49
50 2
            $docTypes = self::parseDocTypeObject($type);
51 2
            if (\is_array($docTypes) === true) {
52 2
                foreach ($docTypes as $docType) {
53 2
                    $tmpReflection->types[] = $docType;
54
                }
55
            } else {
56 2
                $tmpReflection->types[] = $docTypes;
57
            }
58
59 2
            if (\in_array('null', $tmpReflection->types, true)) {
60 2
                $tmpReflection->isNullable = true;
61
            }
62
        }
63
64 2
        return $tmpReflection;
65
    }
66
67
    /**
68
     * @param \phpDocumentor\Reflection\Type $type
69
     *
70
     * @return string|string[]
71
     */
72 2
    public static function parseDocTypeObject($type)
73
    {
74 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Object_) {
75 1
            $tmpObject = (string) $type->getFqsen();
76 1
            if ($tmpObject) {
77 1
                return $tmpObject;
78
            }
79
80
            return 'object';
81
        }
82
83 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Compound) {
84 2
            $types = [];
85 2
            foreach ($type as $subType) {
86 2
                $types[] = self::parseDocTypeObject($subType);
87
            }
88
89 2
            return $types;
90
        }
91
92 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Array_) {
93 1
            $valueTypeTmp = $type->getValueType() . '';
94 1
            if ($valueTypeTmp !== 'mixed') {
95 1
                return $valueTypeTmp . '[]';
96
            }
97
98
            return 'array';
99
        }
100
101 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Null_) {
102 2
            return 'null';
103
        }
104
105 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Mixed_) {
106
            return 'mixed';
107
        }
108
109 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Scalar) {
110
            return 'string|int|float|bool';
111
        }
112
113 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Boolean) {
114
            return 'bool';
115
        }
116
117 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Callable_) {
118
            return 'callable';
119
        }
120
121 2
        if ($type instanceof \phpDocumentor\Reflection\Types\Float_) {
122
            return 'float';
123
        }
124
125 2
        if ($type instanceof \phpDocumentor\Reflection\Types\String_) {
126 2
            return 'string';
127
        }
128
129 1
        if ($type instanceof \phpDocumentor\Reflection\Types\Integer) {
130 1
            return 'int';
131
        }
132
133
        if ($type instanceof \phpDocumentor\Reflection\Types\Void_) {
134
            return 'void';
135
        }
136
137
        if ($type instanceof \phpDocumentor\Reflection\Types\Resource_) {
138
            return 'resource';
139
        }
140
141
        return $type . '';
142
    }
143
144
    /**
145
     * @param string $expectedTypes
146
     * @param mixed  $value
147
     * @param string $type
148
     *
149
     * @return \TypeError
150
     */
151 4
    public function throwException($expectedTypes, $value, $type): \Throwable
152
    {
153 4
        throw new \TypeError("Invalid type: expected \"{$this->property_name}\" to be of type {{$expectedTypes}}, instead got value \"" . $this->valueToString($value) . '" (' . \print_r($value, true) . ") with type {{$type}}.");
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with "Invalid type: expected ...) with type {{$type}}.".

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
154
    }
155
}
156