Completed
Push — master ( cc9974...d0ce99 )
by Massimiliano
02:29
created

Bowerphp::isNeedUpdate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
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
use vierbergenlars\SemVer\version;
27
use vierbergenlars\SemVer\expression;
28
29
/**
30
 * Main class
31
 */
32
class Bowerphp
0 ignored issues
show
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...
Complexity introduced by
The class Bowerphp has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13.
Loading history...
33
{
34
    protected $config;
35
    protected $filesystem;
36
    protected $githubClient;
37
    protected $repository;
38
    protected $output;
39
40
    /**
41
     * @param ConfigInterface       $config
42
     * @param Filesystem            $filesystem
43
     * @param Client                $githubClient
44
     * @param RepositoryInterface   $repository
45
     * @param BowerphpConsoleOutput $output
46
     */
47
    public function __construct(
48
        ConfigInterface $config,
49
        Filesystem $filesystem,
50
        Client $githubClient,
51
        RepositoryInterface $repository,
52
        BowerphpConsoleOutput $output
53
    ) {
54
        $this->config = $config;
55
        $this->filesystem = $filesystem;
56
        $this->githubClient = $githubClient;
57
        $this->repository = $repository;
58
        $this->output = $output;
59
    }
60
61
    /**
62
     * Init bower.json
63
     *
64
     * @param array $params
65
     */
66
    public function init(array $params)
67
    {
68
        if ($this->config->bowerFileExists()) {
69
            $bowerJson = $this->config->getBowerFileContent();
70
            $this->config->setSaveToBowerJsonFile(true);
71
            $this->config->updateBowerJsonFile2($bowerJson, $params);
72
        } else {
73
            $this->config->initBowerJsonFile($params);
74
        }
75
    }
76
77
    /**
78
     * Install a single package
79
     *
80
     * @param PackageInterface   $package
81
     * @param InstallerInterface $installer
82
     * @param bool               $isDependency
83
     */
84
    public function installPackage(PackageInterface $package, InstallerInterface $installer, $isDependency = false)
85
    {
86
        if (strpos($package->getName(), 'github') !== false) {
87
            // install from a github endpoint
88
            $name = basename($package->getName(), '.git');
89
            $repoUrl = $package->getName();
90
            $package = new Package($name, $package->getRequiredVersion());
91
            $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
92
            $package->setRepository($this->repository);
93
            $packageTag = $this->repository->findPackage($package->getRequiredVersion());
94
            if (is_null($packageTag)) {
95
                throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
96
            }
97
        } else {
98
            $packageTag = $this->getPackageTag($package, true);
99
            $package->setRepository($this->repository);
100
        }
101
102
        $package->setVersion($packageTag);
103
104
        $this->updateBowerFile($package, $isDependency);
105
106
        // if package is already installed, match current version with latest available version
107
        if ($this->isPackageInstalled($package)) {
108
            $packageBower = $this->config->getPackageBowerFileContent($package);
109
            if ($packageTag == $packageBower['version']) {
110
                // if version is fully matching, there's no need to install
111
                return;
112
            }
113
        }
114
115
        $this->output->writelnInfoPackage($package);
116
117
        $this->output->writelnInstalledPackage($package);
118
119
        $this->cachePackage($package);
120
121
        $installer->install($package);
122
123
        $overrides = $this->config->getOverrideFor($package->getName());
124
        if (array_key_exists('dependencies', $overrides)) {
125
            $dependencies = $overrides['dependencies'];
126
        } else {
127
            $dependencies = $package->getRequires();
128
        }
129
        if (!empty($dependencies)) {
130
            foreach ($dependencies as $name => $version) {
131
                $depPackage = new Package($name, $version);
132
                if (!$this->isPackageInstalled($depPackage)) {
133
                    $this->installPackage($depPackage, $installer, true);
134
                } elseif ($this->isNeedUpdate($depPackage)) {
135
                    $this->updatePackage($depPackage, $installer);
136
                }
137
            }
138
        }
139
    }
140
141
    /**
142
     * Install all dependencies
143
     *
144
     * @param InstallerInterface $installer
145
     */
146
    public function installDependencies(InstallerInterface $installer)
147
    {
148
        $decode = $this->config->getBowerFileContent();
149
        if (!empty($decode['dependencies'])) {
150
            foreach ($decode['dependencies'] as $name => $requiredVersion) {
151
                if (strpos($requiredVersion, 'github') !== false) {
152
                    list($name, $requiredVersion) = explode('#', $requiredVersion);
153
                }
154
                $package = new Package($name, $requiredVersion);
155
                $this->installPackage($package, $installer, true);
156
            }
157
        }
158
    }
159
160
    /**
161
     * Update a single package
162
     *
163
     * @param PackageInterface   $package
164
     * @param InstallerInterface $installer
165
     */
166
    public function updatePackage(PackageInterface $package, InstallerInterface $installer)
0 ignored issues
show
Complexity introduced by
This operation has 800 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...
167
    {
168
        if (!$this->isPackageInstalled($package)) {
169
            throw new RuntimeException(sprintf('Package %s is not installed.', $package->getName()));
170
        }
171
        if (is_null($package->getRequiredVersion())) {
172
            $decode = $this->config->getBowerFileContent();
173
            if (empty($decode['dependencies']) || empty($decode['dependencies'][$package->getName()])) {
174
                throw new InvalidArgumentException(sprintf('Package %s not found in bower.json', $package->getName()));
175
            }
176
            $package->setRequiredVersion($decode['dependencies'][$package->getName()]);
177
        }
178
179
        $bower = $this->config->getPackageBowerFileContent($package);
180
        $package->setInfo($bower);
181
        $package->setVersion($bower['version']);
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
                } elseif ($this->isNeedUpdate($depPackage)) {
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->getName());
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($name)
265
    {
266
        return $this->findPackage($name);
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->getName());
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
            return true;
350
        }
