Completed
Push — master ( 250b0d...235888 )
by Peter
26:53 queued 21:53
created

Addendum::setLogger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 0
cts 3
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 2
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;
16
17
use Maslosoft\Addendum\Annotations\TargetAnnotation;
18
use Maslosoft\Addendum\Builder\Builder;
19
use Maslosoft\Addendum\Builder\DocComment;
20
use Maslosoft\Addendum\Cache\MetaCache;
21
use Maslosoft\Addendum\Collections\AddendumPlugins;
22
use Maslosoft\Addendum\Collections\Meta;
23
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
24
use Maslosoft\Addendum\Interfaces\AnnotationInterface;
25
use Maslosoft\Addendum\Matcher\AnnotationsMatcher;
26
use Maslosoft\Addendum\Matcher\ClassLiteralMatcher;
27
use Maslosoft\Addendum\Matcher\StaticConstantMatcher;
28
use Maslosoft\Addendum\Plugins\Matcher\DefencerDecorator;
29
use Maslosoft\Addendum\Plugins\Matcher\UseResolverDecorator;
30
use Maslosoft\Addendum\Reflection\ReflectionAnnotatedClass;
31
use Maslosoft\Addendum\Reflection\ReflectionAnnotatedMethod;
32
use Maslosoft\Addendum\Reflection\ReflectionAnnotatedProperty;
33
use Maslosoft\Addendum\Signals\NamespacesSignal;
34
use Maslosoft\Addendum\Utilities\Blacklister;
35
use Maslosoft\Addendum\Utilities\NameNormalizer;
36
use Maslosoft\EmbeDi\EmbeDi;
37
use Maslosoft\Signals\Signal;
38
use Psr\Log\LoggerAwareInterface;
39
use Psr\Log\LoggerInterface;
40
use Psr\Log\NullLogger;
41
use ReflectionClass;
42
use ReflectionException;
43
use Reflector;
44
45
class Addendum implements LoggerAwareInterface
46
{
47
48
	const DefaultInstanceId = 'addendum';
49
50
	/**
51
	 * Runtime path
52
	 * @var string
53
	 */
54
	public $runtimePath = 'runtime';
55
56
	/**
57
	 * Whether to check modification time of file to invalidate cache.
58
	 * Set this to true for development, and false for production.
59
	 * @var bool
60
	 */
61
	public $checkMTime = false;
62
63
	/**
64
	 * Namespaces to check for annotations.
65
	 * By default global and addendum namespace is included.
66
	 * @var string[]
67
	 */
68
	public $namespaces = [
69
		'\\',
70
		TargetAnnotation::Ns
71
	];
72
73
	/**
74
	 * @var bool[]
75
	 * @internal Do not use, this for performance only
76
	 */
77
	public $nameKeys = [];
78
79
	/**
80
	 * Translatable annotations
81
	 * TODO This should be moved to `maslosoft/addendum-i18n-extractor`
82
	 * @var string[]
83
	 */
84
	public $i18nAnnotations = [
85
		'Label',
86
		'Description'
87
	];
88
89
	/**
90
	 * Plugins collection
91
	 * @var AddendumPlugins|mixed[]
92
	 */
93
	public $plugins = [
94
		'matcher' => [
95
			ClassLiteralMatcher::class => [
96
				UseResolverDecorator::class
97
			],
98
			StaticConstantMatcher::class => [
99
				UseResolverDecorator::class
100
			],
101
			AnnotationsMatcher::class => [
102
				DefencerDecorator::class
103
			]
104
		]
105
	];
106
107
	/**
108
	 * Current instance id
109
	 * @var string
110
	 */
111
	protected $instanceId = self::DefaultInstanceId;
112
113
	/**
114
	 * DI
115
	 * @var EmbeDi
116
	 */
117
	protected $di = null;
118
119
	/**
120
	 * Logger
121
	 * @var LoggerInterface
122
	 */
123
	private $loggerInstance;
124
125
	/**
126
	 * Cache for resolved annotations class names.
127
	 * Key is shor annotation name.
128
	 * @var string[]
129
	 */
130
	private static $classNames = [];
131
132
	/**
133
	 * This holds information about all declared classes implementing AnnotatedInterface.
134
	 * @see AnnotatedInterface
135
	 * @var string[]
136
	 */
137
	private static $annotations = [];
138
139
	/**
140
	 * Version holder
141
	 * @var string
142
	 */
143
	private static $versionNumber = null;
144
145
	/**
146
	 * Addendum instances
147
	 * @var Addendum[]
148
	 */
149
	private static $addendums = [];
150
151
	/**
152
	 *
153
	 * @param string $instanceId
154
	 */
155 58
	public function __construct($instanceId = self::DefaultInstanceId)
156
	{
157 58
		$this->instanceId = $instanceId;
158 58
		$this->plugins = new AddendumPlugins($this->plugins);
0 ignored issues
show
Bug introduced by
It seems like $this->plugins can also be of type object<Maslosoft\Addendu...ctions\AddendumPlugins>; however, Maslosoft\Gazebo\ConfigContainer::__construct() does only seem to accept array<integer,*>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
159
160 58
		$this->di = new EmbeDi($instanceId);
161 58
		$this->di->configure($this);
162 58
	}
163
164
	/**
165
	 * Get flyweight addendum instance of `Addendum`.
166
	 * Only one instance will be created for each `$instanceId`
167
	 *
168
	 * @param string $instanceId
169
	 * @return Addendum
170
	 */
171 54
	public static function fly($instanceId = self::DefaultInstanceId)
172
	{
173 54
		if (empty(self::$addendums[$instanceId]))
174
		{
175
			self::$addendums[$instanceId] = (new Addendum($instanceId))->init();
176
		}
177 54
		return self::$addendums[$instanceId];
178
	}
179
180 51
	public function getInstanceId()
181
	{
182 51
		return $this->instanceId;
183
	}
184
185
	/**
186
	 * Get current addendum version.
187
	 *
188
	 * @return string
189
	 */
190
	public function getVersion()
191
	{
192
		if (null === self::$versionNumber)
193
		{
194
			self::$versionNumber = require __DIR__ . '/version.php';
195
		}
196
		return self::$versionNumber;
197
	}
198
199
	/**
200
	 * Initialize addendum and store configuration.
201
	 * This should be called upon first intstance creation.
202
	 *
203
	 * @return Addendum
204
	 */
205
	public function init()
206
	{
207
		if (!$this->di->isStored($this))
208
		{
209
			(new Signal())->emit(new NamespacesSignal($this));
210
		}
211
		$this->di->store($this);
212
		return $this;
213
	}
214
215
	/**
216
	 * Check if class could have annotations.
217
	 * **NOTE:**
218
	 * > This does not check check if class have annotations. It only checks if it implements `AnnotatedInterface`
219
	 * @param string|object $class
220
	 * @return bool
221
	 */
222 32
	public function hasAnnotations($class)
223
	{
224 32
		return (new ReflectionClass($class))->implementsInterface(AnnotatedInterface::class);
225
	}
226
227
	/**
228
	 * Use $class name or object to annotate class
229
	 * @param string|object $class
230
	 * @return ReflectionAnnotatedMethod|ReflectionAnnotatedProperty|ReflectionAnnotatedClass
231
	 */
232 32
	public function annotate($class)
233
	{
234 32
		$className = is_object($class) ? get_class($class) : $class;
235 32
		if (!$this->hasAnnotations($class))
236
		{
237
			throw new ReflectionException(sprintf('To annotate class "%s", it must implement interface %s', $className, AnnotatedInterface::class));
238
		}
239 32
		$reflection = new ReflectionAnnotatedClass($class, $this);
240 27
		return $reflection;
241
	}
242
243
	/**
244
	 * Set logger
245
	 *
246
	 * @param LoggerInterface $logger
247
	 * @return Addendum
248
	 */
249
	public function setLogger(LoggerInterface $logger)
250
	{
251
		$this->loggerInstance = $logger;
252
		return $this;
253
	}
254
255
	/**
256
	 * Get logger
257
	 *
258
	 * @return LoggerInterface logger
259
	 */
260 46
	public function getLogger()
261
	{
262 46
		if (null === $this->loggerInstance)
263
		{
264 33
			$this->loggerInstance = new NullLogger;
265
		}
266 46
		return $this->loggerInstance;
267
	}
268
269
	/**
270
	 * Add annotations namespace.
271
	 * Every added namespace will be included in annotation name resolving for current instance.
272
	 *
273
	 * @param string $ns
274
	 * @renturn Addendum
275
	 */
276 3
	public function addNamespace($ns)
277
	{
278 3
		NameNormalizer::normalize($ns, false);
279 3
		if (!in_array($ns, $this->namespaces))
280
		{
281 1
			$before = count($this->namespaces);
282 1
			$this->namespaces[] = $ns;
283 1
			$this->namespaces = array_unique($this->namespaces);
284 1
			$after = count($this->namespaces);
285 1
			if ($after !== $before)
286
			{
287 1
				$this->nameKeys = array_flip($this->namespaces);
288 1
				Cache\NsCache::$addeNs = true;
289
			}
290
291 1
			$this->di->store($this, [], true);
292
293
			// Reconfigure flyweight instances if present
294 1
			if (!empty(self::$addendums[$this->instanceId]))
295
			{
296 1
				self::$addendums[$this->instanceId]->di->configure(self::$addendums[$this->instanceId]);
297
			}
298
		}
299 3
		Meta::$addNs = true;
300 3
		return $this;
301
	}
302
303
	/**
304
	 * Add many annotation namespaces.
305
	 * This is same as `addNamespace`, in that difference that many namespaces at once can be added.
306
	 *
307
	 * It accepts array of namespaces as param:
308
	 * ```php
309
	 * $nss = [
310
	 * 		'Maslosoft\Addendum\Annotations',
311
	 * 		'Maslosoft\Mangan\Annotations'
312
	 * ];
313
	 * ```
314
	 *
315
	 * @param string[] $nss
316
	 * @return Addendum
317
	 */
318 32
	public function addNamespaces($nss)
319
	{
320 32
		foreach ($nss as $ns)
321
		{
322 3
			$this->addNamespace($ns);
323
		}
324 32
		return $this;
325
	}
326
327
	/**
328
	 * Clear entire annotations cache.
329
	 */
330 3
	public static function cacheClear()
331
	{
332 3
		self::$annotations = [];
333 3
		self::$classNames = [];
334 3
		Blacklister::reset();
335 3
		Builder::clearCache();
336 3
		(new MetaCache())->clear();
337 3
	}
338
339
	/**
340
	 * TODO This should not be static
341
	 * @param Reflector $reflection
342
	 * @return mixed[]
343
	 */
344 44
	public static function getDocComment(Reflector $reflection)
345
	{
346
		// NOTE: Due to a nature of traits, raw doc parsing is always needed
347
		// When using reflection's method `getDocComment` it will return
348
		// doc comment like if it was pasted in code. Thus use statement
349
		// from traits would be omited. See https://github.com/Maslosoft/Addendum/issues/24
350 44
		$docComment = new DocComment();
351 44
		if ($reflection instanceof ReflectionClass)
352
		{
353 37
			$commentString = $docComment->get($reflection)['class'];
354
		}
355
		else
356
		{
357 26
			$commentString = $docComment->get($reflection);
358
		}
359 44
		return $commentString;
360
	}
361
362
	/**
363
	 * This method has no effect
364
	 * @deprecated Since 4.0.4 this has no effect
365
	 * @param bool $enabled
366
	 */
367
	public static function setRawMode($enabled = true)
0 ignored issues
show
Unused Code introduced by
The parameter $enabled is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
368
	{
369
		// Deprecated
370
	}
371
372
	/**
373
	 * Resolve annotation class name to prefixed annotation class name
374
	 *
375
	 * @param string|bool $class
376
	 * @return string
377
	 */
378 51
	public static function resolveClassName($class)
379
	{
380 51
		if (false === $class)
381
		{
382 51
			return null;
383
		}
384 50
		if (isset(self::$classNames[$class]))
385
		{
386 42
			return self::$classNames[$class];
387
		}
388 50
		$matching = [];
389 50
		foreach (self::getDeclaredAnnotations() as $declared)
390
		{
391 50
			if ($declared == $class)
392
			{
393
				$matching[] = $declared;
394
			}
395
			else
396
			{
397 50
				$pos = strrpos($declared, "_$class");
398 50
				if ($pos !== false && ($pos + strlen($class) == strlen($declared) - 1))
399
				{
400 50
					$matching[] = $declared;
401
				}
402
			}
403
		}
404 50
		$result = null;
405 50
		switch (count($matching))
406
		{
407 50
			case 0: $result = $class;
408 50
				break;
409
			case 1: $result = $matching[0];
410
				break;
411
			default: trigger_error("Cannot resolve class name for '$class'. Possible matches: " . join(', ', $matching), E_USER_ERROR);
412
		}
413 50
		self::$classNames[$class] = $result;
414 50
		return $result;
415
	}
416
417 50
	private static function getDeclaredAnnotations()
418
	{
419 50
		if (empty(self::$annotations))
420
		{
421 50
			self::$annotations = [];
422 50
			foreach (get_declared_classes() as $class)
423
			{
424 50
				if ((new ReflectionClass($class))->implementsInterface(AnnotationInterface::class) || $class == AnnotationInterface::class)
425
				{
426 50
					self::$annotations[] = $class;
427
				}
428
			}
429
		}
430 50
		return self::$annotations;
431
	}
432
433
}
434