Total Complexity | 67 |
Total Lines | 344 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like DependencyAnalyzer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use DependencyAnalyzer, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
35 | class DependencyAnalyzer { |
||
36 | |||
37 | /** @var Platform */ |
||
38 | private $platform; |
||
39 | /** @var \OCP\IL10N */ |
||
40 | private $l; |
||
41 | /** @var array */ |
||
42 | private $appInfo; |
||
43 | |||
44 | /** |
||
45 | * @param Platform $platform |
||
46 | * @param \OCP\IL10N $l |
||
47 | */ |
||
48 | public function __construct(Platform $platform, IL10N $l) { |
||
49 | $this->platform = $platform; |
||
50 | $this->l = $l; |
||
51 | } |
||
52 | |||
53 | /** |
||
54 | * @param array $app |
||
55 | * @returns array of missing dependencies |
||
56 | */ |
||
57 | public function analyze(array $app, bool $ignoreMax = false) { |
||
58 | $this->appInfo = $app; |
||
59 | if (isset($app['dependencies'])) { |
||
60 | $dependencies = $app['dependencies']; |
||
61 | } else { |
||
62 | $dependencies = []; |
||
63 | } |
||
64 | |||
65 | return array_merge( |
||
66 | $this->analyzePhpVersion($dependencies), |
||
67 | $this->analyzeDatabases($dependencies), |
||
68 | $this->analyzeCommands($dependencies), |
||
69 | $this->analyzeLibraries($dependencies), |
||
70 | $this->analyzeOS($dependencies), |
||
71 | $this->analyzeOC($dependencies, $app, $ignoreMax) |
||
72 | ); |
||
73 | } |
||
74 | |||
75 | public function isMarkedCompatible(array $app): bool { |
||
76 | if (isset($app['dependencies'])) { |
||
77 | $dependencies = $app['dependencies']; |
||
78 | } else { |
||
79 | $dependencies = []; |
||
80 | } |
||
81 | |||
82 | $maxVersion = $this->getMaxVersion($dependencies, $app); |
||
83 | if ($maxVersion === null) { |
||
84 | return true; |
||
85 | } |
||
86 | return !$this->compareBigger($this->platform->getOcVersion(), $maxVersion); |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Truncates both versions to the lowest common version, e.g. |
||
91 | * 5.1.2.3 and 5.1 will be turned into 5.1 and 5.1, |
||
92 | * 5.2.6.5 and 5.1 will be turned into 5.2 and 5.1 |
||
93 | * @param string $first |
||
94 | * @param string $second |
||
95 | * @return string[] first element is the first version, second element is the |
||
96 | * second version |
||
97 | */ |
||
98 | private function normalizeVersions($first, $second) { |
||
99 | $first = explode('.', $first); |
||
100 | $second = explode('.', $second); |
||
101 | |||
102 | // get both arrays to the same minimum size |
||
103 | $length = min(count($second), count($first)); |
||
104 | $first = array_slice($first, 0, $length); |
||
105 | $second = array_slice($second, 0, $length); |
||
106 | |||
107 | return [implode('.', $first), implode('.', $second)]; |
||
108 | } |
||
109 | |||
110 | /** |
||
111 | * Parameters will be normalized and then passed into version_compare |
||
112 | * in the same order they are specified in the method header |
||
113 | * @param string $first |
||
114 | * @param string $second |
||
115 | * @param string $operator |
||
116 | * @return bool result similar to version_compare |
||
117 | */ |
||
118 | private function compare($first, $second, $operator) { |
||
119 | // we can't normalize versions if one of the given parameters is not a |
||
120 | // version string but null. In case one parameter is null normalization |
||
121 | // will therefore be skipped |
||
122 | if ($first !== null && $second !== null) { |
||
|
|||
123 | list($first, $second) = $this->normalizeVersions($first, $second); |
||
124 | } |
||
125 | |||
126 | return version_compare($first, $second, $operator); |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Checks if a version is bigger than another version |
||
131 | * @param string $first |
||
132 | * @param string $second |
||
133 | * @return bool true if the first version is bigger than the second |
||
134 | */ |
||
135 | private function compareBigger($first, $second) { |
||
136 | return $this->compare($first, $second, '>'); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Checks if a version is smaller than another version |
||
141 | * @param string $first |
||
142 | * @param string $second |
||
143 | * @return bool true if the first version is smaller than the second |
||
144 | */ |
||
145 | private function compareSmaller($first, $second) { |
||
146 | return $this->compare($first, $second, '<'); |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param array $dependencies |
||
151 | * @return array |
||
152 | */ |
||
153 | private function analyzePhpVersion(array $dependencies) { |
||
154 | $missing = []; |
||
155 | if (isset($dependencies['php']['@attributes']['min-version'])) { |
||
156 | $minVersion = $dependencies['php']['@attributes']['min-version']; |
||
157 | if ($this->compareSmaller($this->platform->getPhpVersion(), $minVersion)) { |
||
158 | $missing[] = (string)$this->l->t('PHP %s or higher is required.', [$minVersion]); |
||
159 | } |
||
160 | } |
||
161 | if (isset($dependencies['php']['@attributes']['max-version'])) { |
||
162 | $maxVersion = $dependencies['php']['@attributes']['max-version']; |
||
163 | if ($this->compareBigger($this->platform->getPhpVersion(), $maxVersion)) { |
||
164 | $missing[] = (string)$this->l->t('PHP with a version lower than %s is required.', [$maxVersion]); |
||
165 | } |
||
166 | } |
||
167 | if (isset($dependencies['php']['@attributes']['min-int-size'])) { |
||
168 | $intSize = $dependencies['php']['@attributes']['min-int-size']; |
||
169 | if ($intSize > $this->platform->getIntSize()*8) { |
||
170 | $missing[] = (string)$this->l->t('%sbit or higher PHP required.', [$intSize]); |
||
171 | } |
||
172 | } |
||
173 | return $missing; |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * @param array $dependencies |
||
178 | * @return array |
||
179 | */ |
||
180 | private function analyzeDatabases(array $dependencies) { |
||
181 | $missing = []; |
||
182 | if (!isset($dependencies['database'])) { |
||
183 | return $missing; |
||
184 | } |
||
185 | |||
186 | $supportedDatabases = $dependencies['database']; |
||
187 | if (empty($supportedDatabases)) { |
||
188 | return $missing; |
||
189 | } |
||
190 | if (!is_array($supportedDatabases)) { |
||
191 | $supportedDatabases = array($supportedDatabases); |
||
192 | } |
||
193 | $supportedDatabases = array_map(function ($db) { |
||
194 | return $this->getValue($db); |
||
195 | }, $supportedDatabases); |
||
196 | $currentDatabase = $this->platform->getDatabase(); |
||
197 | if (!in_array($currentDatabase, $supportedDatabases)) { |
||
198 | $missing[] = (string)$this->l->t('Following databases are supported: %s', [implode(', ', $supportedDatabases)]); |
||
199 | } |
||
200 | return $missing; |
||
201 | } |
||
202 | |||
203 | /** |
||
204 | * @param array $dependencies |
||
205 | * @return array |
||
206 | */ |
||
207 | private function analyzeCommands(array $dependencies) { |
||
208 | $missing = []; |
||
209 | if (!isset($dependencies['command'])) { |
||
210 | return $missing; |
||
211 | } |
||
212 | |||
213 | $commands = $dependencies['command']; |
||
214 | if (!is_array($commands)) { |
||
215 | $commands = array($commands); |
||
216 | } |
||
217 | if (isset($commands['@value'])) { |
||
218 | $commands = [$commands]; |
||
219 | } |
||
220 | $os = $this->platform->getOS(); |
||
221 | foreach ($commands as $command) { |
||
222 | if (isset($command['@attributes']['os']) && $command['@attributes']['os'] !== $os) { |
||
223 | continue; |
||
224 | } |
||
225 | $commandName = $this->getValue($command); |
||
226 | if (!$this->platform->isCommandKnown($commandName)) { |
||
227 | $missing[] = (string)$this->l->t('The command line tool %s could not be found', [$commandName]); |
||
228 | } |
||
229 | } |
||
230 | return $missing; |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * @param array $dependencies |
||
235 | * @return array |
||
236 | */ |
||
237 | private function analyzeLibraries(array $dependencies) { |
||
238 | $missing = []; |
||
239 | if (!isset($dependencies['lib'])) { |
||
240 | return $missing; |
||
241 | } |
||
242 | |||
243 | $libs = $dependencies['lib']; |
||
244 | if (!is_array($libs)) { |
||
245 | $libs = array($libs); |
||
246 | } |
||
247 | if (isset($libs['@value'])) { |
||
248 | $libs = [$libs]; |
||
249 | } |
||
250 | foreach ($libs as $lib) { |
||
251 | $libName = $this->getValue($lib); |
||
252 | $libVersion = $this->platform->getLibraryVersion($libName); |
||
253 | if (is_null($libVersion)) { |
||
254 | $missing[] = $this->l->t('The library %s is not available.', [$libName]); |
||
255 | continue; |
||
256 | } |
||
257 | |||
258 | if (is_array($lib)) { |
||
259 | if (isset($lib['@attributes']['min-version'])) { |
||
260 | $minVersion = $lib['@attributes']['min-version']; |
||
261 | if ($this->compareSmaller($libVersion, $minVersion)) { |
||
262 | $missing[] = $this->l->t('Library %1$s with a version higher than %2$s is required - available version %3$s.', |
||
263 | [$libName, $minVersion, $libVersion]); |
||
264 | } |
||
265 | } |
||
266 | if (isset($lib['@attributes']['max-version'])) { |
||
267 | $maxVersion = $lib['@attributes']['max-version']; |
||
268 | if ($this->compareBigger($libVersion, $maxVersion)) { |
||
269 | $missing[] = $this->l->t('Library %1$s with a version lower than %2$s is required - available version %3$s.', |
||
270 | [$libName, $maxVersion, $libVersion]); |
||
271 | } |
||
272 | } |
||
273 | } |
||
274 | } |
||
275 | return $missing; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * @param array $dependencies |
||
280 | * @return array |
||
281 | */ |
||
282 | private function analyzeOS(array $dependencies) { |
||
283 | $missing = []; |
||
284 | if (!isset($dependencies['os'])) { |
||
285 | return $missing; |
||
286 | } |
||
287 | |||
288 | $oss = $dependencies['os']; |
||
289 | if (empty($oss)) { |
||
290 | return $missing; |
||
291 | } |
||
292 | if (is_array($oss)) { |
||
293 | $oss = array_map(function ($os) { |
||
294 | return $this->getValue($os); |
||
295 | }, $oss); |
||
296 | } else { |
||
297 | $oss = array($oss); |
||
298 | } |
||
299 | $currentOS = $this->platform->getOS(); |
||
300 | if (!in_array($currentOS, $oss)) { |
||
301 | $missing[] = (string)$this->l->t('Following platforms are supported: %s', [implode(', ', $oss)]); |
||
302 | } |
||
303 | return $missing; |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * @param array $dependencies |
||
308 | * @param array $appInfo |
||
309 | * @return array |
||
310 | */ |
||
311 | private function analyzeOC(array $dependencies, array $appInfo, bool $ignoreMax) { |
||
312 | $missing = []; |
||
313 | $minVersion = null; |
||
314 | if (isset($dependencies['nextcloud']['@attributes']['min-version'])) { |
||
315 | $minVersion = $dependencies['nextcloud']['@attributes']['min-version']; |
||
316 | } elseif (isset($dependencies['owncloud']['@attributes']['min-version'])) { |
||
317 | $minVersion = $dependencies['owncloud']['@attributes']['min-version']; |
||
318 | } elseif (isset($appInfo['requiremin'])) { |
||
319 | $minVersion = $appInfo['requiremin']; |
||
320 | } elseif (isset($appInfo['require'])) { |
||
321 | $minVersion = $appInfo['require']; |
||
322 | } |
||
323 | $maxVersion = $this->getMaxVersion($dependencies, $appInfo); |
||
324 | |||
325 | if (!is_null($minVersion)) { |
||
326 | if ($this->compareSmaller($this->platform->getOcVersion(), $minVersion)) { |
||
327 | $missing[] = (string)$this->l->t('Server version %s or higher is required.', [$this->toVisibleVersion($minVersion)]); |
||
328 | } |
||
329 | } |
||
330 | if (!$ignoreMax && !is_null($maxVersion)) { |
||
331 | if ($this->compareBigger($this->platform->getOcVersion(), $maxVersion)) { |
||
332 | $missing[] = (string)$this->l->t('Server version %s or lower is required.', [$this->toVisibleVersion($maxVersion)]); |
||
333 | } |
||
334 | } |
||
335 | return $missing; |
||
336 | } |
||
337 | |||
338 | private function getMaxVersion(array $dependencies, array $appInfo): ?string { |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * Map the internal version number to the Nextcloud version |
||
354 | * |
||
355 | * @param string $version |
||
356 | * @return string |
||
357 | */ |
||
358 | protected function toVisibleVersion($version) { |
||
359 | switch ($version) { |
||
360 | case '9.1': |
||
361 | return '10'; |
||
362 | default: |
||
363 | if (strpos($version, '9.1.') === 0) { |
||
364 | $version = '10.0.' . substr($version, 4); |
||
365 | } |
||
366 | return $version; |
||
367 | } |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * @param $element |
||
372 | * @return mixed |
||
373 | */ |
||
374 | private function getValue($element) { |
||
379 | } |
||
380 | } |
||
381 |