path()   A
last analyzed

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 Generator;
8
9
/**
10
 * Yield the content of the given path line by line.
11
 *
12
 * @return Generator<int, string>
13
 */
14
function yieldLines(string $path): Generator
15
{
16
    $stream = fopen($path, 'rb');
17
18
    try {
19
        while (($line = fgets($stream, 1024)) !== false) {
20
            yield $line;
21
        }
22
    } finally {
23
        is_resource($stream) && fclose($stream);
24
    }
25
}
26
27
/**
28
 * Retrieve the PSR-4 map of the composer file.
29
 *
30
 * @return array<string, string>
31
 */
32
function psr4(): array
33
{
34
    if (! is_file($path = Enums::basePath('composer.json'))) {
35
        return [];
36
    }
37
38
    $composer = (array) json_decode((string) file_get_contents($path), true);
39
40
    /** @var array<string, string> */
41
    return $composer['autoload']['psr-4'] ?? [];
42
}
43
44
/**
45
 * Retrieve the traits used by the given target recursively.
46
 *
47
 * @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...
48
 */
49
function traitsUsedBy(string $target): array
50
{
51
    $traits = class_uses($target) ?: [];
52
53
    foreach ($traits as $trait) {
54
        $traits += traitsUsedBy($trait);
55
    }
56
57
    return $traits;
58
}
59
60
/**
61
 * Retrieve the given value in snake case.
62
 */
63
function snake(string $value, string $delimiter = '_'): string
64
{
65
    $value = preg_replace('/\s+/u', '', ucwords($value));
66
67
    return strtolower(preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $value));
68
}
69
70
/**
71
 * Retrieve the given value in camel case.
72
 */
73
function camel(string $value): string
74
{
75
    $words = explode(' ', str_replace(['-', '_'], ' ', $value));
76
    $studly = array_map('ucfirst', $words);
77
78
    return lcfirst(implode($studly));
79
}
80
81
/**
82
 * Parse the given raw string containing the name and value of a case.
83
 *
84
 * @return array<string, string|int>
85
 */
86
function parseCaseValue(string $raw): array
87
{
88
    [$rawName, $rawValue] = explode('=', $raw, limit: 2);
89
    $trimmed = trim($rawValue);
90
    $value = is_numeric($trimmed) ? (int) $trimmed : $trimmed;
91
92
    return [trim($rawName) => $value];
93
}
94
95
/**
96
 * Retrieve the backing type depending on the given value.
97
 */
98
function backingType(mixed $value): ?string
99
{
100
    return match (true) {
101
        is_int($value) => 'int',
102
        is_string($value) => str_contains($value, '<<') ? 'int' : 'string',
103
        default => null,
104
    };
105
}
106
107
/**
108
 * Retrieve the common type among the given types.
109
 */
110
function commonType(string ...$types): string
111
{
112
    $null = '';
113
    $types = array_unique($types);
114
115
    if (($index = array_search('null', $types)) !== false) {
116
        $null = '?';
117
118
        unset($types[$index]);
119
    }
120
121
    if (count($types) == 1) {
122
        return $null . reset($types);
123
    }
124
125
    return implode('|', $types) . ($null ? '|null' : '');
126
}
127
128
/**
129
 * Retrieve only the name of the given namespace.
130
 */
131
function className(string $namespace): string
132
{
133
    return basename(strtr($namespace, '\\', '/'));
134
}
135
136
/**
137
 * Split the given FQCN into namespace and name.
138
 *
139
 * @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...
140
 * @return list<string>
141
 */
142
function splitNamespace(string $namespace): array
143
{
144
    $segments = explode('\\', $namespace);
145
    $name = (string) array_pop($segments);
146
147
    return [implode('\\', $segments), $name];
148
}
149
150
/**
151
 * Retrieve the absolute path of the given namespace.
152
 *
153
 * @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...
154
 */
155
function namespaceToPath(string $namespace): string
156
{
157
    $path = Enums::basePath($namespace) . '.php';
158
159
    foreach (psr4() as $root => $relative) {
160
        if (str_starts_with($namespace, $root)) {
161
            $relative = path($relative) . DIRECTORY_SEPARATOR;
162
163
            return strtr($path, [$root => $relative]);
164
        }
165
    }
166
167
    return $path;
168
}
169
170
/**
171
 * Retrieve the normalized path.
172
 */
173
function path(string $path): string
174
{
175
    $segments = [];
176
    $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
177
    $path = rtrim($path, DIRECTORY_SEPARATOR);
178
    $head = str_starts_with($path, DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : '';
179
180
    foreach (explode(DIRECTORY_SEPARATOR, $path) as $segment) {
181
        if ($segment === '..') {
182
            array_pop($segments);
183
        } elseif ($segment !== '' && $segment !== '.') {
184
            $segments[] = $segment;
185
        }
186
    }
187
188
    return $head . implode(DIRECTORY_SEPARATOR, $segments);
189
}
190
191
/**
192
 * Create the directory for the given path if missing.
193
 */
194
function ensureParentDirectory(string $path): bool
195
{
196
    if (file_exists($directory = dirname($path))) {
197
        return true;
198
    }
199
200
    return mkdir($directory, 0755, recursive: true);
201
}
202