Completed
Pull Request — master (#148)
by
unknown
03:22
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 71 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
                try{
154
					$this->installPackage($package, $installer, true);
155
				}
156
				catch(\RuntimeException $e){
157
					throw new \RuntimeException($name.' '.$requiredVersion.' '.$e->getMessage());
158
				}
159
            }
160
        }
161
    }
162
163
    /**
164
     * Update a single package
165
     *
166
     * @param PackageInterface   $package
167
     * @param InstallerInterface $installer
168
     */
169
    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...
170
    {
171
        if (!$this->isPackageInstalled($package)) {
172
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
173
        }
174
        if (is_null($package->getRequiredVersion())) {
175
            $decode = $this->config->getBowerFileContent();
176
            if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) {
177
                throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName()));
178
            }
179
            $package->setRequiredVersion($decode['dependencies'][$package->getName()]);
180
        }
181
182
        $bower = $this->config->getPackageBowerFileContent($package);
183
        $package->setInfo($bower);
184
        if(isset($bower['version'])){
185
            $package->setVersion($bower['version']);
186
        }
187
        $package->setRequires(isset($bower['dependencies']) ? $bower['dependencies'] : null);
188
189
        $packageTag = $this->getPackageTag($package);
190
        $package->setRepository($this->repository);
191
        if ($packageTag == $package->getVersion()) {
192
            // if version is fully matching, there's no need to update
193
            return;
194
        }
195
        $package->setVersion($packageTag);
196
197
        $this->output->writelnUpdatingPackage($package);
198
199
        $this->cachePackage($package);
200
201
        $installer->update($package);
202
203
        $overrides = $this->config->getOverrideFor($package->getName());
204
        if (array_key_exists('dependencies', $overrides)) {
205
            $dependencies = $overrides['dependencies'];
206
        } else {
207
            $dependencies = $package->getRequires();
208
        }
209
        if (!empty($dependencies)) {
210
            foreach ($dependencies as $name => $requiredVersion) {
211
                $depPackage = new Package($name, $requiredVersion);
212
                if (!$this->isPackageInstalled($depPackage)) {
213
                    $this->installPackage($depPackage, $installer, true);
214
                } else {
215
                    $this->updatePackage($depPackage, $installer);
216
                }
217
            }
218
        }
219
    }
220
221
    /**
222
     * Update all dependencies
223
     *
224
     * @param InstallerInterface $installer
225
     */
226
    public function updatePackages(InstallerInterface $installer)
227
    {
228
        $decode = $this->config->getBowerFileContent();
229
        if (!empty($decode['dependencies'])) {
230
            foreach ($decode['dependencies'] as $packageName => $requiredVersion) {
231
                $this->updatePackage(new Package($packageName, $requiredVersion), $installer);
232
            }
233
        }
234
    }
235
236
    /**
237
     * @param  PackageInterface $package
238
     * @param  string           $info
239
     * @return mixed
240
     */
241
    public function getPackageInfo(PackageInterface $package, $info = 'url')
242
    {
243
        $decode = $this->lookupPackage($package);
244
245
        $this->repository->setHttpClient($this->githubClient);
246
247
        if ($info == 'url') {
248
            $this->repository->setUrl($decode['url'], false);
249
250
            return $this->repository->getUrl();
251
        }
252
253
        if ($info == 'versions') {
254
            $tags = $this->repository->getTags();
255
            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...
256
                return version_compare($b, $a);
257
            });
258
259
            return $tags;
260
        }
261
262
        throw new RuntimeException(sprintf('Unsupported info option "%s".', $info));
263
    }
264
265
    /**
266
     * @param  string $name
267
     * @return array
268
     */
269
    public function lookupPackage($package)
270
    {
271
        return $this->findPackage($package);
272
    }
273
274
    /**
275
     * @param  PackageInterface $package
276
     * @return string
277
     */
278
    public function getPackageBowerFile(PackageInterface $package)
279
    {
280
        $this->repository->setHttpClient($this->githubClient);
281
        $lookupPackage = $this->lookupPackage($package);
282
        $this->repository->setUrl($lookupPackage['url'], false);
283
        $tag = $this->repository->findPackage($package->getRequiredVersion());
284
285
        return $this->repository->getBower($tag, true, $lookupPackage['url']);
286
    }
287
288
    /**
289
     * Uninstall a single package
290
     *
291
     * @param PackageInterface   $package
292
     * @param InstallerInterface $installer
293
     */
294
    public function uninstallPackage(PackageInterface $package, InstallerInterface $installer)
295
    {
296
        if (!$this->isPackageInstalled($package)) {
297
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
298
        }
299
        $installer->uninstall($package);
300
    }
301
302
    /**
303
     * Search packages by name
304
     *
305
     * @param  string $name
306
     * @return array
307
     */
308
    public function searchPackages($name)
309
    {
310
        try {
311
            $url = $this->config->getBasePackagesUrl() . 'search/' . $name;
312
            $response = $this->githubClient->getHttpClient()->get($url);
313
314
            return json_decode($response->getBody(true), true);
315
        } catch (RequestException $e) {
316
            throw new RuntimeException(sprintf('Cannot get package list from %s.', str_replace('/packages/', '', $this->config->getBasePackagesUrl())));
317
        }
318
    }
