Completed
Push — master ( f5e702...ef8b37 )
by Andrii
02:20
created

PackageManager   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 55.79%

Importance

Changes 12
Bugs 1 Features 0
Metric Value
wmc 37
c 12
b 1
f 0
lcom 1
cbo 4
dl 0
loc 274
ccs 53
cts 95
cp 0.5579
rs 8.6

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A readConfig() 0 11 4
A writeConfig() 0 10 4
A mergeConfig() 0 12 4
A mergeVersions() 0 13 4
A isMoreVersion() 0 4 1
A isAnyVersion() 0 4 3
A setConfig() 0 4 1
A perform() 0 6 2
A passthru() 0 5 1
A prepareCommand() 0 9 2
A setBin() 0 4 1
A getBin() 0 8 2
A detectBin() 0 8 2
A scanPackage() 0 14 4
A runAction() 0 7 1
1
<?php
2
3
/*
4
 * Composer plugin for bower/npm assets
5
 *
6
 * @link      https://github.com/hiqdev/composer-asset-plugin
7
 * @package   composer-asset-plugin
8
 * @license   BSD-3-Clause
9
 * @copyright Copyright (c) 2015, HiQDev (http://hiqdev.com/)
10
 */
11
12
namespace hiqdev\composerassetplugin;
13
14
use Composer\Json\JsonFile;
15
use Composer\Package\CompletePackageInterface;
16
17
/**
18
 * Abstract package manager class.
19
 *
20
 * @author Andrii Vasyliev <[email protected]>
21
 */
