Completed
Push — v2 ( d685ea )
by Peter
11:43
created

Addendum::processFile()   D

Complexity

Conditions 13
Paths 2

Size

Total Lines 89
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
dl 0
loc 89
rs 4.9922
c 0
b 0
f 0
ccs 0
cts 16
cp 0
cc 13
eloc 49
nc 2
nop 2
crap 182

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Interfaces\AnnotatedInterface;
16
use Maslosoft\Addendum\Utilities\AnnotationUtility;
17
use Maslosoft\Addendum\Utilities\ClassChecker;
18
use Maslosoft\Addendum\Utilities\FileWalker;
19
use Maslosoft\Signals\Helpers\DataSorter;
20
use Maslosoft\Signals\Helpers\NameNormalizer;
21
use Maslosoft\Signals\Interfaces\ExtractorInterface;
22
use Maslosoft\Signals\Signal;
23
use ReflectionClass;
24
use UnexpectedValueException;
25
26
/**
27
 * Addendum extractor
28
 *
29
 * @author Piotr Maselkowski <pmaselkowski at gmail.com>
30
 */
31
class Addendum implements ExtractorInterface
32
{
33
34
	// Data keys for annotations extraction
35
	const SlotFor = 'SlotFor';
36
	const SignalFor = 'SignalFor';
37
	// Default annotation names
38
	const SlotName = 'SlotFor';
39
	const SignalName = 'SignalFor';
40
41
	/**
42
	 * Signal instance
43
	 * @var Signal
44
	 */
45
	private $signal = null;
46
47
	/**
48
	 * Signals and slots data
49
	 * @var mixed
50
	 */
51
	private $data = [
52
		Signal::Slots => [
53
		],
54
		Signal::Signals => [
55
		]
56
	];
57
58
	/**
59
	 * Scanned file paths
60
	 * @var string[]
61
	 */
62
	private $paths = [];
63
64
	/**
65
	 * Annotations mathing patterns
66
	 * @var string[]
67
	 */
68
	private $patterns = [];
69
70 15
	public function __construct()
71
	{
72
		$annotations = [
73 15
			self::SlotFor,
74
			self::SignalFor
75 15
		];
76 15
		foreach ($annotations as $annotation)
77
		{
78 15
			$annotation = preg_replace('~^@~', '', $annotation);
79 15
			$this->patterns[] = sprintf('~@%s~', $annotation);
80 15
		}
81 15
	}
82
83
	/**
84
	 * Get signals and slots data
85
	 * @return mixed
86
	 */
87
	public function getData()
88
	{
89
		(new FileWalker([], [$this, 'processFile'], $this->signal->paths))->walk();
90
		DataSorter::sort($this->data);
91
		return $this->data;
92
	}
93
94
	/**
95
	 * Get scanned paths. This is available only after getData call.
96
	 * @return string[]
97
	 */
98
	public function getPaths()
99
	{
100
		return $this->paths;
101
	}
102
103
	/**
104
	 * Set signal instance
105
	 * @param Signal $signal
106
	 */
107
	public function setSignal(Signal $signal)
108
	{
109
		$this->signal = $signal;
110
	}
111
112
	/**
113
	 * @param string $file
114
	 */
115
	public function processFile($file, $contents)
116
	{
117
		$file = realpath($file);
118
		$this->paths[] = $file;
119
		// Remove initial `\` from namespace
120
		$annotated = AnnotationUtility::rawAnnotate($file);
121
		$namespace = preg_replace('~^\\\\+~', '', $annotated['namespace']);
122
		$className = $annotated['className'];
123
124
125
		// Use fully qualified name, class must autoload
126
		$fqn = $namespace . '\\' . $className;
127
		NameNormalizer::normalize($fqn);
128
		$info = new ReflectionClass($fqn);
129
		$isAnnotated = $info->implementsInterface(AnnotatedInterface::class);
130
		$hasSignals = $this->hasSignals($contents);
131
132
		// Old classes must now implement interface
133
		// Brake BC!
134
		if ($hasSignals && !$isAnnotated)
135
		{
136
			throw new UnexpectedValueException(sprintf('Class %s must implement %s to use signals', $fqn, AnnotatedInterface::class));
137
		}
138
		var_dump($fqn);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($fqn); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
139
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method processFile() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
140
141
		// Signals
142
		$class = AnnotationUtility::rawAnnotate($file)['class'];
0 ignored issues
show
Unused Code introduced by
// Signals $class = \Mas...notate($file)['class']; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
143
		if (isset($class[self::SignalFor]))
144
		{
145
			$val = $this->getValuesFor($class[self::SignalFor]);
146
			foreach ($val as $slot)
147
			{
148
				NameNormalizer::normalize($slot);
149
				$this->checkClass($slot, $fqn);
150
				$this->data[Signal::Slots][$slot][$fqn] = true;
151
			}
152
		}
153
154
		// Slots
155
		// For constructor injection
156
		if (isset($class[self::SlotFor]))
157
		{
158
			$val = $this->getValuesFor($class[self::SlotFor]);
159
			foreach ($val as $slot)
160
			{
161
				NameNormalizer::normalize($slot);
162
				$this->checkClass($slot, $fqn);
163
				$key = implode('@', [$fqn, '__construct', '()']);
164
				$this->data[Signal::Signals][$slot][$fqn][$key] = true;
165
			}
166
		}
167
168
		// For method injection
169
		$methods = AnnotationUtility::rawAnnotate($file)['methods'];
170
		foreach ($methods as $methodName => $method)
171
		{
172
			if (!isset($method[self::SlotFor]))
173
			{
174
				continue;
175
			}
176
			$val = $this->getValuesFor($method[self::SlotFor]);
177
			foreach ($val as $slot)
178
			{
179
				NameNormalizer::normalize($slot);
180
				$this->checkClass($slot, $fqn);
181
				$key = implode('@', [$fqn, $methodName, '()']);
182
				$this->data[Signal::Signals][$slot][$fqn][$key] = sprintf('%s()', $methodName);
183
			}
184
		}
185
186
		// For property injection
187
		$fields = AnnotationUtility::rawAnnotate($file)['fields'];
188
		foreach ($fields as $fieldName => $method)
189
		{
190
			if (!isset($method[self::SlotFor]))
191
			{
192
				continue;
193
			}
194
			$val = $this->getValuesFor($method[self::SlotFor]);
195
			foreach ($val as $slot)
196
			{
197
				NameNormalizer::normalize($slot);
198
				$this->checkClass($slot, $fqn);
199
				$key = implode('@', [$fqn, $fieldName]);
200
				$this->data[Signal::Signals][$slot][$fqn][$key] = sprintf('%s', $fieldName);
201
			}
202
		}
203
	}
204
205
	private function hasSignals($contents)
206
	{
207
		foreach ($this->patterns as $pattern)
208
		{
209
			if (preg_match($pattern, $contents))
210
			{
211
				return true;
212
			}
213
		}
214
		return false;
215
	}
216
217
	private function getValuesFor($src)
218
	{
219
		$value = [];
220
		foreach ($src as $val)
221
		{
222
			if (!array_key_exists('value', $val))
223
			{
224
				continue;
225
			}
226
			$val = $val['value'];
227
			if (is_array($val))
228
			{
229
				$value = array_merge($value, $val);
230
			}
231
			elseif (is_string($val))
232
			{
233
				$value[] = $val;
234
			}
235
		}
236
		return array_values(array_unique($value));
237
	}
238
239
	private function checkClass($slot, $fqn)
240
	{
241
		if (!ClassChecker::exists($fqn))
242
		{
243
			$this->signal->getLogger()->warning(sprintf("Class `%s` not found while generating signal data for `%s`", $fqn, $slot));
244
		}
245
	}
246
247
}
248