Tokenizer::readToken()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 2
crap 3
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Leaditin\Annotations;
6
7
/**
8
 * Class Tokenizer
9
 *
10
 * @package Leaditin\Annotations
11
 * @author Igor Vuckovic
12
 */
13
class Tokenizer
14
{
15
    /** @var \ReflectionClass */
16
    protected $reflectionClass;
17
18
    /** @var array */
19
    protected $tokens;
20
21
    /** @var array */
22
    protected $structure;
23
24
    /**
25
     * @param \ReflectionClass $reflectionClass
26
     */
27 12
    public function __construct(\ReflectionClass $reflectionClass)
28
    {
29 12
        $this->reflectionClass = $reflectionClass;
30 12
        $this->tokens = token_get_all(file_get_contents($this->reflectionClass->getFileName()));
31 12
        $this->structure = [];
32 12
    }
33
34
    /**
35
     * @param string $name
36
     * @return string
37
     */
38 11
    public function resolveFullyQualifiedClassName(string $name) : string
39
    {
40 11
        $resolvedFromUse = $this->resolveFullyQualifiedClassNameFromUse($name);
41 11
        if (false !== $resolvedFromUse) {
42 9
            return $resolvedFromUse;
43
        }
44
45 9
        $resolvedFromNamespace = $this->resolveFullyQualifiedClassNameFromNamespace($name);
46 9
        if (false !== $resolvedFromNamespace) {
47 8
            return $resolvedFromNamespace;
48
        }
49
50 8
        return $this->trimClassName($name);
51
    }
52
53
    /**
54
     * @param string $name
55
     * @return string
56
     */
57 8
    public function resolveVariableName(string $name) : string
58
    {
59 8
        if ($this->isScalar($name)) {
60 1
            return $name;
61
        }
62
63 7
        if ($this->isSelf($name)) {
64 7
            return $this->trimClassName($this->reflectionClass->name);
65
        }
66
67 7
        return $name;
68
    }
69
70
    /**
71
     * @return array
72
     */
73 11
    protected function readUse() : array
74
    {
75 11
        if (!array_key_exists('use', $this->structure)) {
76 11
            $numberOfTokens = count($this->tokens);
77 11
            $this->structure['use'] = [];
78
79 11
            for ($i = 0; $i < $numberOfTokens; ++$i) {
80 11
                $this->readToken($i, $numberOfTokens);
81
            }
82
        }
83
84 11
        return $this->structure['use'];
85
    }
86
87
    /**
88
     * @param int $tokenPosition
89
     * @param int $numberOfTokens
90
     */
91 11
    protected function readToken(int $tokenPosition, int $numberOfTokens)
92
    {
93 11
        if ($this->tokens[$tokenPosition][0] !== T_USE) {
94 11
            return;
95
        }
96
97 11
        $class = '';
98 11
        for ($j = $tokenPosition + 1; $j < $numberOfTokens; ++$j) {
99 11
            $this->useToken($j, $class);
100
        }
101 11
    }
102
103
    /**
104
     * @param int $tokenPosition
105
     * @param string $class
106
     */
107 11
    protected function useToken(int $tokenPosition, string &$class)
108
    {
109 11
        if ($this->tokens[$tokenPosition][0] === T_STRING) {
110 11
            $class .= '\\' . $this->tokens[$tokenPosition][1];
111 11
        } elseif ($this->tokens[$tokenPosition] === ';') {
112 11
            $this->useClass($class);
113
        }
114 11
    }
115
116
    /**
117
     * @param string $class
118
     */
119 11
    protected function useClass(string &$class)
120
    {
121 11
        $fullyQualifiedClassName = $this->deriveFullyQualifiedClassName($class);
122 11
        $this->structure['use'][$this->trimClassName($fullyQualifiedClassName)] = $this->trimClassName($class);
123 11
        $class = '';
124 11
    }
125
126
    /**
127
     * @param string $className
128
     * @return string
129
     */
130 11
    protected function deriveFullyQualifiedClassName(string $className) : string
131
    {
132 11
        while (!class_exists($className)) {
133 11
            $length = strrpos($className, '\\');
134 11
            if ($length === false) {
135 11
                return $className;
136
            }
137
138 11
            $className = substr($className, 0, $length);
139
        }
140
141 11
        return $className;
142
    }
143
144
    /**
145
     * @param string $name
146
     * @return bool
147
     */
148 8
    protected function isScalar(string $name) : bool
149
    {
150
        $scalars = [
151 8
            'int', 'integer',
152
            'bool', 'boolean',
153
            'string', 'array',
154
            'float', 'double', 'decimal',
155
        ];
156
157 8
        return in_array($name, $scalars, false);
158
    }
159
160
    /**
161
     * @param string $name
162
     * @return bool
163
     */
164 7
    protected function isSelf(string $name) : bool
165
    {
166
        $selves = [
167 7
            'self',
168
            'static',
169
            '$this'
170
        ];
171
172 7
        return in_array($name, $selves, false);
173
    }
174
175
    /**
176
     * @param string $class
177
     * @return bool|int|string
178
     */
179 11
    protected function resolveFullyQualifiedClassNameFromUse(string $class)
180
    {
181 11
        $length = strlen($class);
182
183 11
        foreach ($this->readUse() as $fullyQualifiedClassName => $useClassName) {
184 11
            if (substr($useClassName, -$length) === $class) {
185 11
                return $fullyQualifiedClassName;
186
            }
187
        }
188
189 9
        return false;
190
    }
191
192
    /**
193
     * @param string $class
194
     * @return bool|string
195
     */
196 9
    protected function resolveFullyQualifiedClassNameFromNamespace(string $class)
197
    {
198 9
        if (0 !== strpos($class, '\\')) {
199 9
            $fullyQualifiedClassName = "{$this->reflectionClass->getNamespaceName()}\\$class";
200 9
            if (class_exists($fullyQualifiedClassName)) {
201 8
                return $this->trimClassName($fullyQualifiedClassName);
202
            }
203
        }
204
205 8
        return false;
206
    }
207
208
    /**
209
     * @param string $name
210
     * @return string
211
     */
212 11
    protected function trimClassName(string $name) : string
213
    {
214 11
        return ltrim($name, '\\');
215
    }
216
}
217