Completed
Pull Request — master (#148)
by
unknown
03:13
created

Bowerphp::installPackage()   C

Complexity

Conditions 10
Paths 35

Size

Total Lines 56
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 11
Bugs 5 Features 2
Metric Value
c 11
b 5
f 2
dl 0
loc 56
rs 6.7741
cc 10
eloc 35
nc 35
nop 3

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
/*
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 70 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(
46
        ConfigInterface $config,
47
        Filesystem $filesystem,
48
        Client $githubClient,
49
        RepositoryInterface $repository,
50
        BowerphpConsoleOutput $output
51
    ) {
52
        $this->config = $config;
53
        $this->filesystem = $filesystem;
54
        $this->githubClient = $githubClient;
55
        $this->repository = $repository;
56
        $this->output = $output;
57
    }
58
59
    /**
60
     * Init bower.json
61
     *
62
     * @param array $params
63
     */
64
    public function init(array $params)
65
    {
66
        if ($this->config->bowerFileExists()) {
67
            $bowerJson = $this->config->getBowerFileContent();
68
            $this->config->setSaveToBowerJsonFile(true);
69
            $this->config->updateBowerJsonFile2($bowerJson, $params);
70
        } else {
71
            $this->config->initBowerJsonFile($params);
72
        }
73
    }
74
75
    /**
76
     * Install a single package
77
     *
78
     * @param PackageInterface   $package
79
     * @param InstallerInterface $installer
80
     * @param bool               $isDependency
81
     */
82
    public function installPackage(PackageInterface $package, InstallerInterface $installer, $isDependency = false)
83
    {
84
        if (strpos($package->getName(), 'github') !== false) {
85
            // install from a github endpoint
86
            $name = basename($package->getName(), '.git');
87
            $repoUrl = $package->getName();
88
            $package = new Package($name, $package->getRequiredVersion());
89
            $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
90
            $package->setRepository($this->repository);
91
            $packageTag = $this->repository->findPackage($package->getRequiredVersion());
92
            if (is_null($packageTag)) {
93
                throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
94
            }
95
        } else {
96
            $packageTag = $this->getPackageTag($package, true);
97
            $package->setRepository($this->repository);
98
        }
99
100
        $package->setVersion($packageTag);
101
102
        $this->updateBowerFile($package, $isDependency);
103
104
        // if package is already installed, match current version with latest available version
105
        if ($this->isPackageInstalled($package)) {
106
            $packageBower = $this->config->getPackageBowerFileContent($package);
107
            if(isset($packageBower['version']) && $packageTag == $packageBower['version']) {
108
                // if version is fully matching, there's no need to install
109
                return;
110
            }
111
        }
112
113
        $this->output->writelnInfoPackage($package);
114
115
        $this->output->writelnInstalledPackage($package);
116
117
        $this->cachePackage($package);
118
119
        $installer->install($package);
120
121
        $overrides = $this->config->getOverrideFor($package->getName());
122
        if (array_key_exists('dependencies', $overrides)) {
123
            $dependencies = $overrides['dependencies'];
124
        } else {
125
            $dependencies = $package->getRequires();
126
        }
127
        if (!empty($dependencies)) {
128
            foreach ($dependencies as $name => $version) {
129
                $depPackage = new Package($name, $version);
130
                if (!$this->isPackageInstalled($depPackage)) {
131
                    $this->installPackage($depPackage, $installer, true);
132
                } else {
133
                    $this->updatePackage($depPackage, $installer);
134
                }
135
            }
136
        }
137
    }
138
139
    /**
140
     * Install all dependencies
141
     *
142
     * @param InstallerInterface $installer
143
     */
144
    public function installDependencies(InstallerInterface $installer)
145
    {
146
        $decode = $this->config->getBowerFileContent();
147
        if (!empty($decode['dependencies'])) {
148
            foreach ($decode['dependencies'] as $name => $requiredVersion) {
149
                if (strpos($requiredVersion, 'github') !== false) {
150
                    list($name, $requiredVersion) = explode('#', $requiredVersion);
151
                }
152
                $package = new Package($name, $requiredVersion);
153
                $this->installPackage($package, $installer, true);
154
            }
155
        }
156
    }
157
158
    /**
159
     * Update a single package
160
     *
161
     * @param PackageInterface   $package
162
     * @param InstallerInterface $installer
163
     */
164
    public function updatePackage(PackageInterface $package, InstallerInterface $installer)
0 ignored issues
show
Complexity introduced by
This operation has 1280 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...
165
    {
166
        if (!$this->isPackageInstalled($package)) {
167
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
168
        }
169
        if (is_null($package->getRequiredVersion())) {
170
            $decode = $this->config->getBowerFileContent();
171
            if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) {
172
                throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName()));
173
            }
174
            $package->setRequiredVersion($decode['dependencies'][$package->getName()]);
175
        }
