Passed
Push — master ( 0efa8f...09a34a )
by Arthur
12:45
created

VarTypeResolver::checkTypeExistence()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 12
rs 9.9332
c 0
b 0
f 0
cc 4
nc 4
nop 1
1
<?php
2
3
4
namespace Larapie\DataTransferObject\Resolvers;
5
6
7
use Larapie\DataTransferObject\Exceptions\TypeDoesNotExistException;
8
use phpDocumentor\Reflection\TypeResolver;
9
use phpDocumentor\Reflection\Types\Compound;
10
use ReflectionProperty;
11
12
class VarTypeResolver
13
{
14
    protected $reflection;
15
16
    /** @var string[] List of recognized keywords and unto which Value Object they map */
17
    private static $typeKeywords = array(
18
        'string',
19
        'int',
20
        'integer',
21
        'bool',
22
        'boolean',
23
        'float',
24
        'double',
25
        'object',
26
        'mixed',
27
        'array',
28
        'resource',
29
        'void',
30
        'null',
31
        'scalar',
32
        'callback',
33
        'callable',
34
        'false',
35
        'true',
36
        'self',
37
        '$this',
38
        'static',
39
        'parent',
40
        'iterable'
41
    );
42
43
    /**
44
     * VarTypeResolver constructor.
45
     * @param $reflection
46
     */
47
    public function __construct(ReflectionProperty $reflection)
48
    {
49
        $this->reflection = $reflection;
50
    }
51
52
    public function resolve(string $varDocComment) :array
53
    {
54
        if ($this->reflection->getDeclaringClass()->isAnonymous()) {
55
            $filename = $this->reflection->getDeclaringClass()->getFileName();
56
57
            $contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
58
            $context = $contextFactory->createForNamespace($this->getNamespaceFromFilename($filename), file_get_contents($filename));
59
        } else {
60
            $contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
61
            $context = $contextFactory->createFromReflector($this->reflection);
62
        }
63
64
        $resolver = new TypeResolver();
65
        $resolvedTypes = $resolver->resolve($varDocComment, $context);
66
        $types = [];
67
        if ($resolvedTypes instanceof Compound) {
68
            foreach ($resolvedTypes as $type) {
69
                $types[] = $type->__toString();
70
            }
71
        } else {
72
            $types = [$resolvedTypes->__toString()];
73
        }
74
        $this->checkTypeExistence($types);
75
        return $types;
76
    }
77
78
    protected function getClassFromFilename($fileName) :string
79
    {
80
        $directoriesAndFilename = explode('/', $fileName);
81
        $fileName = array_pop($directoriesAndFilename);
82
        $nameAndExtension = explode('.', $fileName);
83
        return array_shift($nameAndExtension);
84
    }
85
86
    protected function getNamespaceFromFilename($filename) :string
87
    {
88
        $lines = file($filename);
89
        $namespace = preg_grep('/^namespace /', $lines);
0 ignored issues
show
Bug introduced by
It seems like $lines can also be of type false; however, parameter $input of preg_grep() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

89
        $namespace = preg_grep('/^namespace /', /** @scrutinizer ignore-type */ $lines);
Loading history...
90
        $namespaceLine = array_shift($namespace);
91
        $match = array();
92
        preg_match('/^namespace (.*);$/', $namespaceLine, $match);
93
        return $fullNamespace = array_pop($match);
0 ignored issues
show
Unused Code introduced by
The assignment to $fullNamespace is dead and can be removed.
Loading history...
94
    }
95
96
    protected function getFqnFromFileName($fileName) :string
97
    {
98
        return $this->getNamespaceFromFilename($fileName) . '\\' . $this->getClassFromFilename($fileName);
99
    }
100
101
    protected function checkTypeExistence(array $types)
102
    {
103
        foreach ($types as $type) {
104
            $type = str_replace("[]", "", $type);
105
            if (!in_array($type, self::$typeKeywords)) {
106
                if (!$this->classExists($type))
107
                    throw new TypeDoesNotExistException(sprintf(
108
                        'The @var annotation on %s::%s contains a non existent class "%s". '
109
                        . 'Did you maybe forget to add a "use" statement for this annotation?',
110
                        $this->reflection->getDeclaringClass()->getName(),
111
                        $this->reflection->getName(),
112
                        $type
113
                    ));
114
            }
115
116
        }
117
    }
118
119
    /**
120
     * @param string $class
121
     * @return bool
122
     */
123
    private function classExists($class)
124
    {
125
        return class_exists($class) || interface_exists($class);
126
    }
127
128
}