1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @author Bernhard Posselt <[email protected]> |
4
|
|
|
* @author Joas Schilling <[email protected]> |
5
|
|
|
* @author Lukas Reschke <[email protected]> |
6
|
|
|
* @author Morris Jobke <[email protected]> |
7
|
|
|
* @author Thomas Müller <[email protected]> |
8
|
|
|
* |
9
|
|
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
10
|
|
|
* @license AGPL-3.0 |
11
|
|
|
* |
12
|
|
|
* This code is free software: you can redistribute it and/or modify |
13
|
|
|
* it under the terms of the GNU Affero General Public License, version 3, |
14
|
|
|
* as published by the Free Software Foundation. |
15
|
|
|
* |
16
|
|
|
* This program is distributed in the hope that it will be useful, |
17
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
18
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19
|
|
|
* GNU Affero General Public License for more details. |
20
|
|
|
* |
21
|
|
|
* You should have received a copy of the GNU Affero General Public License, version 3, |
22
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/> |
23
|
|
|
* |
24
|
|
|
*/ |
25
|
|
|
|
26
|
|
|
namespace OC\App; |
27
|
|
|
|
28
|
|
|
use OCP\IL10N; |
29
|
|
|
|
30
|
|
|
class DependencyAnalyzer { |
31
|
|
|
|
32
|
|
|
/** @var Platform */ |
33
|
|
|
private $platform; |
34
|
|
|
/** @var \OCP\IL10N */ |
35
|
|
|
private $l; |
36
|
|
|
/** @var array */ |
37
|
|
|
private $appInfo; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @param Platform $platform |
41
|
|
|
* @param \OCP\IL10N $l |
42
|
|
|
*/ |
43
|
|
|
function __construct(Platform $platform, IL10N $l) { |
44
|
|
|
$this->platform = $platform; |
45
|
|
|
$this->l = $l; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @param array $app |
50
|
|
|
* @returns array of missing dependencies |
51
|
|
|
*/ |
52
|
|
|
public function analyze(array $app) { |
53
|
|
|
$this->appInfo = $app; |
54
|
|
|
if (isset($app['dependencies'])) { |
55
|
|
|
$dependencies = $app['dependencies']; |
56
|
|
|
} else { |
57
|
|
|
$dependencies = []; |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
return array_merge( |
61
|
|
|
$this->analyzePhpVersion($dependencies), |
62
|
|
|
$this->analyzeDatabases($dependencies), |
63
|
|
|
$this->analyzeCommands($dependencies), |
64
|
|
|
$this->analyzeLibraries($dependencies), |
65
|
|
|
$this->analyzeOS($dependencies), |
66
|
|
|
$this->analyzeOC($dependencies, $app) |
67
|
|
|
); |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Truncates both versions to the lowest common version, e.g. |
72
|
|
|
* 5.1.2.3 and 5.1 will be turned into 5.1 and 5.1, |
73
|
|
|
* 5.2.6.5 and 5.1 will be turned into 5.2 and 5.1 |
74
|
|
|
* @param string $first |
75
|
|
|
* @param string $second |
76
|
|
|
* @return string[] first element is the first version, second element is the |
77
|
|
|
* second version |
78
|
|
|
*/ |
79
|
|
|
private function normalizeVersions($first, $second) { |
80
|
|
|
$first = explode('.', $first); |
81
|
|
|
$second = explode('.', $second); |
82
|
|
|
|
83
|
|
|
// get both arrays to the same minimum size |
84
|
|
|
$length = min(count($second), count($first)); |
85
|
|
|
$first = array_slice($first, 0, $length); |
86
|
|
|
$second = array_slice($second, 0, $length); |
87
|
|
|
|
88
|
|
|
return [implode('.', $first), implode('.', $second)]; |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Parameters will be normalized and then passed into version_compare |
93
|
|
|
* in the same order they are specified in the method header |
94
|
|
|
* @param string $first |
95
|
|
|
* @param string $second |
96
|
|
|
* @param string $operator |
97
|
|
|
* @return bool result similar to version_compare |
98
|
|
|
*/ |
99
|
|
|
private function compare($first, $second, $operator) { |
100
|
|
|
// we can't normalize versions if one of the given parameters is not a |
101
|
|
|
// version string but null. In case one parameter is null normalization |
102
|
|
|
// will therefore be skipped |
103
|
|
|
if ($first !== null && $second !== null) { |
104
|
|
|
list($first, $second) = $this->normalizeVersions($first, $second); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
return version_compare($first, $second, $operator); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Checks if a version is bigger than another version |
112
|
|
|
* @param string $first |
113
|
|
|
* @param string $second |
114
|
|
|
* @return bool true if the first version is bigger than the second |
115
|
|
|
*/ |
116
|
|
|
private function compareBigger($first, $second) { |
117
|
|
|
return $this->compare($first, $second, '>'); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Checks if a version is smaller than another version |
122
|
|
|
* @param string $first |
123
|
|
|
* @param string $second |
124
|
|
|
* @return bool true if the first version is smaller than the second |
125
|
|
|
*/ |
126
|
|
|
private function compareSmaller($first, $second) { |
127
|
|
|
return $this->compare($first, $second, '<'); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* @param array $dependencies |
132
|
|
|
* @return array |
133
|
|
|
*/ |
134
|
|
|
private function analyzePhpVersion(array $dependencies) { |
135
|
|
|
$missing = []; |
136
|
|
View Code Duplication |
if (isset($dependencies['php']['@attributes']['min-version'])) { |
|
|
|
|
137
|
|
|
$minVersion = $dependencies['php']['@attributes']['min-version']; |
138
|
|
|
if ($this->compareSmaller($this->platform->getPhpVersion(), $minVersion)) { |
139
|
|
|
$missing[] = (string)$this->l->t('PHP %s or higher is required.', $minVersion); |
140
|
|
|
} |
141
|
|
|
} |
142
|
|
View Code Duplication |
if (isset($dependencies['php']['@attributes']['max-version'])) { |
|
|
|
|
143
|
|
|
$maxVersion = $dependencies['php']['@attributes']['max-version']; |
144
|
|
|
if ($this->compareBigger($this->platform->getPhpVersion(), $maxVersion)) { |
145
|
|
|
$missing[] = (string)$this->l->t('PHP with a version lower than %s is required.', $maxVersion); |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
if (isset($dependencies['php']['@attributes']['min-int-size'])) { |
149
|
|
|
$intSize = $dependencies['php']['@attributes']['min-int-size']; |
150
|
|
|
if ($intSize > $this->platform->getIntSize()*8) { |
151
|
|
|
$missing[] = (string)$this->l->t('%sbit or higher PHP required.', $intSize); |
152
|
|
|
} |
153
|
|
|
} |
154
|
|
|
return $missing; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @param array $dependencies |
159
|
|
|
* @return array |
160
|
|
|
*/ |
161
|
|
View Code Duplication |
private function analyzeDatabases(array $dependencies) { |
162
|
|
|
$missing = []; |
163
|
|
|
if (!isset($dependencies['database'])) { |
164
|
|
|
return $missing; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$supportedDatabases = $dependencies['database']; |
168
|
|
|
if (empty($supportedDatabases)) { |
169
|
|
|
return $missing; |
170
|
|
|
} |
171
|
|
|
if (!is_array($supportedDatabases)) { |
172
|
|
|
$supportedDatabases = array($supportedDatabases); |
173
|
|
|
} |
174
|
|
|
$supportedDatabases = array_map(function ($db) { |
175
|
|
|
return $this->getValue($db); |
176
|
|
|
}, $supportedDatabases); |
177
|
|
|
$currentDatabase = $this->platform->getDatabase(); |
178
|
|
|
if (!in_array($currentDatabase, $supportedDatabases)) { |
179
|
|
|
$missing[] = (string)$this->l->t('Following databases are supported: %s', join(', ', $supportedDatabases)); |
180
|
|
|
} |
181
|
|
|
return $missing; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @param array $dependencies |
186
|
|
|
* @return array |
187
|
|
|
*/ |
188
|
|
|
private function analyzeCommands(array $dependencies) { |
189
|
|
|
$missing = []; |
190
|
|
|
if (!isset($dependencies['command'])) { |
191
|
|
|
return $missing; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
$commands = $dependencies['command']; |
195
|
|
|
if (!is_array($commands)) { |
196
|
|
|
$commands = array($commands); |
197
|
|
|
} |
198
|
|
|
$os = $this->platform->getOS(); |
199
|
|
|
foreach ($commands as $command) { |
200
|
|
|
if (isset($command['@attributes']['os']) && $command['@attributes']['os'] !== $os) { |
201
|
|
|
continue; |
202
|
|
|
} |
203
|
|
|
$commandName = $this->getValue($command); |
204
|
|
|
if (!$this->platform->isCommandKnown($commandName)) { |
205
|
|
|
$missing[] = (string)$this->l->t('The command line tool %s could not be found', $commandName); |
206
|
|
|
} |
207
|
|
|
} |
208
|
|
|
return $missing; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @param array $dependencies |
213
|
|
|
* @return array |
214
|
|
|
*/ |
215
|
|
|
private function analyzeLibraries(array $dependencies) { |
216
|
|
|
$missing = []; |
217
|
|
|
if (!isset($dependencies['lib'])) { |
218
|
|
|
return $missing; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
$libs = $dependencies['lib']; |
222
|
|
|
if (!is_array($libs)) { |
223
|
|
|
$libs = array($libs); |
224
|
|
|
} |
225
|
|
|
foreach ($libs as $lib) { |
226
|
|
|
$libName = $this->getValue($lib); |
227
|
|
|
$libVersion = $this->platform->getLibraryVersion($libName); |
228
|
|
|
if (is_null($libVersion)) { |
229
|
|
|
$missing[] = (string)$this->l->t('The library %s is not available.', $libName); |
230
|
|
|
continue; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
if (is_array($lib)) { |
234
|
|
View Code Duplication |
if (isset($lib['@attributes']['min-version'])) { |
|
|
|
|
235
|
|
|
$minVersion = $lib['@attributes']['min-version']; |
236
|
|
|
if ($this->compareSmaller($libVersion, $minVersion)) { |
237
|
|
|
$missing[] = (string)$this->l->t('Library %s with a version higher than %s is required - available version %s.', |
238
|
|
|
array($libName, $minVersion, $libVersion)); |
239
|
|
|
} |
240
|
|
|
} |
241
|
|
View Code Duplication |
if (isset($lib['@attributes']['max-version'])) { |
|
|
|
|
242
|
|
|
$maxVersion = $lib['@attributes']['max-version']; |
243
|
|
|
if ($this->compareBigger($libVersion, $maxVersion)) { |
244
|
|
|
$missing[] = (string)$this->l->t('Library %s with a version lower than %s is required - available version %s.', |
245
|
|
|
array($libName, $maxVersion, $libVersion)); |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
} |
249
|
|
|
} |
250
|
|
|
return $missing; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* @param array $dependencies |
255
|
|
|
* @return array |
256
|
|
|
*/ |
257
|
|
View Code Duplication |
private function analyzeOS(array $dependencies) { |
258
|
|
|
$missing = []; |
259
|
|
|
if (!isset($dependencies['os'])) { |
260
|
|
|
return $missing; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
$oss = $dependencies['os']; |
264
|
|
|
if (empty($oss)) { |
265
|
|
|
return $missing; |
266
|
|
|
} |
267
|
|
|
if (is_array($oss)) { |
268
|
|
|
$oss = array_map(function ($os) { |
269
|
|
|
return $this->getValue($os); |
270
|
|
|
}, $oss); |
271
|
|
|
} else { |
272
|
|
|
$oss = array($oss); |
273
|
|
|
} |
274
|
|
|
$currentOS = $this->platform->getOS(); |
275
|
|
|
if (!in_array($currentOS, $oss)) { |
276
|
|
|
$missing[] = (string)$this->l->t('Following platforms are supported: %s', join(', ', $oss)); |
277
|
|
|
} |
278
|
|
|
return $missing; |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* @param array $dependencies |
283
|
|
|
* @param array $appInfo |
284
|
|
|
* @return array |
285
|
|
|
*/ |
286
|
|
|
private function analyzeOC(array $dependencies, array $appInfo) { |
287
|
|
|
$missing = []; |
288
|
|
|
$minVersion = null; |
289
|
|
|
if (isset($dependencies['owncloud']['@attributes']['min-version'])) { |
290
|
|
|
$minVersion = $dependencies['owncloud']['@attributes']['min-version']; |
291
|
|
|
} elseif (isset($appInfo['requiremin'])) { |
292
|
|
|
$minVersion = $appInfo['requiremin']; |
293
|
|
|
} elseif (isset($appInfo['require'])) { |
294
|
|
|
$minVersion = $appInfo['require']; |
295
|
|
|
} |
296
|
|
|
$maxVersion = null; |
297
|
|
|
if (isset($dependencies['owncloud']['@attributes']['max-version'])) { |
298
|
|
|
$maxVersion = $dependencies['owncloud']['@attributes']['max-version']; |
299
|
|
|
} elseif (isset($appInfo['requiremax'])) { |
300
|
|
|
$maxVersion = $appInfo['requiremax']; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
if (!is_null($minVersion)) { |
304
|
|
|
if ($this->compareSmaller($this->platform->getOcVersion(), $minVersion)) { |
305
|
|
|
$missing[] = (string)$this->l->t('ownCloud %s or higher is required.', $minVersion); |
306
|
|
|
} |
307
|
|
|
} |
308
|
|
|
if (!is_null($maxVersion)) { |
309
|
|
|
if ($this->compareBigger($this->platform->getOcVersion(), $maxVersion)) { |
310
|
|
|
$missing[] = (string)$this->l->t('ownCloud %s or lower is required.', $maxVersion); |
311
|
|
|
} |
312
|
|
|
} |
313
|
|
|
return $missing; |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* @param $element |
318
|
|
|
* @return mixed |
319
|
|
|
*/ |
320
|
|
|
private function getValue($element) { |
321
|
|
|
if (isset($element['@value'])) |
322
|
|
|
return $element['@value']; |
323
|
|
|
return (string)$element; |
324
|
|
|
} |
325
|
|
|
} |
326
|
|
|
|
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.