Test Failed
Push — master ( 188ff4...514edf )
by Théo
13:42 queued 38s
created

AutoloadPrefixer::mergeNamespaces()   B

Complexity

Conditions 9
Paths 5

Size

Total Lines 27

Duplication

Lines 10
Ratio 37.04 %

Code Coverage

Tests 10
CRAP Score 9.3752

Importance

Changes 0
Metric Value
cc 9
nc 5
nop 3
dl 10
loc 27
rs 8.0555
c 0
b 0
f 0
ccs 10
cts 12
cp 0.8333
crap 9.3752
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper\Scoper\Composer;
16
17
use Humbug\PhpScoper\Whitelist;
18
use stdClass;
19
use function array_map;
20
use function is_array;
21
use function is_string;
22
use function str_replace;
23
24
/**
25
 * @private
26
 */
27
final class AutoloadPrefixer
28
{
29
    /**
30
     * @param stdClass $contents Decoded JSON
31
     * @param string   $prefix
32 9
     *
33
     * @return stdClass Prefixed decoded JSON
34 9
     */
35 9
    public static function prefixPackageAutoloadStatements(stdClass $contents, string $prefix, Whitelist $whitelist): stdClass
36
    {
37
        if (isset($contents->autoload)) {
38 9
            $contents->autoload = self::prefixAutoloadStatements($contents->autoload, $prefix, $whitelist);
39 3
        }
40
41
        if (isset($contents->{'autoload-dev'})) {
42 9
            $contents->{'autoload-dev'} = self::prefixAutoloadStatements($contents->{'autoload-dev'}, $prefix, $whitelist);
43 1
        }
44
45
        if (isset($contents->extra, $contents->extra->laravel, $contents->extra->laravel->providers)) {
46 9
            $contents->extra->laravel->providers = self::prefixLaravelProviders($contents->extra->laravel->providers, $prefix, $whitelist);
47
        }
48
49 9
        return $contents;
50
    }
51 9
52 1
    private static function prefixAutoloadStatements(stdClass $autoload, string $prefix, Whitelist $whitelist): stdClass
53
    {
54
        if (false === isset($autoload->{'psr-4'}) && false === isset($autoload->{'psr-0'})) {
55 8
            return $autoload;
56 5
        }
57 5
58 5
        if (isset($autoload->{'psr-0'})) {
59
            $autoload->{'psr-4'} = self::mergePSR0And4(
60
                (array) $autoload->{'psr-0'},
61 8
                (array) ($autoload->{'psr-4'} ?? new stdClass())
62
            );
63 8
        }
64 8
        unset($autoload->{'psr-0'});
65
66
        if (isset($autoload->{'psr-4'})) {
67 8
            $autoload->{'psr-4'} = self::prefixAutoload((array) $autoload->{'psr-4'}, $prefix, $whitelist);
68
        }
69
70 8
        return $autoload;
71
    }
72 8
73
    private static function prefixAutoload(array $autoload, string $prefix, Whitelist $whitelist): array
74 8
    {
75 8
        $loader = [];
76
77 8
        foreach ($autoload as $namespace => $paths) {
78
            $newNamespace = $whitelist->belongsToWhitelistedNamespace($namespace)
79
                ? $namespace
80 8
                : sprintf('%s\\%s', $prefix, $namespace)
81
            ;
82
83 8
            $loader[$newNamespace] = $paths;
84
        }
85
86 5
        return $loader;
87
    }
88 5
89
    /**
90 5
     * @param (string|string[])[] $psr0
91 1
     * @param (string|string[])[] $psr4
92
     *
93
     * @return (string|string[])[]
94 5
     */
95
    private static function mergePSR0And4(array $psr0, array $psr4): array
96 5
    {
97 2
        foreach ($psr0 as $namespace => $path) {
98
            //Append backslashes, if needed, since psr-0 does not require this
99 2
            if ('\\' !== substr($namespace, -1)) {
100
                $namespace .= '\\';
101 3
            }
102
103
            $path = self::updatePSR0Path($path, (string) $namespace);
104 5
105
            if (!isset($psr4[$namespace])) {
106
                $psr4[$namespace] = $path;
107 5
108
                continue;
109 5
            }
110
            $psr4[$namespace] = self::mergeNamespaces((string) $namespace, $path, $psr4);
0 ignored issues
show
Bug introduced by
It seems like $path defined by self::updatePSR0Path($path, (string) $namespace) on line 103 can also be of type array; however, Humbug\PhpScoper\Scoper\...ixer::mergeNamespaces() does only seem to accept string|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Documentation introduced by
$psr4 is of type array<string|integer,string|array>, but the function expects a array<integer,string|array<integer,string>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
111 5
        }
112 4
113 1
        return $psr4;
114
    }
115
116 4
    /**
117
     * @param string|string[] $path
118 4
     *
119
     * @return string|string[]
120 2
     */
121 2
    private static function updatePSR0Path($path, string $namespace)
122 1
    {
123
        $namespaceForPsr = str_replace('\\', '/', $namespace);
124
125 2
        if (false === is_array($path)) {
126 2
            if ('/' !== substr($path, -1)) {
127
                $path .= '/';
128
            }
129 2
130
            $path .= $namespaceForPsr.'/';
131
132
            return $path;
133
        }
134
135
        foreach ($path as $key => $item) {
136
            if ('/' !== substr($item, -1)) {
137
                $item .= '/';
138
            }
139
140
            $item .= $namespaceForPsr.'/';
141
            $path[$key] = $item;
142
        }
143
144
        return $path;
145 3
    }
146
147
    /**
148 3
     * Deals with the 4 possible scenarios:
149 1
     *       PSR0 | PSR4
150
     * array      |
151
     * string     |
152 2
     * or simply the namespace not existing as a psr-4 entry.
153 1
     *
154
     * @param string              $psr0Namespace
155 1
     * @param string|string[]     $psr0Path
156
     * @param (string|string[])[] $psr4
157
     *
158
     * @return string|string[]
159 2
     */
160 1
    private static function mergeNamespaces(string $psr0Namespace, $psr0Path, array $psr4)
161
    {
162 1
        // Both strings
163
        if (is_string($psr4[$psr0Namespace]) && is_string($psr0Path)) {
164
            return [$psr4[$psr0Namespace], $psr0Path];
165 1
        }
166 1
167
        // PSR-4 is string, and PSR-0 is array
168 View Code Duplication
        if (is_string($psr4[$psr0Namespace]) && is_array($psr0Path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
169
            $psr0Path[] = $psr4[$psr0Namespace];
170
171
            return $psr0Path;
172 1
        }
173
174 1
        // Psr-4 is array and psr-0 is string
175 View Code Duplication
        if (is_array($psr4[$psr0Namespace]) && is_string($psr0Path)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176 1
            $psr4[$psr0Namespace][] = $psr0Path;
177
178 1
            return $psr4[$psr0Namespace];
179
        }
180 1
181 1
        if (is_array($psr4[$psr0Namespace]) && is_array($psr0Path)) {
182
            return array_merge($psr4[$psr0Namespace], $psr0Path);
183
        }
184
185
        return $psr0Path;
186
    }
187
188
    private static function prefixLaravelProviders(array $providers, string $prefix, Whitelist $whitelist): array
189
    {
190
        return array_map(
191
            static function (string $provider) use ($prefix, $whitelist): string {
192
                return $whitelist->belongsToWhitelistedNamespace($provider)
193
                    ? $provider
194
                    : sprintf('%s\\%s', $prefix, $provider)
195
                ;
196
            },
197
            $providers
198
        );
199
    }
200
}
201