Completed
Push — master ( 1408f8...7f35a0 )
by Peter
04:28
created

Addendum::getPaths()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
ccs 0
cts 2
cp 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * This software package is licensed under `AGPL, Commercial` license[s].
5
 *
6
 * @package maslosoft/signals
7
 * @license AGPL, Commercial
8
 *
9
 * @copyright Copyright (c) Peter Maselkowski <[email protected]>
10
 *
11
 */
12
13
namespace Maslosoft\Signals\Builder;
14
15
use Maslosoft\Addendum\Utilities\AnnotationUtility;
16
use Maslosoft\Addendum\Utilities\ClassChecker;
17
use Maslosoft\Addendum\Utilities\FileWalker;
18
use Maslosoft\Signals\Helpers\DataSorter;
19
use Maslosoft\Signals\Helpers\NameNormalizer;
20
use Maslosoft\Signals\Interfaces\ExtractorInterface;
21
use Maslosoft\Signals\Signal;
22
23
/**
24
 * Addendum extractor
25
 *
26
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
27
 */
28
class Addendum implements ExtractorInterface
29
{
30
31
	// Data keys for annotations extraction
32
	const SlotFor = 'SlotFor';
33
	const SignalFor = 'SignalFor';
34
	// Default annotation names
35
	const SlotName = 'SlotFor';
36
	const SignalName = 'SignalFor';
37
38
	/**
39
	 * Signal instance
40
	 * @var Signal
41
	 */
42
	private $signal = null;
43
44
	/**
45
	 * Signals and slots data
46
	 * @var mixed
47
	 */
48
	private $data = [
49
		Signal::Slots => [
50
		],
51
		Signal::Signals => [
52
		]
53
	];
54
55
	/**
56
	 * Scanned file paths
57
	 * @var string[]
58
	 */
59
	private $paths = [];
60
61
	/**
62
	 * Get signals and slots data
63
	 * @return mixed
64
	 */
65
	public function getData()
66
	{
67
		$annotations = [
68
			self::SlotFor,
69
			self::SignalFor
70
		];
71
		(new FileWalker($annotations, [$this, 'processFile'], $this->signal->paths))->walk();
72
		DataSorter::sort($this->data);
73
		return $this->data;
74
	}
75
76
	/**
77
	 * Get scanned paths. This is available only after getData call.
78
	 * @return string[]
79
	 */
80
	public function getPaths()
81
	{
82
		return $this->paths;
83
	}
84
85
	/**
86
	 * Set signal instance
87
	 * @param Signal $signal
88
	 */
89
	public function setSignal(Signal $signal)
90
	{
91
		$this->signal = $signal;
92
	}
93
94
	/**
95
	 * @param string $file
96
	 */
97
	public function processFile($file)
98
	{
99
		$file = realpath($file);
100
		$this->paths[] = $file;
101
		// Remove initial `\` from namespace
102
		$namespace = preg_replace('~^\\\\+~', '', AnnotationUtility::rawAnnotate($file)['namespace']);
103
		$className = AnnotationUtility::rawAnnotate($file)['className'];
104
105
		// Use fully qualified name, class must autoload
106
		$fqn = $namespace . '\\' . $className;
107
		NameNormalizer::normalize($fqn);
108
109
		// Signals
110
		$class = AnnotationUtility::rawAnnotate($file)['class'];
111
		if (isset($class[self::SignalFor]))
112
		{
113
			$val = $this->getValuesFor($class[self::SignalFor]);
114
			foreach ($val as $slot)
115
			{
116
				NameNormalizer::normalize($slot);
117
				$this->checkClass($slot, $fqn);
118
				$this->data[Signal::Slots][$slot][$fqn] = true;
119
			}
120
		}
121
122
		// Slots
123
		// For constructor injection
124
		if (isset($class[self::SlotFor]))
125
		{
126
			$val = $this->getValuesFor($class[self::SlotFor]);
127
			foreach ($val as $slot)
128
			{
129
				NameNormalizer::normalize($slot);
130
				$this->checkClass($slot, $fqn);
131
				$key = implode('@', [$fqn, '__construct', '()']);
132
				$this->data[Signal::Signals][$slot][$fqn][$key] = true;
133
			}
134
		}
135
136
		// For method injection
137
		$methods = AnnotationUtility::rawAnnotate($file)['methods'];
138
		foreach ($methods as $methodName => $method)
139
		{
140
			if (!isset($method[self::SlotFor]))
141
			{
142
				continue;
143
			}
144
			$val = $this->getValuesFor($method[self::SlotFor]);
145
			foreach ($val as $slot)
146
			{
147
				NameNormalizer::normalize($slot);
148
				$this->checkClass($slot, $fqn);
149
				$key = implode('@', [$fqn, $methodName, '()']);
150
				$this->data[Signal::Signals][$slot][$fqn][$key] = sprintf('%s()', $methodName);
151
			}
152
		}
153
154
		// For property injection
155
		$fields = AnnotationUtility::rawAnnotate($file)['fields'];
156
		foreach ($fields as $fieldName => $method)
157
		{
158
			if (!isset($method[self::SlotFor]))
159
			{
160
				continue;
161
			}
162
			$val = $this->getValuesFor($method[self::SlotFor]);
163
			foreach ($val as $slot)
164
			{
165
				NameNormalizer::normalize($slot);
166
				$this->checkClass($slot, $fqn);
167
				$key = implode('@', [$fqn, $fieldName]);
168
				$this->data[Signal::Signals][$slot][$fqn][$key] = sprintf('%s', $fieldName);
169
			}
170
		}
171
	}
172
173
	private function getValuesFor($src)
174
	{
175
		$value = [];
176
		foreach ($src as $val)
177
		{
178
			if (!array_key_exists('value', $val))
179
			{
180
				continue;
181
			}
182
			$val = $val['value'];
183
			if (is_array($val))
184
			{
185
				$value = array_merge($value, $val);
186
			}
187
			elseif (is_string($val))
188
			{
189
				$value[] = $val;
190
			}
191
		}
192
		return array_values(array_unique($value));
193
	}
194
195
	private function checkClass($slot, $fqn)
196
	{
197
		if (!ClassChecker::exists($fqn))
198
		{
199
			$this->signal->getLogger()->warning(sprintf("Class `%s` not found while generating signal data for `%s`", $fqn, $slot));
200
		}
201
	}
202
203
}
204