Completed
Push — master ( 0b917b...fc5a3e )
by Andrii
03:48
created

PackageManager::fixConstraint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
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\PackageInterface;
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
     * array known deps collected from requirements
66
     */
67
    protected $knownDeps = [];
68
69
    /**
70
     * Reads config file or dist config if exists, merges with default config.
71
     * @param Plugin $plugin
72
     * @void
73
     */
74 3
    public function __construct(Plugin $plugin)
75
    {
76 3
        $this->plugin = $plugin;
77
        //$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...
78 3
        $this->config = array_merge(
79 3
            $this->config,
80 3
            $this->readConfig($this->file)
81
            //$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...
82 3
        );
83 3
    }
84
85
    public function packageFullName($package)
86
    {
87
        return $package->getName() . ':' . $package->getVersion();
88
    }
89
90
    public function setKnownDeps(PackageInterface $package, $type, $name, $constraint)
91
    {
92
        $res = $this->getKnownDeps($package);
93
        if (!isset($res[$type])) {
94
            $res[$type] = [];
95
        }
96
        $res[$type][$name] = $constraint;
97
        $this->knownDeps[$this->packageFullName($package)] = $res;
98
    }
99
100
    public function getKnownDeps(PackageInterface $package)
101
    {
102
        $full = $this->packageFullName($package);
103
        return isset($this->knownDeps[$full]) ? $this->knownDeps[$full] : [];
104
    }
105
106
    public function getConfig()
107
    {
108
        return $this->config;
109
    }
110
111
    /**
112
     * Reads the JSON config from the $path.
113
     *
114
     * @param string $path path to the Json file
115
     * @return array|mixed
116
     */
117 3
    public function readConfig($path)
118
    {
119 3
        $jsonFile = new JsonFile($path);
120 3
        $config = $jsonFile->exists() ? $jsonFile->read() : [];
121 3
        foreach ($this->dependencies as $key) {
122 3
            if (!isset($config[$key])) {
123 3
                $config[$key] = [];
124 3
            }
125 3
        }
126 3
        return $config;
127
    }
128
129
    /**
130
     * Saves JSON config to the given path.
131
     *
132
     * @param string $path
133
     * @param array $config
134
     * @throws \Exception
135
     */
136
    public function writeConfig($path, array $config)
137
    {
138
        $jsonFile = new JsonFile($path);
139
        $jsonFile->write($this->prepareConfig($config));
140
    }
141
142
    public function prepareConfig(array $config)
143
    {
144
        foreach ($this->dependencies as $key) {
145
            if (!isset($config[$key])) {
146
                continue;
147
            }
148
            if (!$config[$key]) {
149
                unset($config[$key]);
150
                continue;
151
            }
152
            foreach ($config['key'] as $name => &$constraint) {
153
                $constraint = $this->fixConstraint($constraint);
154
            }
155
        }
156
157
        return $config;
158
    }
159
160
    /**
161
     * Fixes constraint for the package manager.
162
     * Does nothing for NPM. Redefined in Bower.
163
     * @param string $constraint
164
     * @return string
165
     */
166
    public function fixConstraint($constraint)
167
    {
168
        return $constraint;
169
    }
170
171
    /**
172
     * Scans the $package and extracts dependencies to the [[config]].
173
     * @param PackageInterface $package
174
     */
175
    public function scanPackage(PackageInterface $package)
176
    {
177
        $extra = $package->getExtra();
178
        $extra_deps = [];
179
        foreach ($this->dependencies as $key) {
180
            $name = $this->name . '-' . $key;
181
            if (isset($extra[$name])) {
182
                $extra_deps[$key] = $extra[$name];
183
            }
184
        }
185
        $known_deps = $this->getKnownDeps($package);
186
        foreach ([$known_deps, $extra_deps] as $deps) {
187
            if (!empty($deps)) {
188
                $this->mergeConfig($deps);
189
            }
190
        }
191
    }
192
193
    /**
194
     * Merges the $config over the [[config]].
195
     * @param array $config
196
     */
197
    protected function mergeConfig(array $config)
198
    {
199
        foreach ($config as $type => $packages) {
200
            foreach ($packages as $name => $constraint) {
201
                $this->addDependency($type, $name, $constraint);
202
            }
203
        }
204
    }
205
206
    public function addDependency($type, $name, $constraint)
207
    {
208
        if (isset($this->config[$type][$name])) {
209
            $this->config[$type][$name] = Constraint::merge($this->config[$type][$name], $constraint);
210
        } else {
211
            $this->config[$type][$name] = $constraint;
212
        }
213
    }
214
215
    /**
216
     * Set config.
217
     * @param array $config
218
     */
219
    public function setConfig(array $config)
220
    {
221
        $this->config = $config;
222
    }
223
224
    /**
225
     * Returns if the package manager has nonempty dependency list.
226
     * @return bool
227
     */
228 1
    public function hasDependencies()
229
    {
230 1
        foreach ($this->dependencies as $key) {
231 1
            if (isset($this->config[$key]) && $this->config[$key]) {
232
                return true;
233
            }
234 1
        }
235
236 1
        return false;
237
    }
238
239
    /**
240
     * Run the given action: show notice, write config and run `perform`.
241
     * @param string $action the action name
242
     */
243
    public function runAction($action)
244
    {
245
        $doing = ucfirst(trim($action, 'e')) . 'ing';
246
        $this->plugin->io->writeError('<info>' . $doing . ' ' . $this->name . ' dependencies...</info>');
247
        $this->writeConfig($this->file, $this->config);
248
        $this->perform($action);
249
    }
250
251
    /**
252
     * Run installation. Specific for every package manager.
253
     * @param string $action the action name
254
     * @void
255
     */
256
    protected function perform($action)
257
    {
258
        $this->plugin->io->writeError('running ' . $this->getBin());
259
        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...
260
            $this->plugin->io->writeError('<error>failed ' . $this->name . ' ' . $action . '</error>');
261
        }
262
    }
263
264
    /**
265
     * Prepares arguments and runs the command with [[passthru()]].
266
     * @param array $arguments
267
     * @return integer the exit code
268
     */
269
    public function passthru(array $arguments = [])
270
    {
271
        passthru($this->getBin() . $this->prepareCommand($arguments), $exitCode);
272
        return $exitCode;
273
    }
274
275
    /**
276
     * Prepares given command arguments.
277
     * @param array $arguments
278
     * @return string
279
     */
280
    public function prepareCommand(array $arguments = [])
281
    {
282
        $result = '';
283
        foreach ($arguments as $a) {
284
            $result .= ' ' . escapeshellarg($a);
285
        }
286
287
        return $result;
288
    }
289
290
    /**
291
     * Set path to binary executable file.
292
     * @param $bin
293
     * @internal param string $value
294
     */
295
    public function setBin($bin)
296
    {
297
        $this->bin = $bin;
298
    }
299
300
    /**
301
     * Get path to the binary executable file.
302
     * @return string
303
     */
304
    public function getBin()
305
    {
306
        if ($this->bin === null) {
307
            $this->bin = $this->detectBin();
308
        }
309
310
        return $this->bin;
311
    }
312
313
    /**
314
     * Find path to the binary.
315
     * @return string
316
     */
317
    public function detectBin()
318
    {
319
        if ($this->plugin->findPackage($this->phpPackage)) {
320
            return $this->plugin->getVendorDir() . DIRECTORY_SEPARATOR . 'bin' . DIRECTORY_SEPARATOR . $this->phpBin;
321
        }
322
323
        return $this->name;
324
    }
325
}
326