AutoloadPrefixer::mergeNamespaces()   B
last analyzed

Complexity

Conditions 9
Paths 5

Size

Total Lines 26
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 10.2655

Importance

Changes 0
Metric Value
cc 9
eloc 11
nc 5
nop 3
dl 0
loc 26
ccs 3
cts 4
cp 0.75
crap 10.2655
rs 8.0555
c 0
b 0
f 0
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\Symbol\EnrichedReflector;
18
use stdClass;
19
use function array_map;
20
use function array_merge;
21
use function is_array;
22
use function is_string;
23
use function rtrim;
24
use function Safe\sprintf;
25
use function Safe\substr;
26
use function str_replace;
27
use function strpos;
28
29
/**
30
 * @private
31
 */
32
final class AutoloadPrefixer
33
{
34
    private string $prefix;
35
    private EnrichedReflector $enrichedReflector;
36
37 11
    public function __construct(
38
        string $prefix,
39 11
        EnrichedReflector $enrichedReflector
40 11
    ) {
41
        $this->prefix = $prefix;
42
        $this->enrichedReflector = $enrichedReflector;
43 11
    }
44 3
45
    /**
46
     * @param stdClass $contents Decoded JSON
47 11
     *
48 1
     * @return stdClass Prefixed decoded JSON
49
     */
50
    public function prefixPackageAutoloadStatements(stdClass $contents): stdClass
51 11
    {
52
        if (isset($contents->autoload)) {
53
            $contents->autoload = self::prefixAutoloadStatements(
54 11
                $contents->autoload,
55
                $this->prefix,
56 11
                $this->enrichedReflector,
57 1
            );
58
        }
59
60 10
        if (isset($contents->{'autoload-dev'})) {
61 7
            $contents->{'autoload-dev'} = self::prefixAutoloadStatements(
62 7
                $contents->{'autoload-dev'},
63 7
                $this->prefix,
64 7
                $this->enrichedReflector,
65
            );
66
        }
67 7
68 2
        if (isset($contents->extra->laravel->providers)) {
69
            $contents->extra->laravel->providers = self::prefixLaravelProviders(
70 5
                $contents->extra->laravel->providers,
71
                $this->prefix,
72
                $this->enrichedReflector,
73 7
            );
74 5
        }
75
76 2
        return $contents;
77
    }
78
79 10
    private static function prefixAutoloadStatements(
80
        stdClass $autoload,
81 10
        string $prefix,
82 8
        EnrichedReflector $enrichedReflector
83
    ): stdClass {
84
        if (!isset($autoload->{'psr-4'}) && !isset($autoload->{'psr-0'})) {
85 10
            return $autoload;
86
        }
87
88 8
        if (isset($autoload->{'psr-0'})) {
89
            [$psr4, $classMap] = self::transformPsr0ToPsr4AndClassmap(
90 8
                (array) $autoload->{'psr-0'},
91
                (array) ($autoload->{'psr-4'} ?? new stdClass()),
92 8
                (array) ($autoload->{'classmap'} ?? new stdClass()),
93 8
            );
94
95 8
            if ([] === $psr4) {
96
                unset($autoload->{'psr-4'});
97
            } else {
98 8
                $autoload->{'psr-4'} = $psr4;
99
            }
100
101 8
            if ([] === $classMap) {
102
                unset($autoload->{'classmap'});
103
            } else {
104
                $autoload->{'classmap'} = $classMap;
105
            }
106
        }
107
        unset($autoload->{'psr-0'});
108
109 7
        if (isset($autoload->{'psr-4'})) {
110
            $autoload->{'psr-4'} = self::prefixAutoload(
111 7
                (array) $autoload->{'psr-4'},
112
                $prefix,
113 7
                $enrichedReflector,
114 3
            );
115
        }
116
117 7
        return $autoload;
118 2
    }
119
120 2
    private static function prefixAutoload(
121
        array $autoload,
122
        string $prefix,
123 5
        EnrichedReflector $enrichedReflector
124
    ): array {
125 5
        $loader = [];
126 2
127
        foreach ($autoload as $namespace => $paths) {
128 2
            $newNamespace = $enrichedReflector->isExcludedNamespace($namespace)
129
                ? $namespace
130
                : sprintf('%s\\%s', $prefix, $namespace);
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

130
                : /** @scrutinizer ignore-deprecated */ sprintf('%s\\%s', $prefix, $namespace);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
131 3
132
            $loader[$newNamespace] = $paths;
133
        }
134 7
135
        return $loader;
136
    }
137
138
    /**
139
     * @param array<string, (string|string[])> $psr0
140
     * @param (string|string[])[]              $psr4
141
     * @param string[] $classMap
142 5
     */
143
    private static function transformPsr0ToPsr4AndClassmap(array $psr0, array $psr4, array $classMap): array
144 5
    {
145 5
        foreach ($psr0 as $namespace => $path) {
146 5
            // Append backslashes, if needed, since psr-0 does not require this
147
            if ('\\' !== substr($namespace, -1)) {
0 ignored issues
show
Deprecated Code introduced by
The function Safe\substr() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

147
            if ('\\' !== /** @scrutinizer ignore-deprecated */ substr($namespace, -1)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
148
                $namespace .= '\\';
149 5
            }
150 4
151 1
            if (false !== strpos($namespace, '_')) {
152
                $classMap[] = $path;
153
154 4
                continue;
155
            }
156 4
157
            $path = self::updatePSR0Path($path, $namespace);
158
159 2
            if (!isset($psr4[$namespace])) {
160 2
                $psr4[$namespace] = $path;
161 1
162
                continue;
163
            }
164 2
165 2
            $psr4[$namespace] = self::mergeNamespaces($namespace, $path, $psr4);
166
        }
167
168 2
        return [$psr4, $classMap];
169
    }
170
171
    /**
172
     * @param string|string[] $path
173
     *
174
     * @return string|string[]
175
     */
176
    private static function updatePSR0Path($path, string $namespace)
177
    {
178
        $namespaceForPsr = rtrim(
179
            str_replace('\\', '/', $namespace),
180
            '/',
181
        );
182
183
        if (!is_array($path)) {
184 3
            if ('/' !== substr($path, -1)) {
0 ignored issues
show
Deprecated Code introduced by
The function Safe\substr() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

184
            if ('/' !== /** @scrutinizer ignore-deprecated */ substr($path, -1)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
185
                $path .= '/';
186
            }
187 3
188 1
            $path .= $namespaceForPsr.'/';
189
190
            return $path;
191
        }
192 2
193 1
        foreach ($path as $key => $item) {
194
            if ('/' !== substr($item, -1)) {
0 ignored issues
show
Deprecated Code introduced by
The function Safe\substr() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

194
            if ('/' !== /** @scrutinizer ignore-deprecated */ substr($item, -1)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
195 1
                $item .= '/';
196
            }
197
198
            $item .= $namespaceForPsr.'/';
199 2
            $path[$key] = $item;
200 1
        }
201
202 1
        return $path;
203
    }
204
205 1
    /**
206 1
     * Deals with the 4 possible scenarios:
207
     *       PSR0 | PSR4
208
     * array      |
209
     * string     |
210
     * or simply the namespace not existing as a psr-4 entry.
211
     *
212 1
     * @param string|string[] $psr0Path
213
     * @param (string|string[])[] $psr4
214 1
     *
215
     * @return string|string[]
216 1
     */
217
    private static function mergeNamespaces(string $psr0Namespace, $psr0Path, array $psr4)
218 1
    {
219
        // Both strings
220 1
        if (is_string($psr0Path) && is_string($psr4[$psr0Namespace])) {
221 1
            return [$psr4[$psr0Namespace], $psr0Path];
222
        }
223
224
        // PSR-4 is string, and PSR-0 is array
225
        if (is_array($psr0Path) && is_string($psr4[$psr0Namespace])) {
226
            $psr0Path[] = $psr4[$psr0Namespace];
227
228
            return $psr0Path;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $psr0Path returns an array which contains values of type string[] which are incompatible with the documented value type string.
Loading history...
229
        }
230
231
        // PSR-4 is array and PSR-0 is string
232
        if (is_string($psr0Path) && is_array($psr4[$psr0Namespace])) {
233
            $psr4[$psr0Namespace][] = $psr0Path;
234
235
            return $psr4[$psr0Namespace];
236
        }
237
238
        if (is_array($psr0Path) && is_array($psr4[$psr0Namespace])) {
239
            return array_merge($psr4[$psr0Namespace], $psr0Path);
0 ignored issues
show
Bug introduced by
It seems like $psr4[$psr0Namespace] can also be of type string; however, parameter $arrays of array_merge() 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

239
            return array_merge(/** @scrutinizer ignore-type */ $psr4[$psr0Namespace], $psr0Path);
Loading history...
240
        }
241
242
        return $psr0Path;
243
    }
244
245
    private static function prefixLaravelProviders(
246
        array $providers,
247
        string $prefix,
248
        EnrichedReflector $enrichedReflector
249
    ): array {
250
        return array_map(
251
            static fn (string $provider) => $enrichedReflector->isExcludedNamespace($provider)
252
                ? $provider
253
                : sprintf('%s\\%s', $prefix, $provider),
0 ignored issues
show
Deprecated Code introduced by
The function Safe\sprintf() has been deprecated: The Safe version of this function is no longer needed in PHP 8.0+ ( Ignorable by Annotation )

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

253
                : /** @scrutinizer ignore-deprecated */ sprintf('%s\\%s', $prefix, $provider),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
254
            $providers,
255
        );
256
    }
257
}
258