Completed
Push — master ( 1b9e01...ecb672 )
by Massimiliano
06:23
created

Bowerphp::isPackageExtraneous()   C

Complexity

Conditions 11
Paths 10

Size

Total Lines 39
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 4 Features 2
Metric Value
c 6
b 4
f 2
dl 0
loc 39
rs 5.2653
cc 11
eloc 23
nc 10
nop 2

How to fix   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 file is part of Bowerphp.
5
 *
6
 * (c) Massimiliano Arione <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Bowerphp;
13
14
use Bowerphp\Config\ConfigInterface;
15
use Bowerphp\Installer\InstallerInterface;
16
use Bowerphp\Output\BowerphpConsoleOutput;
17
use Bowerphp\Package\Package;
18
use Bowerphp\Package\PackageInterface;
19
use Bowerphp\Repository\RepositoryInterface;
20
use Bowerphp\Util\Filesystem;
21
use Github\Client;
22
use Guzzle\Http\Exception\RequestException;
23
use InvalidArgumentException;
24
use RuntimeException;
25
use Symfony\Component\Finder\Finder;
26
27
/**
28
 * Main class
29
 */
30
class Bowerphp
0 ignored issues
show
Complexity introduced by
The class Bowerphp has a coupling between objects value of 13. Consider to reduce the number of dependencies under 13.
Loading history...
Complexity introduced by
This class has a complexity of 67 which exceeds the configured maximum of 50.

The class complexity is the sum of the complexity of all methods. A very high value is usually an indication that your class does not follow the single reponsibility principle and does more than one job.

Some resources for further reading:

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

Loading history...
31
{
32
    protected $config;
33
    protected $filesystem;
34
    protected $githubClient;
35
    protected $repository;
36
    protected $output;
37
38
    /**
39
     * @param ConfigInterface       $config
40
     * @param Filesystem            $filesystem
41
     * @param Client                $githubClient
42
     * @param RepositoryInterface   $repository
43
     * @param BowerphpConsoleOutput $output
44
     */
45
    public function __construct(ConfigInterface $config, Filesystem $filesystem, Client $githubClient, RepositoryInterface $repository, BowerphpConsoleOutput $output)
46
    {
47
        $this->config = $config;
48
        $this->filesystem = $filesystem;
49
        $this->githubClient = $githubClient;
50
        $this->repository = $repository;
51
        $this->output = $output;
52
    }
53
54
    /**
55
     * Init bower.json
56
     *
57
     * @param array $params
58
     */
59
    public function init(array $params)
60
    {
61
        if ($this->config->bowerFileExists()) {
62
            $bowerJson = $this->config->getBowerFileContent();
63
            $this->config->setSaveToBowerJsonFile(true);
64
            $this->config->updateBowerJsonFile2($bowerJson, $params);
65
        } else {
66
            $this->config->initBowerJsonFile($params);
67
        }
68
    }
69
70
    /**
71
     * Install a single package
72
     *
73
     * @param PackageInterface   $package
74
     * @param InstallerInterface $installer
75
     * @param bool               $isDependency
76
     */
77
    public function installPackage(PackageInterface $package, InstallerInterface $installer, $isDependency = false)
78
    {
79
        if (strpos($package->getName(), 'github') !== false) {
80
            // install from a github endpoint
81
            $name = basename($package->getName(), '.git');
82
            $repoUrl = $package->getName();
83
            $package = new Package($name, $package->getRequiredVersion());
84
            $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
85
            $package->setRepository($this->repository);
86
            $packageTag = $this->repository->findPackage($package->getRequiredVersion());
87
            if (is_null($packageTag)) {
88
                throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
89
            }
90
        } else {
91
            $packageTag = $this->getPackageTag($package, true);
92
            $package->setRepository($this->repository);
93
        }
94
95
        $package->setVersion($packageTag);
96
97
        $this->updateBowerFile($package, $isDependency);
98
99
        // if package is already installed, match current version with latest available version
100
        if ($this->isPackageInstalled($package)) {
101
            $packageBower = $this->config->getPackageBowerFileContent($package);
102
            if ($packageTag == $packageBower['version']) {
103
                // if version is fully matching, there's no need to install
104
                return;
105
            }
106
        }
107
108
        $this->output->writelnInfoPackage($package);
109
110
        $this->output->writelnInstalledPackage($package);
111
112
        $this->cachePackage($package);
113
114
        $installer->install($package);
115
116
        $overrides = $this->config->getOverrideFor($package->getName());
117
        if (array_key_exists('dependencies', $overrides)) {
118
            $dependencies = $overrides['dependencies'];
119
        } else {
120
            $dependencies = $package->getRequires();
121
        }
122
        if (!empty($dependencies)) {
123
            foreach ($dependencies as $name => $version) {
124
                $depPackage = new Package($name, $version);
125
                if (!$this->isPackageInstalled($depPackage)) {
126
                    $this->installPackage($depPackage, $installer, true);
127
                } else {
128
                    $this->updatePackage($depPackage, $installer);
129
                }
130
            }
131
        }
132
    }
133
134
    /**
135
     * Install all dependencies
136
     *
137
     * @param InstallerInterface $installer
138
     */
139
    public function installDependencies(InstallerInterface $installer)
140
    {
141
        $decode = $this->config->getBowerFileContent();
142
        if (!empty($decode['dependencies'])) {
143
            foreach ($decode['dependencies'] as $name => $requiredVersion) {
144
                if (strpos($requiredVersion, 'github') !== false) {
145
                    list($name, $requiredVersion) = explode('#', $requiredVersion);
146
                }
147
                $package = new Package($name, $requiredVersion);
148
                $this->installPackage($package, $installer, true);
149
            }
150
        }
151
    }
152
153
    /**
154
     * Update a single package
155
     *
156
     * @param PackageInterface   $package
157
     * @param InstallerInterface $installer
158
     */
159
    public function updatePackage(PackageInterface $package, InstallerInterface $installer)
0 ignored issues
show
Complexity introduced by
This operation has 640 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
160
    {
161
        if (!$this->isPackageInstalled($package)) {
162
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
163
        }
164
        if (is_null($package->getRequiredVersion())) {
165
            $decode = $this->config->getBowerFileContent();
166
            if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) {
167
                throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName()));
168
            }
