1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of tenside/core. |
5
|
|
|
* |
6
|
|
|
* (c) Christian Schiffler <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
* |
11
|
|
|
* This project is provided in good faith and hope to be usable by anyone. |
12
|
|
|
* |
13
|
|
|
* @package tenside/core |
14
|
|
|
* @author Christian Schiffler <[email protected]> |
15
|
|
|
* @copyright 2015 Christian Schiffler <[email protected]> |
16
|
|
|
* @license https://github.com/tenside/core/blob/master/LICENSE MIT |
17
|
|
|
* @link https://github.com/tenside/core |
18
|
|
|
* @filesource |
19
|
|
|
*/ |
20
|
|
|
|
21
|
|
|
namespace Tenside\Core\Composer; |
22
|
|
|
|
23
|
|
|
use Composer\Package\PackageInterface; |
24
|
|
|
use Tenside\Core\Util\JsonFile; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* This class abstracts the composer.json file manipulation. |
28
|
|
|
* |
29
|
|
|
* @SuppressWarnings(PHPMD.TooManyPublicMethods) |
30
|
|
|
*/ |
31
|
|
|
class ComposerJson extends JsonFile |
32
|
|
|
{ |
33
|
|
|
/** |
34
|
|
|
* Add a requirement to composer.json. |
35
|
|
|
* |
36
|
|
|
* @param string $name The package name. |
37
|
|
|
* |
38
|
|
|
* @param string $constraint The version constraint. |
39
|
|
|
* |
40
|
|
|
* @return ComposerJson |
41
|
|
|
*/ |
42
|
|
|
public function requirePackage($name, $constraint) |
43
|
|
|
{ |
44
|
|
|
return $this->setLink('require', $name, $constraint); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Add a requirement to composer.json for dev. |
49
|
|
|
* |
50
|
|
|
* @param string $name The package name. |
51
|
|
|
* |
52
|
|
|
* @param string $constraint The version constraint. |
53
|
|
|
* |
54
|
|
|
* @return ComposerJson |
55
|
|
|
*/ |
56
|
|
|
public function requirePackageDev($name, $constraint) |
57
|
|
|
{ |
58
|
|
|
return $this->setLink('require-dev', $name, $constraint); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Add a replacement to composer.json. |
63
|
|
|
* |
64
|
|
|
* @param string $name The package name. |
65
|
|
|
* |
66
|
|
|
* @param string $constraint The version constraint. |
67
|
|
|
* |
68
|
|
|
* @return ComposerJson |
69
|
|
|
*/ |
70
|
|
|
public function replacePackage($name, $constraint) |
71
|
|
|
{ |
72
|
|
|
return $this->setLink('replace', $name, $constraint); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Add a replacement to composer.json. |
77
|
|
|
* |
78
|
|
|
* @param string $name The package name. |
79
|
|
|
* |
80
|
|
|
* @param string $constraint The version constraint. |
81
|
|
|
* |
82
|
|
|
* @return ComposerJson |
83
|
|
|
*/ |
84
|
|
|
public function providePackage($name, $constraint) |
85
|
|
|
{ |
86
|
|
|
return $this->setLink('provide', $name, $constraint); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* Get a requirement from composer.json. |
91
|
|
|
* |
92
|
|
|
* @param string $name The package name. |
93
|
|
|
* |
94
|
|
|
* @return string|null |
|
|
|
|
95
|
|
|
*/ |
96
|
|
|
public function getRequire($name) |
97
|
|
|
{ |
98
|
|
|
return $this->getLink('require', $name); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Get a requirement from composer.json for dev. |
103
|
|
|
* |
104
|
|
|
* @param string $name The package name. |
105
|
|
|
* |
106
|
|
|
* @return string|null |
|
|
|
|
107
|
|
|
*/ |
108
|
|
|
public function getRequireDev($name) |
109
|
|
|
{ |
110
|
|
|
return $this->getLink('require-dev', $name); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Get a requirement from composer.json. |
115
|
|
|
* |
116
|
|
|
* @param string $name The package name. |
117
|
|
|
* |
118
|
|
|
* @return string|null |
|
|
|
|
119
|
|
|
*/ |
120
|
|
|
public function getReplace($name) |
121
|
|
|
{ |
122
|
|
|
return $this->getLink('replace', $name); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Get a requirement from composer.json. |
127
|
|
|
* |
128
|
|
|
* @param string $name The package name. |
129
|
|
|
* |
130
|
|
|
* @return string|null |
|
|
|
|
131
|
|
|
*/ |
132
|
|
|
public function getProvide($name) |
133
|
|
|
{ |
134
|
|
|
return $this->getLink('provide', $name); |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Check if a require entry has been defined. |
139
|
|
|
* |
140
|
|
|
* @param string $name The package name. |
141
|
|
|
* |
142
|
|
|
* @return bool |
143
|
|
|
*/ |
144
|
|
|
public function isRequiring($name) |
145
|
|
|
{ |
146
|
|
|
return $this->hasLink('require', $name); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* Check if a require-dev entry has been defined. |
151
|
|
|
* |
152
|
|
|
* @param string $name The package name. |
153
|
|
|
* |
154
|
|
|
* @return bool |
155
|
|
|
*/ |
156
|
|
|
public function isRequiringDev($name) |
157
|
|
|
{ |
158
|
|
|
return $this->hasLink('require-dev', $name); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Check if a replacement entry has been defined. |
163
|
|
|
* |
164
|
|
|
* @param string $name The package name. |
165
|
|
|
* |
166
|
|
|
* @return bool |
167
|
|
|
*/ |
168
|
|
|
public function isReplacing($name) |
169
|
|
|
{ |
170
|
|
|
return $this->hasLink('replace', $name); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Check if a provide entry has been defined. |
175
|
|
|
* |
176
|
|
|
* @param string $name The package name. |
177
|
|
|
* |
178
|
|
|
* @return bool |
179
|
|
|
*/ |
180
|
|
|
public function isProviding($name) |
181
|
|
|
{ |
182
|
|
|
return $this->hasLink('provide', $name); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* Set a link on a dependency to a constraint. |
187
|
|
|
* |
188
|
|
|
* @param string $type The link type (require, require-dev, provide, replace). |
189
|
|
|
* |
190
|
|
|
* @param string $name The package name. |
191
|
|
|
* |
192
|
|
|
* @param string $constraint The version constraint. |
193
|
|
|
* |
194
|
|
|
* @return ComposerJson |
195
|
|
|
*/ |
196
|
|
|
private function setLink($type, $name, $constraint) |
197
|
|
|
{ |
198
|
|
|
$this->set($type . '/' . $this->escape($name), $constraint); |
199
|
|
|
|
200
|
|
|
return $this; |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* Check if a link has been defined. |
205
|
|
|
* |
206
|
|
|
* @param string $type The link type (require, require-dev, provide, replace). |
207
|
|
|
* |
208
|
|
|
* @param string $name The package name. |
209
|
|
|
* |
210
|
|
|
* @return bool |
211
|
|
|
*/ |
212
|
|
|
private function hasLink($type, $name) |
213
|
|
|
{ |
214
|
|
|
return $this->has($type . '/' . $this->escape($name)); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Check if a link has been defined. |
219
|
|
|
* |
220
|
|
|
* @param string $type The link type (require, require-dev, provide, replace). |
221
|
|
|
* |
222
|
|
|
* @param string $name The package name. |
223
|
|
|
* |
224
|
|
|
* @return string|null |
|
|
|
|
225
|
|
|
*/ |
226
|
|
|
private function getLink($type, $name) |
227
|
|
|
{ |
228
|
|
|
return $this->get($type . '/' . $this->escape($name)); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Check if a package is locked. |
233
|
|
|
* |
234
|
|
|
* @param string $packageName The name of the package to test. |
235
|
|
|
* |
236
|
|
|
* @return bool |
237
|
|
|
*/ |
238
|
|
|
public function isLocked($packageName) |
239
|
|
|
{ |
240
|
|
|
return $this->has('extra/tenside/version-locks/' . $this->escape($packageName)); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Unlock a locked package version. |
245
|
|
|
* |
246
|
|
|
* @param PackageInterface $package The repository holding the packages to convert. |
247
|
|
|
* |
248
|
|
|
* @return ComposerJson |
249
|
|
|
*/ |
250
|
|
|
public function lockPackage(PackageInterface $package) |
251
|
|
|
{ |
252
|
|
|
$name = $package->getPrettyName(); |
253
|
|
|
$lock = 'extra/tenside/version-locks/' . $this->escape($name); |
254
|
|
|
|
255
|
|
|
// Nothing to do? |
256
|
|
|
if ($this->has($lock)) { |
257
|
|
|
return $this; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
if ($this->isRequiring($name)) { |
261
|
|
|
$this->set($lock, $this->getRequire($name)); |
262
|
|
|
} else { |
263
|
|
|
$this->set($lock, false); |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
$this->requirePackage( |
267
|
|
|
$package->getPrettyName(), |
268
|
|
|
PackageConverter::convertPackageVersion($package, true) |
269
|
|
|
); |
270
|
|
|
|
271
|
|
|
return $this; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Unlock a locked package version. |
276
|
|
|
* |
277
|
|
|
* @param PackageInterface $package The repository holding the packages to convert. |
278
|
|
|
* |
279
|
|
|
* @return ComposerJson |
280
|
|
|
*/ |
281
|
|
|
public function unlockPackage(PackageInterface $package) |
282
|
|
|
{ |
283
|
|
|
$name = $package->getPrettyName(); |
284
|
|
|
$lock = 'extra/tenside/version-locks/' . $this->escape($name); |
285
|
|
|
|
286
|
|
|
// Nothing to do? |
287
|
|
|
if (!$this->has($lock)) { |
288
|
|
|
return $this; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
if (false === ($constraint = $this->get($lock))) { |
292
|
|
|
$this->remove($lock); |
293
|
|
|
$this->requirePackage($name, null); |
294
|
|
|
$this->cleanEmptyArraysInPath('extra/tenside'); |
295
|
|
|
|
296
|
|
|
return $this; |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
$this->requirePackage($name, $constraint); |
300
|
|
|
$this->remove($lock); |
301
|
|
|
$this->cleanEmptyArraysInPath('extra/tenside'); |
302
|
|
|
|
303
|
|
|
return $this; |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Set the locking state for the passed package. |
308
|
|
|
* |
309
|
|
|
* @param PackageInterface $package The package to lock. |
310
|
|
|
* |
311
|
|
|
* @param bool $state The desired state. |
312
|
|
|
* |
313
|
|
|
* @return ComposerJson |
314
|
|
|
*/ |
315
|
|
|
public function setLock(PackageInterface $package, $state) |
316
|
|
|
{ |
317
|
|
|
if ((bool) $state) { |
318
|
|
|
return $this->lockPackage($package); |
319
|
|
|
} |
320
|
|
|
|
321
|
|
|
return $this->unlockPackage($package); |
322
|
|
|
} |
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* Cleanup the array section at the given path by removing empty sub arrays. |
326
|
|
|
* |
327
|
|
|
* @param string $path The base path to remove empty elements from. |
328
|
|
|
* |
329
|
|
|
* @return void |
330
|
|
|
*/ |
331
|
|
|
private function cleanEmptyArraysInPath($path) |
332
|
|
|
{ |
333
|
|
|
$subs = $this->getEntries($path); |
334
|
|
|
if (!empty($subs)) { |
335
|
|
|
foreach ($subs as $subPath) { |
336
|
|
|
$this->cleanEmptyArraysInPath($subPath); |
337
|
|
|
} |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
if ([] === $this->get($path)) { |
341
|
|
|
$this->remove($path); |
342
|
|
|
} |
343
|
|
|
} |
344
|
|
|
} |
345
|
|
|
|
This check compares the return type specified in the
@return
annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.If the return type contains the type array, this check recommends the use of a more specific type like
String[]
orarray<String>
.