Completed
Push — master ( ebe8f0...0b917b )
by Andrii
04:14
created

PackageManager::addDependency()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
ccs 0
cts 6
cp 0
rs 9.4285
cc 2
eloc 5
nc 2
nop 3
crap 6
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-2016, 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
    public function writeConfig($path, array $config)
106
    {
107
        foreach ($this->dependencies as $key) {
108
            if (isset($config[$key]) && !$config[$key]) {
109
                unset($config[$key]);
110
            }
111
        }
112
        $jsonFile = new JsonFile($path);
113
        $jsonFile->write($config);
114
    }
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 $type => $packages) {
147
            foreach ($packages as $name => $version) {
148
                $this->addDependency($type, $name, $version);
149
            }
150
        }
151
    }
152
153
    public function addDependency($type, $name, $version)
154
    {
155
        if (isset($this->config[$type][$name])) {
156
            $this->config[$type][$name] = $this->mergeVersions($this->config[$type][$name], $version);
157
        } else {
158
            $this->config[$type][$name] = $version;
159
        }
160
    }
161
162
    /**
163
     * @param $a
164
     * @param $b
165
     * @return string
166
     */
167
    protected function mergeVersions($a, $b)
168
    {
169
        $a = trim($a);
170
        $b = trim($b);
171
172
        if ($a === $b || $this->isMoreVersion($b, $a)) {
173
            return $a;
174
        } elseif ($this->isMoreVersion($a, $b)) {
175
            return $b;
176
        } else {
177
            return $a . ' ' . $b;
178
        }
179
    }
180
181
    /**
182
     * Check if $a is more then $b, like: a="1.1 || 2.2" b="1.1"
183
     * Possible optimization.
184
     * // TODO Rename and implement.
185
     * @param string $a
186
     * @param string $b
187
     * @return boolean
188
     */
189
    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...
190
    {
191
        return $this->isAnyVersion($a);
192
    }
193
194
    /**
195
     * Checks whether the $version represents any possible version.
196
     *
197
     * @param string $version
198
     * @return boolean
199
     */
200
    public function isAnyVersion($version)
201
    {
202
        return $version === '' || $version === '*' || $version === '>=0.0.0';
203
    }
204
205
    /**
206
     * Set config.
207
     * @param array $config
208
     */
209
    public function setConfig(array $config)
210
    {
211
        $this->config = $config;
212
    }
213
214
    /**
215
     * Returns if the package manager has nonempty dependency list.
216
     * @return bool
217
     */
218 1
    public function hasDependencies()
219
    {
220 1
        foreach ($this->dependencies as $key) {
221 1
            if (isset($this->config[$key]) && $this->config[$key]) {
222
                return true;
223
            }
224 1
        }
225
226 1
        return false;
227
    }
228
229
    /**
230
     * Run the given action: show notice, write config and run `perform`.
231
     * @param string $action the action name
232
     * @void
233
     */
234
    public function runAction($action)
235
    {
236
        $doing = ucfirst(trim($action, 'e')) . 'ing';
237
        $this->plugin->io->writeError('<info>' . $doing . ' ' . $this->name . ' dependencies...</info>');
238
        $this->writeConfig($this->file, $this->config);
239
        $this->perform($action);
240
    }
241
242
    /**
243
     * Run installation. Specific for every package manager.
244
     * @param string $action the action name
245
     * @void
246
     */
247
    protected function perform($action)
248
    {
249
        $this->plugin->io->writeError('running ' . $this->getBin());
250
        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...
251
            $this->plugin->io->writeError('<error>failed ' . $this->name . ' ' . $action . '</error>');
252
        }
253
    }
254
255
    /**
256
     * Prepares arguments and runs the command with [[passthru()]].
257
     * @param array $arguments
258
     * @return integer the exit code
259
     */
260
    public function passthru(array $arguments = [])
261
    {
262
        passthru($this->getBin() . $this->prepareCommand($arguments), $exitCode);
263
        return $exitCode;
264
    }
265
266
    /**
267
     * Prepares given command arguments.
268
     * @param array $arguments
269
     * @return string
270
     */
271
    public function prepareCommand(array $arguments = [])
272
    {
273
        $result = '';
274
        foreach ($arguments as $a) {
275
            $result .= ' ' . escapeshellarg($a);
276
        }
277
278
        return $result;
279
    }
280
281
    /**
282
     * Set path to binary executable file.
283
     * @param $bin
284
     * @internal param string $value
285
     */
286
    public function setBin($bin)
287
    {
288
        $this->bin = $bin;
289
    }
290
291
    /**
292
     * Get path to the binary executable file.
293
     * @return string
294
     */
295
    public function getBin()
296
    {
297
        if ($this->bin === null) {
298
            $this->bin = $this->detectBin();
299
        }
300
301
        return $this->bin;
302
    }
303
304
    /**
305
     * Find path to the binary.
306
     * @return string
307
     */
308
    public function detectBin()
309
    {
310
        if ($this->plugin->findPackage($this->phpPackage)) {
311
            return $this->plugin->getVendorDir() . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . $this->phpBin;
312
        }
313
314
        return $this->name;
315
    }
316
}
317