169
            $package->setRequiredVersion($decode['dependencies'][$package->getName()]);
170
        }
171
172
        $bower = $this->config->getPackageBowerFileContent($package);
173
        $package->setInfo($bower);
174
        $package->setVersion($bower['version']);
175
        $package->setRequires(isset($bower['dependencies']) ? $bower['dependencies'] : null);
176
177
        $packageTag = $this->getPackageTag($package);
178
        $package->setRepository($this->repository);
179
        if ($packageTag == $package->getVersion()) {
180
            // if version is fully matching, there's no need to update
181
            return;
182
        }
183
        $package->setVersion($packageTag);
184
185
        $this->output->writelnUpdatingPackage($package);
186
187
        $this->cachePackage($package);
188
189
        $installer->update($package);
190
191
        $overrides = $this->config->getOverrideFor($package->getName());
192
        if (array_key_exists('dependencies', $overrides)) {
193
            $dependencies = $overrides['dependencies'];
194
        } else {
195
            $dependencies = $package->getRequires();
196
        }
197
        if (!empty($dependencies)) {
198
            foreach ($dependencies as $name => $requiredVersion) {
199
                $depPackage = new Package($name, $requiredVersion);
200
                if (!$this->isPackageInstalled($depPackage)) {
201
                    $this->installPackage($depPackage, $installer, true);
202
                } else {
203
                    $this->updatePackage($depPackage, $installer);
204
                }
205
            }
206
        }
207
    }
208
209
    /**
210
     * Update all dependencies
211
     *
212
     * @param InstallerInterface $installer
213
     */
214
    public function updatePackages(InstallerInterface $installer)
