Completed
Push — master ( 536f20...20484c )
by Andrii
02:09
created

PackageManager::runAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4286
cc 1
eloc 5
nc 1
nop 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\CompletePackage;
16
17
/**
18
 * Abstract package manager class.
19
 *
20
 * @author Andrii Vasyliev <[email protected]>
21
 */
22
abstract class PackageManager
23
{
24
    /**
25
     * The plugin.
26
     * @var Plugin
27
     */
28
    protected $plugin;
29
30
    /**
31
     * Package manager name: bower or npm.
32
     * @var string
33
     */
34
    protected $name;
35
36
    /**
37
     * Package config file: bower.json or package.json
38
     * @var string
39
     */
40
    public $file;
41
42
    /**
43
     * Path to package manager binary.
44
     * @var string
45
     */
46
    public $bin;
47
48
    /**
49
     * Package name of PHP version of package manager.
50
     * @var string
51
     */
52
    public $phpPackage;
53
54
    /**
55
     * Binary name of PHP version of package manager.
56
     * @var string
57
     */
58
    protected $phpBin;
59
60
    /**
61
     * Package config. Initially holds default config.
62
     */
63
    protected $config = [];
64
65
    /**
66
     * Conversion table.
67
     * @var string
68
     */
69
    protected $keyTable = [
70
        'require'     => 'dependencies',
71
        'require-dev' => 'devDependencies',
72
    ];
73
74
    /**
75
     * Reads config file or dist config if exists, merges with default config.
76
     */
77
    public function __construct(Plugin $plugin)
78
    {
79
        $this->plugin = $plugin;
80
        //$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...
81
        $this->config = array_merge(
82
            $this->config,
83
            $this->readConfig($this->file)
84
            //$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...
85
        );
86
    }
87
88
    public function readConfig($file)
89
    {
90
        $jsonFile = new JsonFile($file);
91
        $config = $jsonFile->exists() ? $jsonFile->read() : [];
92
        foreach ($this->keyTable as $key) {
0 ignored issues
show
Bug introduced by
The expression $this->keyTable of type string is not traversable.
Loading history...
93
            if (!isset($config[$key])) {
94
                $config[$key] = [];
95
            }
96
        }
97
        return $config;
98
    }
99
100
    public function writeConfig($file, $config)
101
    {
102
        foreach ($this->keyTable as $key) {
0 ignored issues
show
Bug introduced by
The expression $this->keyTable of type string is not traversable.
Loading history...
103
            if (isset($config[$key]) && !$config[$key]) {
104
                unset($config[$key]);
105
            }
106
        }
107
        $jsonFile = new JsonFile($file);
108
        $jsonFile->write($config);
109
    }
110
111
    public function scanPackage(CompletePackage $package)
112
    {
113
        $extra = $package->getExtra();
114
        $config = [];
115
        foreach (['require', 'require-dev'] as $key) {
116
            $name = $this->name . '-' . $key;
117
            if (isset($extra[$name])) {
118
                $config[$key] = $extra[$name];
119
            }
120
        }
121
        if ($config) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $config of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
122
            $this->mergeConfig($config);
123
        }
124
    }
125
126
    protected function mergeConfig($config)
127
    {
128
        foreach ($config as $key => $packages) {
129
            $key = $this->keyTable[$key];
130
            foreach ($packages as $name => $version) {
131
                $this->config[$key][$name] = isset($this->config[$key][$name])
132
                    ? $this->mergeVersions($this->config[$key][$name], $version)
133
                    : $version;
134
            }
135
        }
136
    }
137
138
    protected function mergeVersions($a, $b)
139
    {
140
        $a = trim($a);
141
        $b = trim($b);
142
        if ($a === $b || $this->isMoreVersion($b, $a)) {
143
            return $a;
144
        } elseif ($this->isMoreVersion($a, $b)) {
145
            return $b;
146
        } else {
147
            return $a . ' ' . $b;
148
        }
149
    }
150
151
    /**
152
     * Check if $a is more then $b, like: a="1.1 || 2.2" b="1.1"
153
     * Possible optimization.
154
     */
155
    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...
156
    {
157
        return $this->isAnyVersion($a);
158
        // WRONG: return strpos($b, $a) !== false;
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% 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...
159
    }
160
161
    public function isAnyVersion($version)
162
    {
163
        return !$version || $version === '*' || $version === '>=0.0.0';
164
    }
165
166
    /**
167
     * Set config.
168
     * @param array $config
169
     */
170
    public function setConfig(array $config)
171
    {
172
        $this->config = $config;
173
    }
174
175
    /**
176
     * Run given action: show notice, write config and run `perform`.
177
     * @param string $action
178
     */
179
    public function runAction($action)
180
    {
181
        $doing = trim($action, 'e') . 'ing';
182
        $this->plugin->io->write($doing . ' ' . $this->name . ' dependencies...');
183
        $this->writeConfig($this->file, $this->config);
184
        $this->perform($action);
185
    }
186
187
    /**
188
     * Run installation. Specific for every package manager.
189
     * @param string $action
190
     */
191
    protected function perform($action)
192
    {
193
        if ($this->passthru($action)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->passthru($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...
194
            $this->plugin->io->write('failed ' . $name . ' ' . $action);
0 ignored issues
show
Bug introduced by
The variable $name does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
195
        }
196
    }
197
198
    /**
199
     * Prepares arguments and runs it with passthru.
200
     * @param string $args
201
     * @return int exit code
202
     */
203
    public function passthru($args = '')
204
    {
205
        passthru($this->getBin() . $this->prepareCommand($args), $exitcode);
206
        return $exitcode;
207
    }
208
209
    /**
210
     * Prepares given command arguments.
211
     * @param string|array $args
212
     * @return string
213
     */
214
    public function prepareCommand($args = '')
215
    {
216
        if (is_string($args)) {
217
            $res = ' ' . trim($args);
218
        } else {
219
            $res = '';
220
            foreach ($args as $a) {
221
                $res .= ' ' . escapeshellarg($a);
222
            }
223
        }
224
225
        return $res;
226
    }
227
228
    /**
229
     * Set path to binary.
230
     * @param string $value
231
     */
232
    public function setBin($value)
233
    {
234
        $this->bin = $value;
235
    }
236
237
    /**
238
     * Get path to binary.
239
     * @return string
240
     */
241
    public function getBin()
242
    {
243
        if ($this->bin === null) {
244
            $this->bin = $this->detectBin();
245
        }
246
247
        return $this->bin;
248
    }
249
250
    /**
251
     * Find path binary.
252
     * @return string
253
     */
254
    public function detectBin()
255
    {
256
        if (isset($this->plugin->getPackages()[$this->phpPackage])) {
257
            return $this->plugin->getVendorDir() . '/bin/' . $this->phpBin;
258
        }
259
260
        return $this->name;
261
    }
262
263
}
264