Completed
Push — master ( 7a70a9...536f20 )
by Andrii
02:06
created

PackageManager::getBin()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4286
cc 2
eloc 4
nc 2
nop 0
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';
81
        $this->config = array_merge(
82
            $this->config,
83
            $this->readConfig(file_exists($dist) ? $dist : $this->file)
84
        );
85
    }
86
87
    public function readConfig($file)
88
    {
89
        $jsonFile = new JsonFile($file);
90
        $config = $jsonFile->exists() ? $jsonFile->read() : [];
91
        foreach ($this->keyTable as $key) {
0 ignored issues
show
Bug introduced by
The expression $this->keyTable of type string is not traversable.
Loading history...
92
            if (!isset($config[$key])) {
93
                $config[$key] = [];
94
            }
95
        }
96
        return $config;
97
    }
98
99
    public function writeConfig($file, $config)
100
    {
101
        foreach ($this->keyTable as $key) {
0 ignored issues
show
Bug introduced by
The expression $this->keyTable of type string is not traversable.
Loading history...
102
            if (isset($config[$key]) && !$config[$key]) {
103
                unset($config[$key]);
104
            }
105
        }
106
        $jsonFile = new JsonFile($file);
107
        $jsonFile->write($config);
108
    }
109
110
    public function scanPackage(CompletePackage $package)
111
    {
112
        $extra = $package->getExtra();
113
        $config = [];
114
        foreach (['require', 'require-dev'] as $key) {
115
            $name = $this->name . '-' . $key;
116
            if (isset($extra[$name])) {
117
                $config[$key] = $extra[$name];
118
            }
119
        }
120
        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...
121
            $this->mergeConfig($config);
122
        }
123
    }
124
125
    protected function mergeConfig($config)
126
    {
127
        foreach ($config as $key => $packages) {
128
            $key = $this->keyTable[$key];
129
            foreach ($packages as $name => $version) {
130
                $this->config[$key][$name] = isset($this->config[$key][$name])
131
                    ? $this->mergeVersions($this->config[$key][$name], $version)
132
                    : $version;
133
            }
134
        }
135
    }
136
137
    protected function mergeVersions($a, $b)
138
    {
139
        $a = trim($a);
140
        $b = trim($b);
141
        if ($a === $b || $this->isMoreVersion($b, $a)) {
142
            return $a;
143
        } elseif ($this->isMoreVersion($a, $b)) {
144
            return $b;
145
        } else {
146
            return $a . ' ' . $b;
147
        }
148
    }
149
150
    /**
151
     * Check if $a is more then $b, like: a="1.1 || 2.2" b="1.1"
152
     * Possible optimization.
153
     */
154
    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...
155
    {
156
        return $this->isAnyVersion($a);
157
        // 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...
158
    }
159
160
    public function isAnyVersion($version)
161
    {
162
        return !$version || $version === '*' || $version === '>=0.0.0';
163
    }
164
165
    public function setConfig(array $config)
166
    {
167
        $this->config = $config;
168
    }
169
170
    /**
171
     * Install packages.
172
     */
173
    public function installPackages()
174
    {
175
        $this->plugin->io->write('installing ' . $this->name . ' dependencies...');
176
        $this->writeConfig($this->file, $this->config);
177
        $this->runInstall();
178
    }
179
180
    /**
181
     * Run installation. Specific for every package manager.
182
     */
183
    abstract protected function runInstall();
184
185
    /**
186
     * Prepares given command arguments.
187
     * @param string|array $args
188
     * @return string
189
     */
190
    public function prepareCommand($args = '')
191
    {
192
        if (is_string($args)) {
193
            $res = ' ' . trim($args);
194
        } else {
195
            $res = '';
196
            foreach ($args as $a) {
197
                $res .= ' ' . escapeshellarg($a);
198
            }
199
        }
200
201
        return $res;
202
    }
203
204
    /**
205
     * Prepares arguments and runs it with passthru.
206
     * @param string $args
207
     * @return int exit code
208
     */
209
    public function passthru($args = '')
210
    {
211
        passthru($this->getBin() . $this->prepareCommand($args), $exitcode);
212
        return $exitcode;
213
    }
214
215
    /**
216
     * Set path to binary.
217
     * @param string $value
218
     */
219
    public function setBin($value)
220
    {
221
        $this->bin = $value;
222
    }
223
224
    /**
225
     * Get path to binary.
226
     * @return string
227
     */
228
    public function getBin()
229
    {
230
        if ($this->bin === null) {
231
            $this->bin = $this->detectBin();
232
        }
233
234
        return $this->bin;
235
    }
236
237
    /**
238
     * Find path binary.
239
     * @return string
240
     */
241
    public function detectBin()
242
    {
243
        if (isset($this->plugin->getPackages()[$this->phpPackage])) {
244
            return './vendor/bin/' . $this->phpBin;
245
        }
246
247
        return $this->name;
248
    }
249
}
250