215
    {
216
        $decode = $this->config->getBowerFileContent();
217
        if (!empty($decode['dependencies'])) {
218
            foreach ($decode['dependencies'] as $packageName => $requiredVersion) {
219
                $this->updatePackage(new Package($packageName, $requiredVersion), $installer);
220
            }
221
        }
222
    }
223
224
    /**
225
     * @param  PackageInterface $package
226
     * @param  string           $info
227
     * @return mixed
228
     */
229
    public function getPackageInfo(PackageInterface $package, $info = 'url')
230
    {
231
        $decode = $this->lookupPackage($package->getName());
232
233
        $this->repository->setHttpClient($this->githubClient);
234
235
        if ($info == 'url') {
236
            $this->repository->setUrl($decode['url'], false);
237
238
            return $this->repository->getUrl();
239
        }
240
241
        if ($info == 'versions') {
242
            $tags = $this->repository->getTags();
243
            usort($tags, function ($a, $b) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 2.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $b. Configured minimum length is 2.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
244
                return version_compare($b, $a);
245
            });
246
247
            return $tags;
248
        }
249
250
        throw new RuntimeException(sprintf('Unsupported info option "%s".', $info));
251
    }
252
253
    /**
254
     * @param  string $name
255
     * @return array
256
     */
257
    public function lookupPackage($name)
258
    {
259
        return $this->findPackage($name);
260
    }
261
262
    /**
263
     * @param  PackageInterface $package
264
     * @return string
265
     */
266
    public function getPackageBowerFile(PackageInterface $package)
267
    {
268
        $this->repository->setHttpClient($this->githubClient);
269
        $lookupPackage = $this->lookupPackage($package->getName());
270
        $this->repository->setUrl($lookupPackage['url'], false);
271
        $tag = $this->repository->findPackage($package->getRequiredVersion());
272
273
        return $this->repository->getBower($tag, true, $lookupPackage['url']);
274
    }
275
276
    /**
277
     * Uninstall a single package
278
     *
279
     * @param PackageInterface   $package
280
     * @param InstallerInterface $installer
281
     */
282
    public function uninstallPackage(PackageInterface $package, InstallerInterface $installer)
283
    {
284
        if (!$this->isPackageInstalled($package)) {
285
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
286
        }
287
        $installer->uninstall($package);
288
    }
289
290
    /**
291
     * Search packages by name
292
     *
293
     * @param  string $name
294
     * @return array
295
     */
296
    public function searchPackages($name)
297
    {
298
        try {
299
            $url = $this->config->getBasePackagesUrl() . 'search/' . $name;
300
            $response = $this->githubClient->getHttpClient()->get($url);
301
302
            return json_decode($response->getBody(true), true);
303
        } catch (RequestException $e) {
304
            throw new RuntimeException(sprintf('Cannot get package list from %s.', str_replace('/packages/', '', $this->config->getBasePackagesUrl())));
305
        }
306
    }
307
308
    /**
309
     * Get a list of installed packages
310
     *
311
     * @param  InstallerInterface $installer
312
     * @param  Finder             $finder
313
     * @return array
314
     */
315
    public function getInstalledPackages(InstallerInterface $installer, Finder $finder)
316
    {
317
        return $installer->getInstalled($finder);
318
    }
319
320
    /**
321
     * Check if package is installed
322
     *
323
     * @param  PackageInterface $package
324
     * @return bool
325
     */
326
    public function isPackageInstalled(PackageInterface $package)
327
    {
328
        return $this->filesystem->exists($this->config->getInstallDir() . '/' . $package->getName() . '/.bower.json');
329
    }
330
331
    /**
332
     * {@inheritdoc}
333
     */
334
    public function isPackageExtraneous(PackageInterface $package, $checkInstall = false)
0 ignored issues
show
Complexity introduced by
This operation has 216 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

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

