Completed
Pull Request — stable10 (#757)
by Robin
09:09
created

DependencyAnalyzer::compareBigger()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Bernhard Posselt <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Stefan Weil <[email protected]>
10
 * @author Thomas Müller <[email protected]>
11
 *
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OC\App;
29
30
use OCP\IL10N;
31
32
class DependencyAnalyzer {
33
34
	/** @var Platform */
35
	private $platform;
36
	/** @var \OCP\IL10N */
37
	private $l;
38
	/** @var array */
39
	private $appInfo;
40
41
	/**
42
	 * @param Platform $platform
43
	 * @param \OCP\IL10N $l
44
	 */
45
	function __construct(Platform $platform, IL10N $l) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
46
		$this->platform = $platform;
47
		$this->l = $l;
48
	}
49
50
	/**
51
	 * @param array $app
52
	 * @returns array of missing dependencies
53
	 */
54
	public function analyze(array $app) {
55
		$this->appInfo = $app;
56
		if (isset($app['dependencies'])) {
57
			$dependencies = $app['dependencies'];
58
		} else {
59
			$dependencies = [];
60
		}
61
62
		return array_merge(
63
			$this->analyzePhpVersion($dependencies),
64
			$this->analyzeDatabases($dependencies),
65
			$this->analyzeCommands($dependencies),
66
			$this->analyzeLibraries($dependencies),
67
			$this->analyzeOS($dependencies),
68
			$this->analyzeOC($dependencies, $app)
69
		);
70
	}
71
72
	/**
73
	 * Truncates both versions to the lowest common version, e.g.
74
	 * 5.1.2.3 and 5.1 will be turned into 5.1 and 5.1,
75
	 * 5.2.6.5 and 5.1 will be turned into 5.2 and 5.1
76
	 * @param string $first
77
	 * @param string $second
78
	 * @return string[] first element is the first version, second element is the
79
	 * second version
80
	 */
81
	private function normalizeVersions($first, $second) {
82
		$first = explode('.', $first);
83
		$second = explode('.', $second);
84
85
		// get both arrays to the same minimum size
86
		$length = min(count($second), count($first));
87
		$first = array_slice($first, 0, $length);
88
		$second = array_slice($second, 0, $length);
89
90
		return [implode('.', $first), implode('.', $second)];
91
	}
92
93
	/**
94
	 * Parameters will be normalized and then passed into version_compare
95
	 * in the same order they are specified in the method header
96
	 * @param string $first
97
	 * @param string $second
98
	 * @param string $operator
99
	 * @return bool result similar to version_compare
100
	 */
101
	private function compare($first, $second, $operator) {
102
		// we can't normalize versions if one of the given parameters is not a
103
		// version string but null. In case one parameter is null normalization
104
		// will therefore be skipped
105
		if ($first !== null && $second !== null) {
106
			list($first, $second) = $this->normalizeVersions($first, $second);
107
		}
108
109
		return version_compare($first, $second, $operator);
110
	}
111
112
	/**
113
	 * Checks if a version is bigger than another version
114
	 * @param string $first
115
	 * @param string $second
116
	 * @return bool true if the first version is bigger than the second
117
	 */
118
	private function compareBigger($first, $second) {
119
		return $this->compare($first, $second, '>');
120
	}
121
122
	/**
123
	 * Checks if a version is smaller than another version
124
	 * @param string $first
125
	 * @param string $second
126
	 * @return bool true if the first version is smaller than the second
127
	 */
128
	private function compareSmaller($first, $second) {
129
		return $this->compare($first, $second, '<');
130
	}
131
132
	/**
133
	 * @param array $dependencies
134
	 * @return array
135
	 */
136
	private function analyzePhpVersion(array $dependencies) {
137
		$missing = [];
138 View Code Duplication
		if (isset($dependencies['php']['@attributes']['min-version'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
139
			$minVersion = $dependencies['php']['@attributes']['min-version'];
140
			if ($this->compareSmaller($this->platform->getPhpVersion(), $minVersion)) {
141
				$missing[] = (string)$this->l->t('PHP %s or higher is required.', $minVersion);
142
			}
143
		}
144 View Code Duplication
		if (isset($dependencies['php']['@attributes']['max-version'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
145
			$maxVersion = $dependencies['php']['@attributes']['max-version'];
146
			if ($this->compareBigger($this->platform->getPhpVersion(), $maxVersion)) {
147
				$missing[] = (string)$this->l->t('PHP with a version lower than %s is required.', $maxVersion);
148
			}
149
		}
150 View Code Duplication
		if (isset($dependencies['php']['@attributes']['min-int-size'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
			$intSize = $dependencies['php']['@attributes']['min-int-size'];
152
			if ($intSize > $this->platform->getIntSize()*8) {
153
				$missing[] = (string)$this->l->t('%sbit or higher PHP required.', $intSize);
154
			}
155
		}
156
		return $missing;
157
	}
158
159
	/**
160
	 * @param array $dependencies
161
	 * @return array
162
	 */
163 View Code Duplication
	private function analyzeDatabases(array $dependencies) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164
		$missing = [];
165
		if (!isset($dependencies['database'])) {
166
			return $missing;
167
		}
168
169
		$supportedDatabases = $dependencies['database'];
170
		if (empty($supportedDatabases)) {
171
			return $missing;
172
		}
173
		if (!is_array($supportedDatabases)) {
174
			$supportedDatabases = array($supportedDatabases);
175
		}
176
		$supportedDatabases = array_map(function ($db) {
177
			return $this->getValue($db);
178
		}, $supportedDatabases);
179
		$currentDatabase = $this->platform->getDatabase();
180
		if (!in_array($currentDatabase, $supportedDatabases)) {
181
			$missing[] = (string)$this->l->t('Following databases are supported: %s', join(', ', $supportedDatabases));
182
		}
183
		return $missing;
184
	}
185
186
	/**
187
	 * @param array $dependencies
188
	 * @return array
189
	 */
190
	private function analyzeCommands(array $dependencies) {
191
		$missing = [];
192
		if (!isset($dependencies['command'])) {
193
			return $missing;
194
		}
195
196
		$commands = $dependencies['command'];
197
		if (!is_array($commands)) {
198
			$commands = array($commands);
199
		}
200
		$os = $this->platform->getOS();
201
		foreach ($commands as $command) {
202
			if (isset($command['@attributes']['os']) && $command['@attributes']['os'] !== $os) {
203
				continue;
204
			}
205
			$commandName = $this->getValue($command);
206
			if (!$this->platform->isCommandKnown($commandName)) {
207
				$missing[] = (string)$this->l->t('The command line tool %s could not be found', $commandName);
208
			}
209
		}
210
		return $missing;
211
	}
212
213
	/**
214
	 * @param array $dependencies
215
	 * @return array
216
	 */
217
	private function analyzeLibraries(array $dependencies) {
218
		$missing = [];
219
		if (!isset($dependencies['lib'])) {
220
			return $missing;
221
		}
222
223
		$libs = $dependencies['lib'];
224
		if (!is_array($libs)) {
225
			$libs = array($libs);
226
		}
227
		foreach ($libs as $lib) {
228
			$libName = $this->getValue($lib);
229
			$libVersion = $this->platform->getLibraryVersion($libName);
230
			if (is_null($libVersion)) {
231
				$missing[] = (string)$this->l->t('The library %s is not available.', $libName);
232
				continue;
233
			}
234
235
			if (is_array($lib)) {
236 View Code Duplication
				if (isset($lib['@attributes']['min-version'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237
					$minVersion = $lib['@attributes']['min-version'];
238
					if ($this->compareSmaller($libVersion, $minVersion)) {
239
						$missing[] = (string)$this->l->t('Library %s with a version higher than %s is required - available version %s.',
240
							array($libName, $minVersion, $libVersion));
241
					}
242
				}
243 View Code Duplication
				if (isset($lib['@attributes']['max-version'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
244
					$maxVersion = $lib['@attributes']['max-version'];
245
					if ($this->compareBigger($libVersion, $maxVersion)) {
246
						$missing[] = (string)$this->l->t('Library %s with a version lower than %s is required - available version %s.',
247
							array($libName, $maxVersion, $libVersion));
248
					}
249
				}
250
			}
251
		}
252
		return $missing;
253
	}
254
255
	/**
256
	 * @param array $dependencies
257
	 * @return array
258
	 */
259 View Code Duplication
	private function analyzeOS(array $dependencies) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
260
		$missing = [];
261
		if (!isset($dependencies['os'])) {
262
			return $missing;
263
		}
264
265
		$oss = $dependencies['os'];
266
		if (empty($oss)) {
267
			return $missing;
268
		}
269
		if (is_array($oss)) {
270
			$oss = array_map(function ($os) {
271
				return $this->getValue($os);
272
			}, $oss);
273
		} else {
274
			$oss = array($oss);
275
		}
276
		$currentOS = $this->platform->getOS();
277
		if (!in_array($currentOS, $oss)) {
278
			$missing[] = (string)$this->l->t('Following platforms are supported: %s', join(', ', $oss));
279
		}
280
		return $missing;
281
	}
282
283
	/**
284
	 * @param array $dependencies
285
	 * @param array $appInfo
286
	 * @return array
287
	 */
288
	private function analyzeOC(array $dependencies, array $appInfo) {
289
		$missing = [];
290
		$minVersion = null;
291
		if (isset($dependencies['owncloud']['@attributes']['min-version'])) {
292
			$minVersion = $dependencies['owncloud']['@attributes']['min-version'];
293
		} elseif (isset($appInfo['requiremin'])) {
294
			$minVersion = $appInfo['requiremin'];
295
		} elseif (isset($appInfo['require'])) {
296
			$minVersion = $appInfo['require'];
297
		}
298
		$maxVersion = null;
299
		if (isset($dependencies['owncloud']['@attributes']['max-version'])) {
300
			$maxVersion = $dependencies['owncloud']['@attributes']['max-version'];
301
		} elseif (isset($appInfo['requiremax'])) {
302
			$maxVersion = $appInfo['requiremax'];
303
		}
304
305 View Code Duplication
		if (!is_null($minVersion)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
306
			if ($this->compareSmaller($this->platform->getOcVersion(), $minVersion)) {
307
				$missing[] = (string)$this->l->t('Server version %s or higher is required.', $this->toVisibleVersion($minVersion));
0 ignored issues
show
Documentation introduced by
$this->toVisibleVersion($minVersion) is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
308
			}
309
		}
310 View Code Duplication
		if (!is_null($maxVersion)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
311
			if ($this->compareBigger($this->platform->getOcVersion(), $maxVersion)) {
312
				$missing[] = (string)$this->l->t('Server version %s or lower is required.', $this->toVisibleVersion($maxVersion));
0 ignored issues
show
Documentation introduced by
$this->toVisibleVersion($maxVersion) is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
313
			}
314
		}
315
		return $missing;
316
	}
317
318
	/**
319
	 * Map the internal version number to the Nextcloud version
320
	 *
321
	 * @param string $version
322
	 * @return string
323
	 */
324
	protected function toVisibleVersion($version) {
325
		switch ($version) {
326
			case '9.1':
327
				return '10';
328
			case '9.2':
329
				return '11';
330
			default:
331
				if (strpos($version, '9.1.') === 0) {
332
					$version = '10.0.' . substr($version, 4);
333
				} else if (strpos($version, '9.2.') === 0) {
334
					$version = '11.0.' . substr($version, 4);
335
				}
336
				return $version;
337
		}
338
	}
339
340
	/**
341
	 * @param $element
342
	 * @return mixed
343
	 */
344
	private function getValue($element) {
345
		if (isset($element['@value']))
346
			return $element['@value'];
347
		return (string)$element;
348
	}
349
}
350