Passed
Push — master ( 6791f9...01e57e )
by Adam
02:08
created

Loader::getGlobalMetadata()   C

Complexity

Conditions 11
Paths 12

Size

Total Lines 52
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 17.8835

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 52
ccs 16
cts 26
cp 0.6153
rs 5.9999
cc 11
eloc 28
nc 12
nop 1
crap 17.8835

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
 * Loader.php
4
 *
5
 * @copyright      More in license.md
6
 * @license        https://www.ipublikuj.eu
7
 * @author         Adam Kadlec https://www.ipublikuj.eu
8
 * @package        iPublikuj:Packages!
9
 * @subpackage     Loaders
10
 * @since          1.0.0
11
 *
12
 * @date           30.05.15
13
 */
14
15
declare(strict_types = 1);
16
17
namespace IPub\Packages\Loaders;
18
19
use Composer;
20
use Composer\Semver;
21
22
use Nette;
23
use Nette\DI;
24
use Nette\Utils;
25
26
use IPub\Packages\Entities;
27
use IPub\Packages\Exceptions;
28
29
/**
30
 * Package loader
31
 *
32
 * @package        iPublikuj:Packages!
33
 * @subpackage     Loaders
34
 *
35
 * @author         Adam Kadlec <[email protected]>
36
 */