176
177
        $bower = $this->config->getPackageBowerFileContent($package);
178
        $package->setInfo($bower);
179
        if(isset($bower['version'])){
180
            $package->setVersion($bower['version']);
181
        }
182
        $package->setRequires(isset($bower['dependencies']) ? $bower['dependencies'] : null);
183
184
        $packageTag = $this->getPackageTag($package);
185
        $package->setRepository($this->repository);
186
        if ($packageTag == $package->getVersion()) {
187
            // if version is fully matching, there's no need to update
188
            return;
189
        }
190
        $package->setVersion($packageTag);
191
192
        $this->output->writelnUpdatingPackage($package);
193
194
        $this->cachePackage($package);
195
196
        $installer->update($package);
197
198
        $overrides = $this->config->getOverrideFor($package->getName());
199
        if (array_key_exists('dependencies', $overrides)) {
200
            $dependencies = $overrides['dependencies'];
201
        } else {
202
            $dependencies = $package->getRequires();
203
        }
204
        if (!empty($dependencies)) {
205
            foreach ($dependencies as $name => $requiredVersion) {
206
                $depPackage = new Package($name, $requiredVersion);
207
                if (!$this->isPackageInstalled($depPackage)) {
208
                    $this->installPackage($depPackage, $installer, true);
209
                } else {
210
                    $this->updatePackage($depPackage, $installer);
211
                }
212
            }
213
        }
214
    }
215
216
    /**
217
     * Update all dependencies
218
     *
219
     * @param InstallerInterface $installer
220
     */
221
    public function updatePackages(InstallerInterface $installer)
222
    {
223
        $decode = $this->config->getBowerFileContent();
224
        if (!empty($decode['dependencies'])) {
225
            foreach ($decode['dependencies'] as $packageName => $requiredVersion) {
226
                $this->updatePackage(new Package($packageName, $requiredVersion), $installer);
227
            }
228
        }
229
    }
230
231
    /**
232
     * @param  PackageInterface $package
233
     * @param  string           $info
234
     * @return mixed
235
     */
236
    public function getPackageInfo(PackageInterface $package, $info = 'url')
237
    {
238
        $decode = $this->lookupPackage($package);
239
240
        $this->repository->setHttpClient($this->githubClient);
241
242
        if ($info == 'url') {
243
            $this->repository->setUrl($decode['url'], false);
244
245
            return $this->repository->getUrl();
246
        }
247
248
        if ($info == 'versions') {
249
            $tags = $this->repository->getTags();
250
            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...
251
                return version_compare($b, $a);
252
            });
253
254
            return $tags;
255
        }
256
257
        throw new RuntimeException(sprintf('Unsupported info option "%s".', $info));
258
    }
259
260
    /**
261
     * @param  string $name
262
     * @return array
263
     */
264
    public function lookupPackage($package)
265
    {
266
        return $this->findPackage($package);
267
    }
268
269
    /**
270
     * @param  PackageInterface $package
271
     * @return string
272
     */
273
    public function getPackageBowerFile(PackageInterface $package)
274
    {
275
        $this->repository->setHttpClient($this->githubClient);
276
        $lookupPackage = $this->lookupPackage($package);
277
        $this->repository->setUrl($lookupPackage['url'], false);
278
        $tag = $this->repository->findPackage($package->getRequiredVersion());
279
280
        return $this->repository->getBower($tag, true, $lookupPackage['url']);
281
    }
282
283
    /**
284
     * Uninstall a single package
285
     *
286
     * @param PackageInterface   $package
287
     * @param InstallerInterface $installer
288
     */
289
    public function uninstallPackage(PackageInterface $package, InstallerInterface $installer)
290
    {
291
        if (!$this->isPackageInstalled($package)) {
292
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
293
        }
294
        $installer->uninstall($package);
295
    }
296
297
    /**
298
     * Search packages by name
299
     *
300
     * @param  string $name
301
     * @return array
302
     */
303
    public function searchPackages($name)
304
    {
305
        try {
306
            $url = $this->config->getBasePackagesUrl() . 'search/' . $name;
307
            $response = $this->githubClient->getHttpClient()->get($url);
308
309
            return json_decode($response->getBody(true), true);
310
        } catch (RequestException $e) {
311
            throw new RuntimeException(sprintf('Cannot get package list from %s.', str_replace('/packages/', '', $this->config->getBasePackagesUrl())));
312
        }
313
    }
314
315
    /**
316
     * Get a list of installed packages
317
     *
318
     * @param  InstallerInterface $installer
319
     * @param  Finder             $finder
320
     * @return array
321
     */
322
    public function getInstalledPackages(InstallerInterface $installer, Finder $finder)
323
    {
324
        return $installer->getInstalled($finder);
325
    }
326
327
    /**
328
     * Check if package is installed
329
     *
330
     * @param  PackageInterface $package
331
     * @return bool
332
     */