Loading history...
335
    {
336
        if ($checkInstall && !$this->isPackageInstalled($package)) {
337
            return false;
338
        }
339
        try {
340
            $bower = $this->config->getBowerFileContent();
341
        } catch (RuntimeException $e) { // no bower.json file, package is extraneous
342
343
            return true;
344
        }
345
        if (!isset($bower['dependencies'])) {
346
            return true;
347
        }
348
        // package is a direct dependencies
349
        if (isset($bower['dependencies'][$package->getName()])) {
350
            return false;
351
        }
352
        // look for dependencies of dependencies
353
        foreach ($bower['dependencies'] as $name => $version) {
354
            $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name . '/.bower.json');
355
            $depBower = json_decode($dotBowerJson, true);
356
            if (isset($depBower['dependencies'][$package->getName()])) {
357
                return false;
358
            }
359
            // look for dependencies of dependencies of dependencies
360
            if (isset($depBower['dependencies'])) {
361
                foreach ($depBower['dependencies'] as $name1 => $version1) {
362
                    $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name1 . '/.bower.json');
363
                    $depDepBower = json_decode($dotBowerJson, true);
364
                    if (isset($depDepBower['dependencies'][$package->getName()])) {
365
                        return false;
366
                    }
367
                }
368
            }
369
        }
370
371
        return true;
372
    }
373
374
    /**
375
     * @param  array $params
376
     * @return array
377
     */
378
    protected function createAClearBowerFile(array $params)
379
    {
380
        $authors = array('Beelab <[email protected]>');
381
        if (!empty($params['author'])) {
382
            $authors[] = $params['author'];
383
        }
384
        $structure = array(
385
            'name'         => $params['name'],
386
            'authors'      => $authors,
387
            'private'      => true,
388
            'dependencies' => new \StdClass(),
389
        );
390
391
        return $structure;
392
    }
393
394
    /**
395
     * @param  PackageInterface $package
396
     * @param  bool             $setInfo
397
     * @return string
398
     */
399
    protected function getPackageTag(PackageInterface $package, $setInfo = false)
400
    {
401
        $decode = $this->findPackage($package->getName());
402
        // open package repository
403
        $repoUrl = $decode['url'];
404
        $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
405
        $packageTag = $this->repository->findPackage($package->getRequiredVersion());
406
        if (is_null($packageTag)) {
407
            throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
408
        }
409
        $bowerJson = $this->repository->getBower($packageTag);
410
        $bower = json_decode($bowerJson, true);
411
        if (!is_array($bower)) {
412
            throw new RuntimeException(sprintf('Invalid bower.json found in package %s: %s.', $package->getName(), $bowerJson));
413
        }
414
        if ($setInfo) {
415
            $package->setInfo($bower);
416
        }
417
418
        return $packageTag;
419
    }
420
421
    /**
422
     * @param  string $name
423
     * @return array
424
     */
425
    protected function findPackage($name)
426
    {
427
        try {
428
            $response = $this->githubClient->getHttpClient()->get($this->config->getBasePackagesUrl() . urlencode($name));
429
        } catch (RuntimeException $e) {
430
            throw new RuntimeException(sprintf('Cannot fetch registry info for package %s from search registry (%s).', $name, $e->getMessage()));
431
        }
432
        $packageInfo = json_decode($response->getBody(true), true);
433
        if (!is_array($packageInfo) || empty($packageInfo['url'])) {
434
            throw new RuntimeException(sprintf('Registry info for package %s has malformed json or is missing "url".', $name));
435
        }
436
437
        return $packageInfo;
438
    }
439
440
    /**
441
     * @param PackageInterface $package
442
     */
443
    private function cachePackage(PackageInterface $package)
444
    {
445
        // get release archive from repository
446
        $file = $this->repository->getRelease();
447
448
        $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
449
        $this->filesystem->write($tmpFileName, $file);
450
    }
451
452
    /**
453
     * @param PackageInterface $package
454
     * @param bool             $isDependency
455
     */
456
    private function updateBowerFile(PackageInterface $package, $isDependency = false)
457
    {
458
        if ($this->config->isSaveToBowerJsonFile() && !$isDependency) {
459
            try {
460
                $this->config->updateBowerJsonFile($package);
461
            } catch (RuntimeException $e) {
462
                $this->output->writelnNoBowerJsonFile();
463
            }
464
        }
465
    }
466
}
467