37 1
final class Loader implements ILoader
38
{
39
	/**
40
	 * Implement nette smart magic
41
	 */
42 1
	use Nette\SmartObject;
43
44
	/**
45
	 * @var string[]
46
	 */
47
	private $packageFiles = [];
48
49
	/**
50
	 * @var array
51
	 */
52
	private $metadataSources = [];
53
54
	/**
55
	 * @var string
56
	 */
57
	private $vendorDir;
58
59
	/**
60
	 * @var array|NULL
61
	 */
62
	private $globalMetadata;
63
64
	/**
65
	 * @var Semver\VersionParser
66
	 */
67
	private $versionParser;
68
69
	/**
70
	 * @var DI\Container
71
	 */
72
	private $container;
73
74
	/**
75
	 * @param array $packageFiles
76
	 * @param array $metadataSources
77
	 * @param $vendorDir
78
	 * @param DI\Container $container
79
	 */
80
	public function __construct(array $packageFiles = [], array $metadataSources = [], $vendorDir, DI\Container $container)
81
	{
82 1
		$this->packageFiles = $packageFiles;
83 1
		$this->metadataSources = $metadataSources;
84 1
		$this->vendorDir = $vendorDir;
85 1
		$this->versionParser = new Semver\VersionParser;
86
87 1
		$this->container = $container;
88 1
	}
89
90
	/**
91
	 * @param string $file
92
	 *
93
	 * @return Entities\IPackage
94
	 *
95
	 * @throws Exceptions\InvalidPackageDefinitionException
96
	 * @throws Exceptions\InvalidStateException
97
	 */
98
	public function load(string $file) : Entities\IPackage
99
	{
100 1
		$path = dirname($file);
101
102
		try {
103 1
			$data = Utils\Json::decode(file_get_contents($file), Utils\Json::FORCE_ARRAY);
104
105
		} catch (Utils\JsonException $ex) {
0 ignored issues
show
Bug introduced by
The class Nette\Utils\JsonException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
106
			throw new Exceptions\InvalidPackageDefinitionException(sprintf('The file "%s" has invalid JSON format.', $file));
107
		}
108
109 1
		$tmpPackage = new Entities\VirtualPackage($data, $path);
110
111 1
		if (($metadata = $this->getGlobalMetadata($tmpPackage)) !== []) {
112
			$data = Utils\Arrays::mergeTree($data, [
113
				'extra' => [
114
					'ipub' => $metadata,
115
				]
116
			]);
117
		}
118
119 1
		foreach ($this->packageFiles as $packageFile) {
120 1
			if (is_file($path . DIRECTORY_SEPARATOR . $packageFile)) {
121
				$class = $this->getPackageClassByFile($path . DIRECTORY_SEPARATOR . $packageFile);
122
123
				include_once $path . DIRECTORY_SEPARATOR . $packageFile;
124
125
				$package = $this->container->createInstance($class, [$data]);
126
127 1
				return $package;
128
			}
129
		}
130
131 1
		return new Entities\VirtualPackage($data, $path);
132
	}
133
134
	/**
135
	 * @param string $file
136
	 *
137
	 * @return string
138
	 */
139
	private function getPackageClassByFile(string $file) : string
140
	{
141
		$classes = $this->getClassesFromFile($file);
142
143
		if (count($classes) !== 1) {
144
			throw new Exceptions\InvalidArgumentException(sprintf('File \'%s\' must contain only one class.', $file));
145
		}
146
147
		return $classes[0];
148
	}
149
150
	/**
151
	 * @param Entities\IPackage $package
152
	 *
153
	 * @return array
154
	 *
155
	 * @throws Exceptions\InvalidMetadataSourceDefinitionException
156
	 * @throws Exceptions\InvalidStateException
157
	 */
158
	private function getGlobalMetadata(Entities\IPackage $package) : array
159
	{
160 1
		if ($this->globalMetadata === NULL) {
161 1
			$this->globalMetadata = [];
162
163 1
			foreach ($this->metadataSources as $source) {
164 1
				if (substr($source, 0, 7) === 'http://' || substr($source, 0, 8) === 'https://') {
165 1
					$ch = curl_init();
166
167 1
					curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
168 1
					curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
169 1
					curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
170 1
					curl_setopt($ch, CURLOPT_URL, $source);
171
172 1
					$data = curl_exec($ch);
173
174
				} else {
175
					$data = file_get_contents($source);
176
				}
177
178 1
				if (!$data) {
179
					throw new Exceptions\InvalidStateException(sprintf('Source \'$source\' is empty.', $source));
180
				}
181
182 1
				if ($data) {
183
					try {
184 1
						$data = Utils\Json::decode($data, Utils\Json::FORCE_ARRAY);
185
186
					} catch (Utils\JsonException $ex) {
0 ignored issues
show
Bug introduced by
The class Nette\Utils\JsonException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
187
						throw new Exceptions\InvalidMetadataSourceDefinitionException(sprintf('The global metadata source "%s" has invalid JSON format.', $source));
188
					}
189
190 1
					$this->globalMetadata = Utils\Arrays::mergeTree($this->globalMetadata, $data);
191
				}
192
			}
193
194
		}
195
196 1
		if (!isset($this->globalMetadata[$package->getName()])) {
197 1
			return [];
198
		}
199
200
		$versionProvide = new Semver\Constraint\Constraint('==', $package->getVersion());
201
202
		foreach ($this->globalMetadata[$package->getName()] as $data) {
203
			$versionRequire = $this->versionParser->parseConstraints($data['version']);
204
205
			if ($versionRequire->matches($versionProvide)) {
206
				return $data['metadata'];
207
			}
208
		}
209
	}
210
211
	/**
212
	 * Get class names from given file
213
	 * http://stackoverflow.com/a/11070559
214
	 *
215
	 * @param string $file
216
	 *
217
	 * @return array
218
	 */
219
	private function getClassesFromFile(string $file) : array
220
	{
221
		$classes = [];
222
223
		$namespace = 0;
224
		$tokens = token_get_all(file_get_contents($file));
225
		$count = count($tokens);
226
		$dlm = FALSE;
227
228
		for ($i = 2; $i < $count; $i++) {
229
			if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] === "phpnamespace" || $tokens[$i - 2][1] === "namespace")) ||
230
				($dlm && $tokens[$i - 1][0] === T_NS_SEPARATOR && $tokens[$i][0] === T_STRING)
231
			) {
232
				if (!$dlm) {
233
					$namespace = 0;
234
				}
235
236
				if (isset($tokens[$i][1])) {
237
					$namespace = $namespace ? $namespace . "\\" . $tokens[$i][1] : $tokens[$i][1];
238
					$dlm = TRUE;
239
				}
240
241
			} elseif ($dlm && ($tokens[$i][0] != T_NS_SEPARATOR) && ($tokens[$i][0] != T_STRING)) {
242
				$dlm = FALSE;
243
			}
244
245
			if (($tokens[$i - 2][0] === T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] === "phpclass"))
246
				&& $tokens[$i - 1][0] === T_WHITESPACE && $tokens[$i][0] === T_STRING
247
			) {
248
				$class_name = $tokens[$i][1];
249
250
251
				$classes[] = $namespace . '\\' . $class_name;
252
			}
253
		}
254
255
		return $classes;
256
	}
257
}
258