319
320
    /**
321
     * Get a list of installed packages
322
     *
323
     * @param  InstallerInterface $installer
324
     * @param  Finder             $finder
325
     * @return array
326
     */
327
    public function getInstalledPackages(InstallerInterface $installer, Finder $finder)
328
    {
329
        return $installer->getInstalled($finder);
330
    }
331
332
    /**
333
     * Check if package is installed
334
     *
335
     * @param  PackageInterface $package
336
     * @return bool
337
     */
338
    public function isPackageInstalled(PackageInterface $package)
339
    {
340
        return $this->filesystem->exists($this->config->getInstallDir() . '/' . $package->getName() . '/.bower.json');
341
    }
342
343
    /**
344
     * {@inheritdoc}
345
     */
346
    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...
347
    {
348
        if ($checkInstall && !$this->isPackageInstalled($package)) {
349
            return false;
350
        }
351
        try {
352
            $bower = $this->config->getBowerFileContent();
353
        } catch (RuntimeException $e) { // no bower.json file, package is extraneous
354
355
            return true;
356
        }
357
        if (!isset($bower['dependencies'])) {
358
            return true;
359
        }
360
        // package is a direct dependencies
361
        if (isset($bower['dependencies'][$package->getName()])) {
362
            return false;
363
        }
364
        // look for dependencies of dependencies
365
        foreach ($bower['dependencies'] as $name => $version) {
366
            $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name . '/.bower.json');
367
            $depBower = json_decode($dotBowerJson, true);
368
            if (isset($depBower['dependencies'][$package->getName()])) {
369
                return false;
370
            }
371
            // look for dependencies of dependencies of dependencies
372
            if (isset($depBower['dependencies'])) {
373
                foreach ($depBower['dependencies'] as $name1 => $version1) {
374
                    $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name1 . '/.bower.json');
375
                    $depDepBower = json_decode($dotBowerJson, true);
376
                    if (isset($depDepBower['dependencies'][$package->getName()])) {
377
                        return false;
378
                    }
379
                }
380
            }
381
        }
382
383
        return true;
384
    }
385
386
    /**
387
     * @param  array $params
388
     * @return array
389
     */
390
    protected function createAClearBowerFile(array $params)
391
    {
392
        $authors = ['Beelab <[email protected]>'];
393
        if (!empty($params['author'])) {
394
            $authors[] = $params['author'];
395
        }
396
        $structure = [
397
            'name'         => $params['name'],
398
            'authors'      => $authors,
399
            'private'      => true,
400
            'dependencies' => new \StdClass(),
401
        ];
402
403
        return $structure;
404
    }
405
406
    /**
407
     * @param  PackageInterface $package
408
     * @param  bool             $setInfo
409
     * @return string
410
     */
411
    protected function getPackageTag(PackageInterface $package, $setInfo = false)
412
    {
413
        $decode = $this->findPackage($package);
414
        // open package repository
415
        $repoUrl = $decode['url'];
416
        $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
417
        $packageTag = $this->repository->findPackage($package->getRequiredVersion());
418
        if (is_null($packageTag)) {
419
            throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
420
        }
421
        $bowerJson = $this->repository->getBower($packageTag);
422
        $bower = json_decode($bowerJson, true);
423
        if (!is_array($bower)) {
424
            throw new RuntimeException(sprintf('Invalid bower.json found in package %s: %s.', $package->getName(), $bowerJson));
425
        }
426
        if ($setInfo) {
427
            $package->setInfo($bower);
428
        }
429
430
        return $packageTag;
431
    }
432
433
    /**
434
     * @param  string $name
435
     * @return array
436
     */
437
    protected function findPackage($package)
438
    {
439
        $name = $package->getName();
440
        
441
        if( $url = $package->getRequiredVersionUrl() ){
442
            return ['name'=>$name,'url'=>$url];
443
        }
444
        
445
        try {
446
            $response = $this->githubClient->getHttpClient()->get($this->config->getBasePackagesUrl().urlencode($name));
447
        } catch (RuntimeException $e) {
448
            throw new RuntimeException(sprintf('Cannot fetch registry info for package %s from search registry (%s).', $name, $e->getMessage()));
449
        }
450
        $packageInfo = json_decode($response->getBody(true), true);
451
        if (!is_array($packageInfo) || empty($packageInfo['url'])) {
452
            throw new RuntimeException(sprintf('Registry info for package %s has malformed json or is missing "url".', $name));
453
        }
454
455
        return $packageInfo;
456
    }
457
458
    /**
459
     * @param PackageInterface $package
460
     */
461
    private function cachePackage(PackageInterface $package)
462
    {
463
        // get release archive from repository
464
        $file = $this->repository->getRelease();
465
466
        $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
467
        $this->filesystem->write($tmpFileName, $file);
468
    }
469
470
    /**
471
     * @param PackageInterface $package
472
     * @param bool             $isDependency
473
     */
474
    private function updateBowerFile(PackageInterface $package, $isDependency = false)
475
    {
476
        if ($this->config->isSaveToBowerJsonFile() && !$isDependency) {
477
            try {
478
                $this->config->updateBowerJsonFile($package);
479
            } catch (RuntimeException $e) {
480
                $this->output->writelnNoBowerJsonFile();
481
            }
482
        }
483
    }
484
}
485