Completed
Push — master ( c612e7...f3e1c8 )
by Christian
06:23
created

ComposerJson::unlockPackage()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 24
rs 8.9713
cc 3
eloc 14
nc 3
nop 1
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
class ComposerJson extends JsonFile
30
{
31
    /**
32
     * Add a requirement to composer.json.
33
     *
34
     * @param string $name       The package name.
35
     *
36
     * @param string $constraint The version constraint.
37
     *
38
     * @return ComposerJson
39
     */
40
    public function requirePackage($name, $constraint)
41
    {
42
        return $this->setLink('require', $name, $constraint);
43
    }
44
45
    /**
46
     * Add a requirement to composer.json for dev.
47
     *
48
     * @param string $name       The package name.
49
     *
50
     * @param string $constraint The version constraint.
51
     *
52
     * @return ComposerJson
53
     */
54
    public function requirePackageDev($name, $constraint)
55
    {
56
        return $this->setLink('require-dev', $name, $constraint);
57
    }
58
59
    /**
60
     * Add a replacement to composer.json.
61
     *
62
     * @param string $name       The package name.
63
     *
64
     * @param string $constraint The version constraint.
65
     *
66
     * @return ComposerJson
67
     */
68
    public function replacePackage($name, $constraint)
69
    {
70
        return $this->setLink('replace', $name, $constraint);
71
    }
72
73
    /**
74
     * Add a replacement to composer.json.
75
     *
76
     * @param string $name       The package name.
77
     *
78
     * @param string $constraint The version constraint.
79
     *
80
     * @return ComposerJson
81
     */
82
    public function providePackage($name, $constraint)
83
    {
84
        return $this->setLink('provide', $name, $constraint);
85
    }
86
87
    /**
88
     * Get a requirement from composer.json.
89
     *
90
     * @param string $name The package name.
91
     *
92
     * @return string|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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[] or array<String>.

Loading history...
93
     */
94
    public function getRequire($name)
95
    {
96
        return $this->getLink('require', $name);
97
    }
98
99
    /**
100
     * Get a requirement from composer.json for dev.
101
     *
102
     * @param string $name The package name.
103
     *
104
     * @return string|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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[] or array<String>.

Loading history...
105
     */
106
    public function getRequireDev($name)
107
    {
108
        return $this->getLink('require-dev', $name);
109
    }
110
111
    /**
112
     * Get a requirement from composer.json.
113
     *
114
     * @param string $name The package name.
115
     *
116
     * @return string|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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[] or array<String>.

Loading history...
117
     */
118
    public function getReplace($name)
119
    {
120
        return $this->getLink('replace', $name);
121
    }
122
123
    /**
124
     * Get a requirement from composer.json.
125
     *
126
     * @param string $name The package name.
127
     *
128
     * @return string|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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[] or array<String>.

Loading history...
129
     */
130
    public function getProvide($name)
131
    {
132
        return $this->getLink('provide', $name);
133
    }
134
135
    /**
136
     * Check if a require entry has been defined.
137
     *
138
     * @param string $name The package name.
139
     *
140
     * @return bool
141
     */
142
    public function isRequiring($name)
143
    {
144
        return $this->hasLink('require', $name);
145
    }
146
147
    /**
148
     * Check if a require-dev entry has been defined.
149
     *
150
     * @param string $name The package name.
151
     *
152
     * @return bool
153
     */
154
    public function isRequiringDev($name)
155
    {
156
        return $this->hasLink('require-dev', $name);
157
    }
158
159
    /**
160
     * Check if a replacement entry has been defined.
161
     *
162
     * @param string $name The package name.
163
     *
164
     * @return bool
165
     */
166
    public function isReplacing($name)
167
    {
168
        return $this->hasLink('replace', $name);
169
    }
170
171
    /**
172
     * Check if a provide entry has been defined.
173
     *
174
     * @param string $name The package name.
175
     *
176
     * @return bool
177
     */
178
    public function isProviding($name)
179
    {
180
        return $this->hasLink('provide', $name);
181
    }
182
183
    /**
184
     * Set a link on a dependency to a constraint.
185
     *
186
     * @param string $type       The link type (require, require-dev, provide, replace).
187
     *
188
     * @param string $name       The package name.
189
     *
190
     * @param string $constraint The version constraint.
191
     *
192
     * @return ComposerJson
193
     */
194
    private function setLink($type, $name, $constraint)
195
    {
196
        $this->set($type . '/' . $this->escape($name), $constraint);
197
198
        return $this;
199
    }
200
201
    /**
202
     * Check if a link has been defined.
203
     *
204
     * @param string $type The link type (require, require-dev, provide, replace).
205
     *
206
     * @param string $name The package name.
207
     *
208
     * @return bool
209
     */
210
    private function hasLink($type, $name)
211
    {
212
        return $this->has($type . '/' . $this->escape($name));
213
    }
214
215
    /**
216
     * Check if a link has been defined.
217
     *
218
     * @param string $type The link type (require, require-dev, provide, replace).
219
     *
220
     * @param string $name The package name.
221
     *
222
     * @return string|null
0 ignored issues
show
Documentation introduced by
Should the return type not be array|string|integer|null? Also, consider making the array more specific, something like array<String>, or String[].

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[] or array<String>.

Loading history...
223
     */
224
    private function getLink($type, $name)
225
    {
226
        return $this->get($type . '/' . $this->escape($name));
227
    }
228
229
    /**
230
     * Check if a package is locked.
231
     *
232
     * @param string $packageName The name of the package to test.
233
     *
234
     * @return bool
235
     */
236
    public function isLocked($packageName)
237
    {
238
        return $this->has('extra/tenside/version-locks/' . $this->escape($packageName));
239
    }
240
241
    /**
242
     * Unlock a locked package version.
243
     *
244
     * @param PackageInterface $package The repository holding the packages to convert.
245
     *
246
     * @return ComposerJson
247
     */
248
    public function lockPackage(PackageInterface $package)
249
    {
250
        $name = $package->getPrettyName();
251
        $lock = 'extra/tenside/version-locks/' . $this->escape($name);
252
253
        // Nothing to do?
254
        if ($this->has($lock)) {
255
            return $this;
256
        }
257
258
        if ($this->isRequiring($name)) {
259
            $this->set($lock, $this->getRequire($name));
260
        } else {
261
            $this->set($lock, false);
262
        }
263
264
        $this->requirePackage(
265
            $package->getPrettyName(),
266
            PackageConverter::convertPackageVersion($package, true)
267
        );
268
269
        return $this;
270
    }
271
272
    /**
273
     * Unlock a locked package version.
274
     *
275
     * @param PackageInterface $package The repository holding the packages to convert.
276
     *
277
     * @return ComposerJson
278
     */
279
    public function unlockPackage(PackageInterface $package)
280
    {
281
        $name = $package->getPrettyName();
282
        $lock = 'extra/tenside/version-locks/' . $this->escape($name);
283
284
        // Nothing to do?
285
        if (!$this->has($lock)) {
286
            return $this;
287
        }
288
289
        if (false === ($constraint = $this->get($lock))) {
290
            $this->remove($lock);
291
            $this->requirePackage($name, null);
292
            $this->cleanEmptyArraysInPath('extra/tenside');
293
294
            return $this;
295
        }
296
297
        $this->requirePackage($name, $constraint);
298
        $this->remove($lock);
299
        $this->cleanEmptyArraysInPath('extra/tenside');
300
301
        return $this;
302
    }
303
304
    /**
305
     * Set the locking state for the passed package.
306
     *
307
     * @param PackageInterface $package The package to lock.
308
     *
309
     * @param bool             $state   The desired state.
310
     *
311
     * @return ComposerJson
312
     */
313
    public function setLock(PackageInterface $package, $state)
314
    {
315
        if ((bool) $state) {
316
            return $this->lockPackage($package);
317
        }
318
319
        return $this->unlockPackage($package);
320
    }
321
322
    /**
323
     * Cleanup the array section at the given path by removing empty sub arrays.
324
     *
325
     * @param string $path The base path to remove empty elements from.
326
     *
327
     * @return void
328
     */
329
    private function cleanEmptyArraysInPath($path)
330
    {
331
        $subs = $this->getEntries($path);
332
        if ($subs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $subs of type string[] 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...
333
            foreach ($subs as $subPath) {
334
                $this->cleanEmptyArraysInPath($subPath);
335
            }
336
        }
337
338
        if ([] === $this->get($path)) {
339
            $this->remove($path);
340
        }
341
    }
342
}
343