ReflectionReader   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 193
Duplicated Lines 14.51 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 5
dl 28
loc 193
ccs 75
cts 75
cp 1
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A read() 0 15 2
A readAnnotationsFromClass() 0 6 1
A readAnnotationsFromMethods() 14 14 3
A readAnnotationsFromProperties() 14 14 3
A parseComment() 0 15 3
A parseLine() 0 9 2
A parseArgumentsFromLine() 0 21 4
A exportArgumentsFromLine() 0 8 2
A filterName() 0 6 1
A filterValue() 0 7 1
A filterArguments() 0 17 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Leaditin\Annotations\Reader;
6
7
use Leaditin\Annotations\Annotation;
8
use Leaditin\Annotations\Collection;
9
use Leaditin\Annotations\Exception\ReaderException;
10
use Leaditin\Annotations\Reflection;
11
use Leaditin\Annotations\Tokenizer;
12
13
/**
14
 * Class ReflectionReader
15
 *
16
 * @package Leaditin\Annotations
17
 * @author Igor Vuckovic <[email protected]>
18
 */
19
class ReflectionReader implements ReaderInterface
20
{
21
    /** @var \ReflectionClass */
22
    protected $reflectionClass;
23
24
    /** @var Tokenizer */
25
    protected $tokenizer;
26
27
    /**
28
     * @param string $class
29
     * @return Reflection
30
     * @throws ReaderException
31
     */
32 8
    public function read(string $class) : Reflection
33
    {
34
        try {
35 8
            $this->reflectionClass = new \ReflectionClass($class);
36 7
            $this->tokenizer = new Tokenizer($this->reflectionClass);
37
38 7
            return new Reflection(
39 7
                $this->readAnnotationsFromClass(),
40 7
                $this->readAnnotationsFromMethods(),
41 7
                $this->readAnnotationsFromProperties()
42
            );
43 1
        } catch (\ReflectionException $ex) {
44 1
            throw ReaderException::unableToReadClass($class, $ex);
45
        }
46
    }
47
48
    /**
49
     * @return Collection
50
     */
51 7
    protected function readAnnotationsFromClass() : Collection
52
    {
53 7
        $collection = $this->parseComment($this->reflectionClass->getDocComment());
54
55 7
        return new Collection(...$collection);
56
    }
57
58
    /**
59
     * @return array|Collection[]
60
     */
61 7 View Code Duplication
    protected function readAnnotationsFromMethods() : array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
62
    {
63 7
        $annotationsFromMethods = [];
64 7
        foreach ($this->reflectionClass->getMethods() as $method) {
65 7
            $methodCollection = $this->parseComment($method->getDocComment());
66 7
            if (empty($methodCollection)) {
67 7
                continue;
68
            }
69
70 7
            $annotationsFromMethods[$method->name] = new Collection(...$methodCollection);
71
        }
72
73 7
        return $annotationsFromMethods;
74
    }
75
76
    /**
77
     * @return array|Collection[]
78
     */
79 7 View Code Duplication
    protected function readAnnotationsFromProperties() : array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
    {
81 7
        $annotationsFromProperties = [];
82 7
        foreach ($this->reflectionClass->getProperties() as $property) {
83 7
            $propertyCollection = $this->parseComment($property->getDocComment());
84 7
            if (empty($propertyCollection)) {
85 7
                continue;
86
            }
87
88 7
            $annotationsFromProperties[$property->name] = new Collection(...$propertyCollection);
89
        }
90
91 7
        return $annotationsFromProperties;
92
    }
93
94
    /**
95
     * @param string|bool $comment
96
     * @return array|Annotation[]
97
     */
98 7
    protected function parseComment($comment = false) : array
99
    {
100 7
        $annotations = [];
101
102 7
        if ($comment === false) {
103 7
            return $annotations;
104
        }
105
106 7
        preg_match_all('/@([^*]+)/', $comment, $matches);
107 7
        foreach ($matches[1] as $match) {
108 7
            $annotations[] = $this->parseLine($match);
109
        }
110
111 7
        return $annotations;
112
    }
113
114
    /**
115
     * @param string $line
116
     * @return bool|Annotation
117
     */
118 7
    protected function parseLine(string $line)
119
    {
120 7
        preg_match('/^(\w+)(\(.+\)|\s+.+\n*|\}?)/', $line, $matches);
121 7
        $name = $matches[1];
122 7
        $arguments = $this->parseArgumentsFromLine($matches[2] ?: '');
123 7
        $this->filterArguments($name, $arguments);
124
125 7
        return new Annotation($name, $arguments);
126
    }
127
128
    /**
129
     * @param string $line
130
     * @return array
131
     */
132 7
    protected function parseArgumentsFromLine(string $line) : array
133
    {
134 7
        $line = preg_replace('/^\((.*?)\)$/', '$1', trim($line));
135 7
        $arguments = [];
136
137 7
        if ($line === '') {
138 7
            return $arguments;
139
        }
140
141 7
        $data = $this->exportArgumentsFromLine($line);
142 7
        foreach ($data as $index => $property) {
143 7
            $properties = explode('=', $property, 2);
144 7
            if (count($properties) === 1) {
145 7
                $arguments[$index] = $this->filterValue($property);
146
            } else {
147 7
                $arguments[$this->filterName($properties[0])] = $this->filterValue($properties[1]);
148
            }
149
        }
150
151 7
        return $arguments;
152
    }
153
154
    /**
155
     * @param string $line
156
     * @return array
157
     */
158 7
    protected function exportArgumentsFromLine(string $line) : array
159
    {
160 7
        if (false !== strpos($line, '|')) {
161 7
            return explode('|', $line);
162
        }
163
164 7
        return explode(',', $line);
165
    }
166
167
    /**
168
     * @param string $name
169
     * @return string
170
     */
171
    protected function filterName(string $name) : string
172
    {
173 7
        return preg_replace_callback('/^"([^"]*)"$|^\'([^\']*)\'$/', function ($matches) {
174 7
            return $matches[2] ?? $matches[1];
175 7
        }, trim($name));
176
    }
177
178
    /**
179
     * @param string $value
180
     * @return int|string|bool|float|array
181
     */
182 7
    protected function filterValue(string $value)
183
    {
184 7
        $value = $this->filterName($value);
185 7
        $data = json_decode(str_replace('\'', '"', $value), true);
186
187 7
        return $data ?? $value;
188
    }
189
190
    /**
191
     * @param string $name
192
     * @param array $arguments
193
     */
194 7
    protected function filterArguments(string $name, array &$arguments)
195
    {
196
        $allowed = [
197 7
            'var', 'param', 'property', 'method', 'return', 'throws',
198
        ];
199
200 7
        if (!in_array($name, $allowed, false)) {
201 7
            return;
202
        }
203
204 7
        array_walk($arguments, function (&$val) {
205 7
            $val = preg_replace('/(\$\w+)$/', '', $val);
206 7
            $val = trim($val);
207 7
            $val = $this->tokenizer->resolveVariableName($val);
208 7
            $val = $this->tokenizer->resolveFullyQualifiedClassName($val);
209 7
        });
210
    }
211
}
212