22
abstract class PackageManager
23
{
24
    /**
25
     * @var Plugin the plugin instance
26
     */
27
    protected $plugin;
28
29
    /**
30
     * @var string Package manager name: `bower` or `npm`
31
     */
32
    protected $name;
33
34
    /**
35
     * @var string Package config file name: `bower.json` or `package.json`
36
     */
37
    public $file;
38
39
    /**
40
     * @var string Path to package manager binary
41
     */
42
    public $bin;
43
44
    /**
45
     * @var string Package name of the PHP version of the package manager
46
     */
47
    public $phpPackage;
48
49
    /**
50
     * @var string Binary name of PHP version of package manager
51
     */
52
    protected $phpBin;
53
54
    /**
55
     * @var array Package config. Initially holds default config
56
     */
57
    protected $config = [];
58
59
    /**
60
     * @var array List of keys holding dependencies
61
     */
62
    protected $dependencies = ['dependencies', 'devDependencies'];
63
64
    /**
65
     * Reads config file or dist config if exists, merges with default config.
66
     * @param Plugin $plugin
67
     * @void
68
     */
69 3
    public function __construct(Plugin $plugin)
70
    {
71 3
        $this->plugin = $plugin;
72
        //$dist = $this->file . '.dist';
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
73 3
        $this->config = array_merge(
74 3
            $this->config,
75 3
            $this->readConfig($this->file)
76
            //$this->readConfig(file_exists($dist) ? $dist : $this->file)
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
77 3
        );
78 3
    }
79
80
    /**
81
     * Reads the JSON config from the $path.
82
     *
83
     * @param string $path path to the Json file
84
     * @return array|mixed
85
     */
86 3
    public function readConfig($path)
87
    {
88 3
        $jsonFile = new JsonFile($path);
89 3
        $config = $jsonFile->exists() ? $jsonFile->read() : [];
90 3
        foreach ($this->dependencies as $key) {
91 3
            if (!isset($config[$key])) {
92 3
                $config[$key] = [];
93 3
            }
94 3
        }
95 3
        return $config;
96
    }
97
98
    /**
99
     * Saves JSON config to the given path.
100
     *
101
     * @param string $path
102
     * @param array $config
103
     * @throws \Exception
104
     */
105 1
    public function writeConfig($path, array $config)
106
    {
107 1
        foreach ($this->dependencies as $key) {
108 1
            if (isset($config[$key]) && !$config[$key]) {
109 1
                unset($config[$key]);
110 1
            }
111 1
        }
112 1
        $jsonFile = new JsonFile($path);
113 1
        $jsonFile->write($config);
114 1
    }
115
116
    /**
117
     * Scans the $package and extracts dependencies to the [[config]].
118
     *
119
     * @param CompletePackageInterface $package
120
     * @see mergeConfig()
121
     * @void
122
     */
123
    public function scanPackage(CompletePackageInterface $package)
124
    {
125
        $extra = $package->getExtra();
126
        $config = [];
127
        foreach ($this->dependencies as $key) {
128
            $name = $this->name . '-' . $key;
129
            if (isset($extra[$name])) {
130
                $config[$key] = $extra[$name];
131
            }
132
        }
133
        if (!empty($config)) {
134
            $this->mergeConfig($config);
135
        }
136
    }
137
138
    /**
139
     * Merges the $config over the [[config]], doesn't resolve version conflicts.
140
     * @param array $config
141
     * @see mergeVersions()
142
     * @void
143
     */
144
    protected function mergeConfig(array $config)
145
    {
146
        foreach ($config as $key => $packages) {
147
            foreach ($packages as $name => $version) {
148
                if (isset($this->config[$key][$name])) {
149
                    $this->config[$key][$name] = $this->mergeVersions($this->config[$key][$name], $version);
150
                } else {
151
                    $this->config[$key][$name] = $version;
152
                }
153
            }
154
        }
155
    }
156
157
    /**
158
     * @param $a
159
     * @param $b
160
     * @return string
161
     */
162
    protected function mergeVersions($a, $b)
163
    {
164
        $a = trim($a);
165
        $b = trim($b);
166
167
        if ($a === $b || $this->isMoreVersion($b, $a)) {
168
            return $a;
169
        } elseif ($this->isMoreVersion($a, $b)) {
170
            return $b;
171
        } else {
172
            return $a . ' ' . $b;
173
        }
174
    }
175
176
    /**
177
     * Check if $a is more then $b, like: a="1.1 || 2.2" b="1.1"
178
     * Possible optimization.
179
     * // TODO Rename and implement.
180
     * @param string $a
181
     * @param string $b
182
     * @return boolean
183
     */
184
    public function isMoreVersion($a, $b)
0 ignored issues
show
Unused Code introduced by
The parameter $b is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
185
    {
186
        return $this->isAnyVersion($a);
187
    }
188
189
    /**
190
     * Checks whether the $version represents any possible version.
191
     *
192
     * @param string $version
193
     * @return boolean
194
     */
195
    public function isAnyVersion($version)
196
    {
197
        return $version === '' || $version === '*' || $version === '>=0.0.0';
198
    }
199
200
    /**
201
     * Set config.
202
     * @param array $config
203
     */
204
    public function setConfig(array $config)
205
    {
206
        $this->config = $config;
207
    }
208
209
    /**
210
     * Run the given action: show notice, write config and run `perform`.
211
     * @param string $action the action name
212
     * @void
213
     */
214 1
    public function runAction($action)
215
    {
216 1
        $doing = lcfirst(trim($action, 'e')) . 'ing';
217 1
        $this->plugin->io->writeError('<info>' . $doing . ' ' . $this->name . ' dependencies...</info>');
218 1
        $this->writeConfig($this->file, $this->config);
219 1
        $this->perform($action);
220 1
    }
221
222
    /**
223
     * Run installation. Specific for every package manager.
224
     * @param string $action the action name
225
     * @void
226
     */
227 1
    protected function perform($action)
228
    {
229 1
        if ($this->passthru([$action])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->passthru(array($action)) of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
230 1
            $this->plugin->io->writeError('<error>failed ' . $this->name . ' ' . $action . '</error>');
231 1
        }
232 1
    }
233
234
    /**
235
     * Prepares arguments and runs the command with [[passthru()]].
236
     * @param array $arguments
237
     * @return integer the exit code
238
     */
239 1
    public function passthru(array $arguments = [])
240
    {
241 1
        passthru($this->getBin() . $this->prepareCommand($arguments), $exitCode);
242 1
        return $exitCode;
243
    }
244
245
    /**
246
     * Prepares given command arguments.
247
     * @param array $arguments
248
     * @return string
249
     */
250 1
    public function prepareCommand(array $arguments = [])
251
    {
252 1
        $result = '';
253 1
        foreach ($arguments as $a) {
254 1
            $result .= ' ' . escapeshellarg($a);
255 1
        }
256
257 1
        return $result;
258
    }
259
260
    /**
261
     * Set path to binary executable file.
262
     * @param $bin
263
     * @internal param string $value
264
     */
265
    public function setBin($bin)
266
    {
267
        $this->bin = $bin;
268
    }
269
270
    /**
271
     * Get path to the binary executable file.
272
     * @return string
273
     */
274 1
    public function getBin()
275
    {
276 1
        if ($this->bin === null) {
277 1
            $this->bin = $this->detectBin();
278 1
        }
279
280 1
        return $this->bin;
281
    }
282
283
    /**
284
     * Find path to the binary.
285
     * @return string
286
     */
287 1
    public function detectBin()
288
    {
289 1
        if (isset($this->plugin->getPackages()[$this->phpPackage])) {
290
            return $this->plugin->getVendorDir() . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . $this->phpBin;
291
        }
292
293 1
        return $this->name;
294
    }
295
}
296