Completed
Push — master ( e893f6...204e13 )
by Peter
20:17
created

Builder::clearCache()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * This software package is licensed under AGPL, Commercial license.
5
 *
6
 * @package maslosoft/addendum
7
 * @licence AGPL, Commercial
8
 * @copyright Copyright (c) Piotr Masełkowski <[email protected]> (Meta container, further improvements, bugfixes)
9
 * @copyright Copyright (c) Maslosoft (Meta container, further improvements, bugfixes)
10
 * @copyright Copyright (c) Jan Suchal (Original version, builder, parser)
11
 * @link http://maslosoft.com/addendum/ - maslosoft addendum
12
 * @link https://code.google.com/p/addendum/ - original addendum project
13
 */
14
15
namespace Maslosoft\Addendum\Builder;
16
17
use Exception;
18
use Maslosoft\Addendum\Addendum;
19
use Maslosoft\Addendum\Collections\AnnotationsCollection;
20
use Maslosoft\Addendum\Collections\MatcherConfig;
21
use Maslosoft\Addendum\Interfaces\AnnotationInterface;
22
use Maslosoft\Addendum\Matcher\AnnotationsMatcher;
23
use Maslosoft\Addendum\Reflection\ReflectionAnnotatedClass;
24
use Maslosoft\Addendum\Reflection\ReflectionAnnotatedMethod;
25
use Maslosoft\Addendum\Reflection\ReflectionAnnotatedProperty;
26
use Maslosoft\Addendum\Utilities\Blacklister;
27
use Maslosoft\Addendum\Utilities\ClassChecker;
28
use Maslosoft\Addendum\Utilities\NameNormalizer;
29
use Maslosoft\Addendum\Utilities\ReflectionName;
30
use ReflectionClass;
31
use ReflectionMethod;
32
use ReflectionProperty;
33
34
/**
35
 * @Label("Annotations builder")
36
 */