351
        if (!isset($bower['dependencies'])) {
352
            return true;
353
        }
354
        // package is a direct dependencies
355
        if (isset($bower['dependencies'][$package->getName()])) {
356
            return false;
357
        }
358
        // look for dependencies of dependencies
359
        foreach ($bower['dependencies'] as $name => $version) {
360
            $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name . '/.bower.json');
361
            $depBower = json_decode($dotBowerJson, true);
362
            if (isset($depBower['dependencies'][$package->getName()])) {
363
                return false;
364
            }
365
            // look for dependencies of dependencies of dependencies
366
            if (isset($depBower['dependencies'])) {
367
                foreach ($depBower['dependencies'] as $name1 => $version1) {
368
                    $dotBowerJson = $this->filesystem->read($this->config->getInstallDir() . '/' . $name1 . '/.bower.json');
369
                    $depDepBower = json_decode($dotBowerJson, true);
370
                    if (isset($depDepBower['dependencies'][$package->getName()])) {
371
                        return false;
372
                    }
373
                }
374
            }
375
        }
376
377
        return true;
378
    }
379
380
    /**
381
     * @param  array $params
382
     * @return array
383
     */
384
    protected function createAClearBowerFile(array $params)
385
    {
386
        $authors = ['Beelab <[email protected]>'];
387
        if (!empty($params['author'])) {
388
            $authors[] = $params['author'];
389
        }
390
        $structure = [
391
            'name'         => $params['name'],
392
            'authors'      => $authors,
393
            'private'      => true,
394
            'dependencies' => new \StdClass(),
395
        ];
396
397
        return $structure;
398
    }
399
400
    /**
401
     * @param  PackageInterface $package
402
     * @param  bool             $setInfo
403
     * @return string
404
     */
405
    protected function getPackageTag(PackageInterface $package, $setInfo = false)
406
    {
407
        $decode = $this->findPackage($package->getName());
408
        // open package repository
409
        $repoUrl = $decode['url'];
410
        $this->repository->setUrl($repoUrl)->setHttpClient($this->githubClient);
411
        $packageTag = $this->repository->findPackage($package->getRequiredVersion());
412
        if (is_null($packageTag)) {
413
            throw new RuntimeException(sprintf('Cannot find package %s version %s.', $package->getName(), $package->getRequiredVersion()));
414
        }
415
        $bowerJson = $this->repository->getBower($packageTag);
416
        $bower = json_decode($bowerJson, true);
417
        if (!is_array($bower)) {
418
            throw new RuntimeException(sprintf('Invalid bower.json found in package %s: %s.', $package->getName(), $bowerJson));
419
        }
420
        if ($setInfo) {
421
            $package->setInfo($bower);
422
        }
423
424
        return $packageTag;
425
    }
426
427
    /**
428
     * @param  string $name
429
     * @return array
430
     */
431
    protected function findPackage($name)
432
    {
433
        try {
434
            $response = $this->githubClient->getHttpClient()->get($this->config->getBasePackagesUrl() . urlencode($name));
435
        } catch (RuntimeException $e) {
436
            throw new RuntimeException(sprintf('Cannot fetch registry info for package %s from search registry (%s).', $name, $e->getMessage()));
437
        }
438
        $packageInfo = json_decode($response->getBody(true), true);
439
        if (!is_array($packageInfo) || empty($packageInfo['url'])) {
440
            throw new RuntimeException(sprintf('Registry info for package %s has malformed json or is missing "url".', $name));
441
        }
442
443
        return $packageInfo;
444
    }
445
446
    /**
447
     * @param PackageInterface $package
448
     */
449
    private function cachePackage(PackageInterface $package)
450
    {
451
        // get release archive from repository
452
        $file = $this->repository->getRelease();
453
454
        $tmpFileName = $this->config->getCacheDir() . '/tmp/' . $package->getName();
455
        $this->filesystem->write($tmpFileName, $file);
456
    }
457
458
    /**
459
     * @param PackageInterface $package
460
     * @param bool             $isDependency
461
     */
462
    private function updateBowerFile(PackageInterface $package, $isDependency = false)
463
    {
464
        if ($this->config->isSaveToBowerJsonFile() && !$isDependency) {
465
            try {
466
                $this->config->updateBowerJsonFile($package);
467
            } catch (RuntimeException $e) {
468
                $this->output->writelnNoBowerJsonFile();
469
            }
470
        }
471
    }
472
473
    /**
474
     * Update only if needed is greater version
475
     *
476
     * @param  PackageInterface $package
477
     * @return bool
478
     */
479
    public function isNeedUpdate($package)
480
    {
481
        $packageBower = $this->config->getPackageBowerFileContent($package);
482
        $semver = new version($packageBower['version']);
483
484
        return !$semver->satisfies(new expression($package->getRequiredVersion()));
485
    }
486
}
487