Passed
Branch feature/cli (4cfcac)
by Andrea Marco
01:47
created

path()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 10
nc 8
nop 1
dl 0
loc 16
rs 9.2222
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cerbero\Enum;
6
7
use Cerbero\Enum\Enums\Backed;
8
use Generator;
9
10
/**
11
 * Yield the content of the given path line by line.
12
 *
13
 * @return Generator<int, string>
14
 */
15
function yieldLines(string $path): Generator
16
{
17
    $stream = fopen($path, 'rb');
18
19
    try {
20
        while (($line = fgets($stream, 1024)) !== false) {
21
            yield $line;
22
        }
23
    } finally {
24
        is_resource($stream) && fclose($stream);
25
    }
26
}
27
28
/**
29
 * Retrieve the PSR-4 map of the composer file.
30
 *
31
 * @return array<string, string>
32
 */
33
function psr4(): array
34
{
35
    if (! is_file($path = Enums::basePath('composer.json'))) {
36
        return [];
37
    }
38
39
    $composer = (array) json_decode((string) file_get_contents($path), true);
40
41
    /** @var array<string, string> */
42
    return $composer['autoload']['psr-4'] ?? [];
43
}
44
45
/**
46
 * Retrieve the traits used by the given target recursively.
47
 *
48
 * @return array<class-string, class-string>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<class-string, class-string> at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in array<class-string, class-string>.
Loading history...
49
 */
50
function traitsUsedBy(string $target): array
51
{
52
    $traits = class_uses($target) ?: [];
53
54
    foreach ($traits as $trait) {
55
        $traits += traitsUsedBy($trait);
56
    }
57
58
    return $traits;
59
}
60
61
/**
62
 * Retrieve the given value in snake case.
63
 */
64
function snake(string $value, string $delimiter = '_'): string
65
{
66
    $value = preg_replace('/\s+/u', '', ucwords($value));
67
68
    return strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
69
}
70
71
/**
72
 * Retrieve the given value in camel case.
73
 */
74
function camel(string $value): string
75
{
76
    $words = explode(' ', str_replace(['-', '_'], ' ', $value));
77
    $studly = array_map('ucfirst', $words);
78
79
    return lcfirst(implode($studly));
80
}
81
82
/**
83
 * Parse the given raw string containing the name and value of a case.
84
 *
85
 * @return array<string, string|int>
86
 */
87
function parseCaseValue(string $raw): array
88
{
89
    [$rawName, $rawValue] = explode('=', $raw, limit: 2);
90
    $trimmed = trim($rawValue);
91
    $value = is_numeric($trimmed) ? (int) $trimmed : $trimmed;
92
93
    return [trim($rawName) => $value];
94
}
95
96
/**
97
 * Retrieve the given cases, optionally backed by values.
98
 *
99
 * @param string[] $cases
100
 * @return array<string, string|int|null>
101
 * @throws \ValueError
102
 */
103
function backCases(array $cases, ?string $backed): array
104
{
105
    $backedCases = [];
106
107
    $backed = match (true) {
108
        is_string($backed) => Backed::fromName($backed),
0 ignored issues
show
Bug introduced by
The method fromName() does not exist on Cerbero\Enum\Enums\Backed. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

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

108
        is_string($backed) => Backed::/** @scrutinizer ignore-call */ fromName($backed),
Loading history...
109
        default => str_contains($cases[0] ?? '', '=') ? Backed::custom : Backed::pure,
110
    };
111
112
    $pairs = $backed->yieldPairs();
0 ignored issues
show
Bug introduced by
The method yieldPairs() does not exist on null. ( Ignorable by Annotation )

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

112
    /** @scrutinizer ignore-call */ 
113
    $pairs = $backed->yieldPairs();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
113
114
    foreach ($cases as $case) {
115
        $backedCases += $pairs->send($case);
116
117
        $pairs->next();
118
    }
119
120
    return $backedCases;
121
}
122
123
/**
124
 * Retrieve the backing type depending on the given value.
125
 */
126
function backingType(mixed $value): ?string
127
{
128
    return match (true) {
129
        is_int($value) => 'int',
130
        is_string($value) => str_contains($value, '<<') ? 'int' : 'string',
131
        default => null,
132
    };
133
}
134
135
/**
136
 * Retrieve the common type among the given types.
137
 */
138
function commonType(string ...$types): string
139
{
140
    $null = '';
141
    $types = array_unique($types);
142
143
    if (($index = array_search('null', $types)) !== false) {
144
        $null = '?';
145
146
        unset($types[$index]);
147
    }
148
149
    if (count($types) == 1) {
150
        return $null . reset($types);
151
    }
152
153
    return implode('|', $types) . ($null ? '|null' : '');
154
}
155
156
/**
157
 * Retrieve only the name of the given namespace.
158
 */
159
function className(string $namespace): string
160
{
161
    return basename(strtr($namespace, '\\', '/'));
162
}
163
164
/**
165
 * Split the given FQCN into namespace and name.
166
 *
167
 * @param class-string $namespace
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
168
 * @return list<string>
169
 */
170
function splitNamespace(string $namespace): array
171
{
172
    $segments = explode('\\', $namespace);
173
    $name = (string) array_pop($segments);
174
175
    return [implode('\\', $segments), $name];
176
}
177
178
/**
179
 * Retrieve the absolute path of the given namespace.
180
 *
181
 * @param class-string $namespace
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string.
Loading history...
182
 */
183
function namespaceToPath(string $namespace): string
184
{
185
    $path = Enums::basePath($namespace) . '.php';
186
187
    foreach (psr4() as $root => $relative) {
188
        if (str_starts_with($namespace, $root)) {
189
            $relative = path($relative) . DIRECTORY_SEPARATOR;
190
191
            return strtr($path, [$root => $relative]);
192
        }
193
    }
194
195
    return $path;
196
}
197
198
/**
199
 * Retrieve the normalized path.
200
 */
201
function path(string $path): string
202
{
203
    $segments = [];
204
    $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
205
    $path = rtrim($path, DIRECTORY_SEPARATOR);
206
    $head = str_starts_with($path, DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
207
208
    foreach (explode(DIRECTORY_SEPARATOR, $path) as $segment) {
209
        if ($segment === '..') {
210
            array_pop($segments);
211
        } elseif ($segment !== '' && $segment !== '.') {
212
            $segments[] = $segment;
213
        }
214
    }
215
216
    return $head . implode(DIRECTORY_SEPARATOR, $segments);
217
}
218