Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
96.88% |
62 / 64 |
|
75.00% |
6 / 8 |
CRAP | |
0.00% |
0 / 1 |
Path | |
96.88% |
62 / 64 |
|
75.00% |
6 / 8 |
30 | |
0.00% |
0 / 1 |
normalize | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
6 | |||
combine | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
4 | |||
getDirectoryPath | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFileName | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFileExtension | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
5.02 | |||
getFileNameWithoutExtension | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
toParts | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
2 | |||
setEnvironmentName | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
9.02 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\IO; |
6 | |
7 | use PeServer\Core\Collection\Arr; |
8 | use PeServer\Core\IO\PathParts; |
9 | use PeServer\Core\Text; |
10 | use PeServer\Core\Throws\ArgumentException; |
11 | |
12 | /** |
13 | * パス処理系。 |
14 | */ |
15 | abstract class Path |
16 | { |
17 | #region function |
18 | |
19 | /** |
20 | * パスの正規化。 |
21 | * |
22 | * @param string $path パス。 |
23 | * @return string 絶対パス。 |
24 | */ |
25 | public static function normalize(string $path): string |
26 | { |
27 | $targetPath = Text::replace($path, ['/', '\\'], DIRECTORY_SEPARATOR); |
28 | $parts = array_filter(Text::split($targetPath, DIRECTORY_SEPARATOR), fn($s) => (bool)mb_strlen($s)); |
29 | $absolutes = []; |
30 | foreach ($parts as $part) { |
31 | if ($part === '.') { |
32 | continue; |
33 | } |
34 | if ($part === '..') { |
35 | array_pop($absolutes); |
36 | } else { |
37 | $absolutes[] = $part; |
38 | } |
39 | } |
40 | |
41 | $result = Text::join(DIRECTORY_SEPARATOR, $absolutes); |
42 | if (Text::getByteCount($targetPath) && $targetPath[0] === DIRECTORY_SEPARATOR) { |
43 | $result = DIRECTORY_SEPARATOR . $result; |
44 | } |
45 | |
46 | return $result; |
47 | } |
48 | |
49 | /** |
50 | * パスの結合。 |
51 | * |
52 | * @param string $basePath ベースとなるパス。 |
53 | * @param string ...$addPaths 連結していくパス。 |
54 | * @return string 結合後のパス。正規化される。 |
55 | */ |
56 | public static function combine(string $basePath, string ...$addPaths): string |
57 | { |
58 | $paths = array_merge([$basePath], array_map(function ($s) { |
59 | return Text::trim($s, '/\\'); |
60 | }, $addPaths)); |
61 | $paths = array_filter($paths, function ($v, $k) { |
62 | return !Text::isNullOrEmpty($v) && ($k === 0 ? true : $v !== '/' && $v !== '\\'); |
63 | }, ARRAY_FILTER_USE_BOTH); |
64 | |
65 | |
66 | $joinedPath = Text::join(DIRECTORY_SEPARATOR, $paths); |
67 | return self::normalize($joinedPath); |
68 | } |
69 | |
70 | /** |
71 | * ディレクトリパスを取得。 |
72 | * |
73 | * `dirname` ラッパー。 |
74 | * |
75 | * @param string $path |
76 | * @return string |
77 | * @see https://php.net/manual/function.dirname.php |
78 | */ |
79 | public static function getDirectoryPath(string $path): string |
80 | { |
81 | return dirname($path); |
82 | } |
83 | |
84 | /** |
85 | * ファイル名を取得。 |
86 | * |
87 | * `basename` ラッパー。 |
88 | * |
89 | * @param string $path |
90 | * @return string |
91 | * @see https://php.net/manual/function.basename.php |
92 | */ |
93 | public static function getFileName(string $path): string |
94 | { |
95 | return basename($path); |
96 | } |
97 | |
98 | /** |
99 | * 拡張子取得。 |
100 | * |
101 | * @param string $path |
102 | * @param boolean $withDot `.` を付与するか。 |
103 | * @return string |
104 | */ |
105 | public static function getFileExtension(string $path, bool $withDot = false): string |
106 | { |
107 | if (Text::isNullOrWhiteSpace($path)) { |
108 | return Text::EMPTY; |
109 | } |
110 | |
111 | $dotIndex = Text::getLastPosition($path, '.'); |
112 | if ($dotIndex === -1) { |
113 | return Text::EMPTY; |
114 | } |
115 | |
116 | $result = Text::substring($path, $dotIndex); |
117 | if ($withDot) { |
118 | return $result; |
119 | } |
120 | |
121 | if (!Text::getByteCount($result)) { |
122 | return Text::EMPTY; |
123 | } |
124 | |
125 | return Text::substring($result, 1); |
126 | } |
127 | |
128 | /** |
129 | * 拡張子を省いたファイル名を取得。 |
130 | * |
131 | * @param string $path |
132 | * @return string |
133 | */ |
134 | public static function getFileNameWithoutExtension(string $path): string |
135 | { |
136 | $fileName = self::getFileName($path); |
137 | $dotIndex = Text::getLastPosition($fileName, '.'); |
138 | if ($dotIndex === -1) { |
139 | return $fileName; |
140 | } |
141 | |
142 | return Text::substring($fileName, 0, $dotIndex); |
143 | } |
144 | |
145 | /** |
146 | * パスの分割。 |
147 | * |
148 | * `pathinfo` ラッパー。 |
149 | * |
150 | * @param string $path |
151 | * @return PathParts |
152 | * @throws ArgumentException |
153 | * @see https://php.net/manual/function.pathinfo.php |
154 | */ |
155 | public static function toParts(string $path): PathParts |
156 | { |
157 | if (Text::isNullOrWhiteSpace($path)) { |
158 | throw new ArgumentException('$path'); |
159 | } |
160 | |
161 | $parts = pathinfo($path); |
162 | |
163 | $result = new PathParts( |
164 | $parts['dirname'], |
165 | $parts['basename'], |
166 | $parts['filename'], |
167 | $parts['extension'] ?? Text::EMPTY |
168 | ); |
169 | |
170 | return $result; |
171 | } |
172 | |
173 | /** |
174 | * ファイルパスに対して環境名を付与する。 |
175 | * |
176 | * * `file.ext` + `debug` = `file.debug.ext` |
177 | * |
178 | * @param string $path パス。 |
179 | * @param string $environment 環境名。 |
180 | * @return string 環境名が付与されたファイルパス。入力値によっては環境に合わせたディレクトリセパレータに変わる可能性あり(`./a.b` => `.\a.env.b`) |
181 | * @throws ArgumentException |
182 | */ |
183 | public static function setEnvironmentName(string $path, string $environment): string |
184 | { |
185 | if (Text::isNullOrWhiteSpace($path)) { |
186 | throw new ArgumentException('$path'); |
187 | } |
188 | if (Text::isNullOrWhiteSpace($environment)) { |
189 | throw new ArgumentException('$environment'); |
190 | } |
191 | |
192 | $parts = self::toParts($path); |
193 | |
194 | $name = Text::isNullOrEmpty($parts->extension) |
195 | ? $parts->fileNameWithoutExtension . '.' . $environment |
196 | : $parts->fileNameWithoutExtension . '.' . $environment . '.' . $parts->extension; |
197 | |
198 | if ($parts->directory === '.' && !(Text::startsWith($path, './', false) || Text::startsWith($path, '.\\', false))) { |
199 | return $name; |
200 | } |
201 | |
202 | if ($parts->directory === DIRECTORY_SEPARATOR) { |
203 | return DIRECTORY_SEPARATOR . $name; |
204 | } |
205 | |
206 | if (Text::endsWith($parts->directory, DIRECTORY_SEPARATOR, false)) { |
207 | return Text::trimEnd($parts->directory, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; |
208 | } |
209 | |
210 | return $parts->directory . DIRECTORY_SEPARATOR . $name; |
211 | } |
212 | |
213 | #endregion |
214 | } |