Passed
Pull Request — master (#76)
by Théo
02:28
created

ComposerConfiguration::getDevPackagePaths()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 22
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.2
c 0
b 0
f 0
cc 2
eloc 10
nc 1
nop 5
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Composer;
16
17
use Assert\Assertion;
18
use InvalidArgumentException;
19
use KevinGH\Box\Json\Json;
20
use Seld\JsonLint\ParsingException;
21
use function KevinGH\Box\FileSystem\file_contents;
22
use function KevinGH\Box\FileSystem\make_path_absolute;
23
24
final class ComposerConfiguration
25
{
26
    /**
27
     * Attempts to locate the `composer.json` and `composer.lock` files in the provided base-path in order to collect
28
     * all the dev packages.
29
     *
30
     * @param string $basePath
31
     *
32
     * @return string[] Dev package paths
33
     */
34
    public static function retrieveDevPackages(string $basePath): array
35
    {
36
        Assertion::directory($basePath);
37
38
        $composerFile = make_path_absolute('composer.json', $basePath);
39
        $composerLockFile = make_path_absolute('composer.lock', $basePath);
40
41
        if (file_exists($composerFile)) {
42
            Assertion::readable($composerFile);
43
            Assertion::file($composerFile, 'Expected "%s" to be a file. Directory or link found.');
44
            Assertion::file($composerLockFile, 'Expected "%s" to exists. The file is either missing or a directory/link has been found instead.');
45
46
            $composerFileContents = file_contents($composerFile);
47
            $composerLockFileContents = file_contents($composerLockFile);
48
49
            return self::getDevPackagePaths(
50
                $basePath,
51
                $composerFile,
52
                $composerFileContents,
53
                $composerLockFile,
54
                $composerLockFileContents
55
            );
56
        }
57
58
        return [];
59
    }
60
61
    /**
62
     * @param string $basePath
63
     * @param string $composerFile
64
     * @param string $composerFileContents
65
     * @param string $composerLockFile
66
     * @param string $composerLockFileContents
67
     *
68
     * @return string[] Dev packages paths
69
     */
70
    private static function getDevPackagePaths(
71
        string $basePath,
72
        string $composerFile,
73
        string $composerFileContents,
74
        string $composerLockFile,
75
        string $composerLockFileContents
76
    ): array {
77
        $vendorDir = make_path_absolute(
78
            self::getVendorDir($composerFile, $composerFileContents),
79
            $basePath
80
        );
81
82
        $packageNames = self::getDevPackageNames($composerLockFile, $composerLockFileContents);
83
84
        return array_filter(
85
            array_map(
86
                function (string $packageName) use ($vendorDir): ?string {
87
                    $realPath = realpath($vendorDir.DIRECTORY_SEPARATOR.$packageName);
88
89
                    return false !== $realPath ? $realPath : null;
1 ignored issue
show
introduced by
The condition false !== $realPath can never be false.
Loading history...
90
                },
91
                $packageNames
92
            )
93
        );
94
    }
95
96
    private static function getVendorDir(string $composerFile, string $composerFileContents): string
97
    {
98
        try {
99
            $config = (new Json())->decode($composerFileContents, true);
100
        } catch (ParsingException $exception) {
101
            throw new InvalidArgumentException(
102
                sprintf(
103
                    'Expected the file "%s" to be a valid composer.json file but an error has been found: %s',
104
                    $composerFile,
105
                    $exception->getMessage()
106
                ),
107
                0,
108
                $exception->getPrevious()
109
            );
110
        }
111
112
        if (!array_key_exists('config', $config)) {
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type stdClass; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

112
        if (!array_key_exists('config', /** @scrutinizer ignore-type */ $config)) {
Loading history...
113
            return 'vendor';
114
        }
115
116
        if (!array_key_exists('vendor-dir', $config['config'])) {
117
            return 'vendor';
118
        }
119
120
        return $config['config']['vendor-dir'];
121
    }
122
123
    /**
124
     * @param string $composerLockFile
125
     * @param string $composerLockFileContents
126
     *
127
     * @return string[] Names of the dev packages
128
     */
129
    private static function getDevPackageNames(string $composerLockFile, string $composerLockFileContents): array
130
    {
131
        try {
132
            $config = (new Json())->decode($composerLockFileContents, true);
133
        } catch (ParsingException $exception) {
134
            throw new InvalidArgumentException(
135
                sprintf(
136
                    'Expected the file "%s" to be a valid composer.json file but an error has been found: %s',
137
                    $composerLockFile,
138
                    $exception->getMessage()
139
                ),
140
                0,
141
                $exception->getPrevious()
142
            );
143
        }
144
145
        if (!array_key_exists('packages-dev', $config)) {
0 ignored issues
show
Bug introduced by
It seems like $config can also be of type stdClass; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

145
        if (!array_key_exists('packages-dev', /** @scrutinizer ignore-type */ $config)) {
Loading history...
146
            return [];
147
        }
148
149
        return array_map(
150
            function (array $package): string {
151
                return $package['name'];
152
            },
153
            $config['packages-dev']
154
        );
155
    }
156
}
157