333
    public function isPackageInstalled(PackageInterface $package)
334
    {
335
        return $this->filesystem->exists($this->config->getInstallDir() . '/' . $package->getName() . '/.bower.json');
336
    }
337
338
    /**
339
     * {@inheritdoc}
340
     */
341
    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...
342
    {
343
        if ($checkInstall && !$this->isPackageInstalled($package)) {
344
            return false;
345
        }
346
        try {
347
            $bower = $this->config->getBowerFileContent();
348
        } catch (RuntimeException $e) { // no bower.json file, package is extraneous
349
350
            return true;
351
        }
352
        if (!isset($bower['dependencies'])) {
353
            return true;
354
        }
355
        // package is a direct dependencies
356
        if (isset($bower['dependencies'][$package->getName()])) {
357
            return false;
358
        }
359
        // look for dependencies of dependencies
360
        foreach ($bower['dependencies'] as $name => $version) {
361
            $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name . '/.bower.json');
362
            $depBower = json_decode($dotBowerJson, true);
363
            if (isset($depBower['dependencies'][$package->getName()])) {
364
                return false;
365
            }
366
            // look for dependencies of dependencies of dependencies
367
            if (isset($depBower['dependencies'])) {
368
                foreach ($depBower['dependencies'] as $name1 => $version1) {
369
                    $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name1 . '/.bower.json');
370
                    $depDepBower = json_decode($dotBowerJson, true);
371
                    if (isset($depDepBower['dependencies'][$package->getName()])) {
372
                        return false;
373
                    }
374
                }
375
            }
376
        }
377
378
        return true;
379
    }
380
381
    /**
382
     * @param  array $params
383
     * @return array
384
     */
385
    protected function createAClearBowerFile(array $params)
386
    {
387
        $authors = ['Beelab <[email protected]>'];
388
        if (!empty($params['author'])) {
389
            $authors[] = $params['author'];
390
        }
391
        $structure = [
392
            'name'         => $params['name'],
393
            'authors'      => $authors,
394
            'private'      => true,
395
            'dependencies' => new \StdClass(),
396
        ];
397
398
        return $structure;
399
    }
400
401
    /**
402
     * @param  PackageInterface $package
403
     * @param  bool             $setInfo
404
     * @return string
405
     */
406
    protected function getPackageTag(PackageInterface $package, $setInfo = false)
407
    {
408
        $decode = $this->findPackage($package);
409
        // open package repository
410
        $repoUrl = $decode['url'];
411
        $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
412
        $packageTag = $this->repository->findPackage($package->getRequiredVersion());
413
        if (is_null($packageTag)) {
414
            throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
415
        }
416
        $bowerJson = $this->repository->getBower($packageTag);
417
        $bower = json_decode($bowerJson, true);
418
        if (!is_array($bower)) {
419
            throw new RuntimeException(sprintf('Invalid bower.json found in package %s: %s.', $package->getName(), $bowerJson));
420
        }
421
        if ($setInfo) {
422
            $package->setInfo($bower);
423
        }
424
425
        return $packageTag;
426
    }
427
428
    /**
429
     * @param  string $name
430
     * @return array
431
     */
432
    protected function findPackage($package)
433
    {
434
        $name = $package->getName();
435
        
436
        if( $url = $package->getRequiredVersionUrl() ){
437
            return ['name'=>$name,'url'=>$url];
438
        }
439
        
440
        try {
441
            $response = $this->githubClient->getHttpClient()->get($this->config->getBasePackagesUrl().urlencode($name));
442
        } catch (RuntimeException $e) {
443
            throw new RuntimeException(sprintf('Cannot fetch registry info for package %s from search registry (%s).', $name, $e->getMessage()));
444
        }
445
        $packageInfo = json_decode($response->getBody(true), true);
446
        if (!is_array($packageInfo) || empty($packageInfo['url'])) {
447
            throw new RuntimeException(sprintf('Registry info for package %s has malformed json or is missing "url".', $name));
448
        }
449
450
        return $packageInfo;
451
    }
452
453
    /**
454
     * @param PackageInterface $package
455
     */
456
    private function cachePackage(PackageInterface $package)
457
    {
458
        // get release archive from repository
459
        $file = $this->repository->getRelease();
460
461
        $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
462
        $this->filesystem->write($tmpFileName, $file);
463
    }
464
465
    /**
466
     * @param PackageInterface $package
467
     * @param bool             $isDependency
468
     */
469
    private function updateBowerFile(PackageInterface $package, $isDependency = false)
470
    {
471
        if ($this->config->isSaveToBowerJsonFile() && !$isDependency) {
472
            try {
473
                $this->config->updateBowerJsonFile($package);
474
            } catch (RuntimeException $e) {
475
                $this->output->writelnNoBowerJsonFile();
476
            }
477
        }
478
    }
479
}
480