37
class Builder
38
{
39
40
	/**
41
	 * Cached values of parsing
42
	 * @var string[][][]
43
	 */
44
	private static $cache = [];
45
46
	/**
47
	 * Addendum instance
48
	 * @var Addendum
49
	 */
50
	private $addendum = null;
51
52 35
	public function __construct(Addendum $addendum = null)
53
	{
54 35
		$this->addendum = $addendum? : new Addendum();
55 35
	}
56
57
	/**
58
	 * Build annotations collection
59
	 * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty $targetReflection
60
	 * @return AnnotationsCollection
61
	 */
62 35
	public function build($targetReflection)
63
	{
64 35
		$annotations = [];
65 35
		$t = [];
0 ignored issues
show
Unused Code introduced by
$t is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
66
67
68
69
		// Decide where from take traits
70 35
		if ($targetReflection instanceof ReflectionClass)
71 35
		{
72 35
			$t = $targetReflection->getTraits();
73 35
		}
74
		else
75
		{
76 19
			$t = $targetReflection->getDeclaringClass()->getTraits();
77
		}
78
79
		// Get annotations from traits
80 35
		$traitsData = [];
81 35
		foreach ($t as $trait)
82
		{
83 4
			$targetTrait = new ReflectionAnnotatedClass($trait->name, $this->addendum);
84 4
			$annotationsTrait = null;
85
86
			// Try to get annotations from entity, be it method, property or trait itself
87 3
			switch (true)
88
			{
89 3
				case $targetReflection instanceof ReflectionProperty && $targetTrait->hasProperty($targetReflection->name):
90 3
					$annotationsTrait = new ReflectionAnnotatedProperty($targetTrait->name, $targetReflection->name, $this->addendum);
91 2
					break;
92 3
				case $targetReflection instanceof ReflectionMethod && $targetTrait->hasMethod($targetReflection->name):
93
					$annotationsTrait = new ReflectionAnnotatedMethod($targetTrait->name, $targetReflection->name, $this->addendum);
94
					break;
95 3
				case $targetReflection instanceof \ReflectionClass:
96 3
					$annotationsTrait = $targetTrait;
97 3
					break;
98
			}
99
100
			// Does not have property or method
101 3
			if (null === $annotationsTrait)
102 3
			{
103 1
				continue;
104
			}
105
106
			// Data from traits
107 3
			$traitsData = $this->parse($annotationsTrait);
108 35
		}
109
110
		// Data from class
111 35
		$data = $this->parse($targetReflection);
112
113
		// Merge data from traits
114 35
		$data = array_merge($traitsData, $data);
115
116
		// Get annotations from current entity
117 35
		foreach ($data as $class => $parameters)
118 1
		{
119 33
			foreach ($parameters as $params)
120
			{
121 33
				$annotation = $this->instantiateAnnotation($class, $params, $targetReflection);
122 33
				if ($annotation !== false)
123 33
				{
124 33
					$annotations[$class][] = $annotation;
125 33
				}
126 33
			}
127 35
		}
128 35
		return new AnnotationsCollection($annotations);
129
	}
130
131
	/**
132
	 * Create new instance of annotation
133
	 * @param string $class
134
	 * @param mixed[] $parameters
135
	 * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty|bool $targetReflection
136
	 * @return boolean|object
137
	 */
138 33
	public function instantiateAnnotation($class, $parameters, $targetReflection = false)
139
	{
140 33
		$class = ucfirst($class) . "Annotation";
141
142
		// If namespaces are empty assume global namespace
143 33
		$fqn = $this->normalizeFqn('\\', $class);
144 33
		foreach ($this->addendum->namespaces as $ns)
145
		{
146 33
			$fqn = $this->normalizeFqn($ns, $class);
147 33
			if (Blacklister::ignores($fqn))
148 33
			{
149 27
				continue;
150
			}
151
			try
152
			{
153 33
				if (!ClassChecker::exists($fqn))
154 33
				{
155 11
					$this->addendum->getLogger()->debug('Annotation class `{fqn}` not found, ignoring', ['fqn' => $fqn]);
156 11
					Blacklister::ignore($fqn);
157 11
				}
158
				else
159
				{
160
					// Class exists, exit loop
161 33
					break;
162
				}
163
			}
164 11
			catch (Exception $e)
165
			{
166
				// Ignore class autoloading errors
167
			}
168 33
		}
169 33
		if (Blacklister::ignores($fqn))
170 33
		{
171 1
			return false;
172
		}
173
		try
174
		{
175
			// NOTE: @ need to be used here or php might complain
176 33
			if (@!class_exists($fqn))
177 33
			{
178
				$this->addendum->getLogger()->debug('Annotation class `{fqn}` not found, ignoring', ['fqn' => $fqn]);
179
				Blacklister::ignore($fqn);
180
				return false;
181
			}
182
		}
183 33
		catch (Exception $e)
184
		{
185
			// Ignore autoload errors and return false
186
			Blacklister::ignore($fqn);
187
			return false;
188
		}
189 33
		$resolvedClass = Addendum::resolveClassName($fqn);
190 33
		if ((new ReflectionClass($resolvedClass))->implementsInterface(AnnotationInterface::class) || $resolvedClass == AnnotationInterface::class)
191 33
		{
192 33
			return new $resolvedClass($parameters, $targetReflection);
193
		}
194
		return false;
195
	}
196
197
	/**
198
	 * Normalize class name and namespace to proper fully qualified name
199
	 * @param string $ns
200
	 * @param string $class
201
	 * @return string
202
	 */
203 33
	private function normalizeFqn($ns, $class)
204
	{
205 33
		$fqn = "\\$ns\\$class";
206 33
		NameNormalizer::normalize($fqn);
207 33
		return $fqn;
208
	}
209
210
	/**
211
	 * Get doc comment
212
	 * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty $reflection
213
	 * @return mixed[]
214
	 */
215 35
	private function parse($reflection)
216
	{
217 35
		$key = ReflectionName::createName($reflection);
218 35
		if (!isset(self::$cache[$key]))
219 35
		{
220 31
			$parser = new AnnotationsMatcher;
221 31
			$data = [];
222 31
			$parser->setPlugins(new MatcherConfig([
223 31
				'addendum' => $this->addendum,
224
				'reflection' => $reflection
225 31
			]));
226 31
			$parser->matches($this->getDocComment($reflection), $data);
227 31
			self::$cache[$key] = $data;
228 31
		}
229 35
		return self::$cache[$key];
230
	}
231
232
	/**
233
	 * Get doc comment
234
	 * @param ReflectionAnnotatedClass|ReflectionAnnotatedMethod|ReflectionAnnotatedProperty $reflection
235
	 * @return mixed[]
236
	 */
237 31
	protected function getDocComment($reflection)
238
	{
239 31
		return Addendum::getDocComment($reflection);
240
	}
241
242
	/**
243
	 * Clear local parsing cache
244
	 */
245 3
	public static function clearCache()
246
	{
247 3
		self::$cache = [];
248 3
	}
249
250
}
251