Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
95.83% |
46 / 48 |
|
86.67% |
13 / 15 |
CRAP | |
0.00% |
0 / 1 |
File | |
95.83% |
46 / 48 |
|
86.67% |
13 / 15 |
27 | |
0.00% |
0 / 1 |
createEmptyFileIfNotExists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFileSize | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
readContent | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
saveContent | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
writeContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
appendContent | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
readJsonFile | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
writeJsonFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
exists | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
copy | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
removeFile | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
removeFileIfExists | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
createUniqueFilePath | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
3.04 | |||
createTemporaryFilePath | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
createTemporaryFileStream | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\IO; |
6 | |
7 | use stdClass; |
8 | use PeServer\Core\Binary; |
9 | use PeServer\Core\Cryptography; |
10 | use PeServer\Core\Encoding; |
11 | use PeServer\Core\Environment; |
12 | use PeServer\Core\Errors\ErrorHandler; |
13 | use PeServer\Core\IO\Directory; |
14 | use PeServer\Core\IO\IOState; |
15 | use PeServer\Core\IO\IOUtility; |
16 | use PeServer\Core\IO\Stream; |
17 | use PeServer\Core\Serialization\Json; |
18 | use PeServer\Core\ResultData; |
19 | use PeServer\Core\Serialization\JsonSerializer; |
20 | use PeServer\Core\Text; |
21 | use PeServer\Core\Throws\ArgumentException; |
22 | use PeServer\Core\Throws\FileNotFoundException; |
23 | use PeServer\Core\Throws\IOException; |
24 | use PeServer\Core\Throws\ParseException; |
25 | |
26 | /** |
27 | * ファイル処理系。 |
28 | */ |
29 | abstract class File |
30 | { |
31 | #region function |
32 | |
33 | /** |
34 | * ファイルが存在しない場合に空ファイルの作成。 |
35 | * |
36 | * `touch` ラッパー。 |
37 | * |
38 | * @param string $path |
39 | * @see https://www.php.net/manual/function.touch.php |
40 | */ |
41 | public static function createEmptyFileIfNotExists(string $path): void |
42 | { |
43 | touch($path); |
44 | } |
45 | |
46 | /** |
47 | * ファイルサイズを取得。 |
48 | * |
49 | * @param string $path |
50 | * @return int |
51 | * @phpstan-return non-negative-int |
52 | * @see https://www.php.net/manual/function.filesize.php |
53 | * @throws IOException |
54 | */ |
55 | public static function getFileSize(string $path): int |
56 | { |
57 | $result = ErrorHandler::trap(fn () => filesize($path)); |
58 | if ($result->isFailureOrFalse()) { |
59 | throw new IOException(); |
60 | } |
61 | |
62 | return $result->value; |
63 | } |
64 | |
65 | /** |
66 | * ファイルの内容を取得。 |
67 | * |
68 | * @param string $path |
69 | * @return Binary |
70 | * @see https://www.php.net/manual/function.file-get-contents.php |
71 | * @throws IOException |
72 | */ |
73 | public static function readContent(string $path): Binary |
74 | { |
75 | $result = ErrorHandler::trap(fn () => file_get_contents($path)); |
76 | if ($result->isFailureOrFalse()) { |
77 | throw new IOException($path); |
78 | } |
79 | |
80 | return new Binary($result->value); |
81 | } |
82 | |
83 | /** |
84 | * 対象ファイルに指定データを書き込み。 |
85 | * |
86 | * @param string $path |
87 | * @param Binary $data |
88 | * @param boolean $append |
89 | * @return int 書き込みサイズ。 |
90 | * @throws IOException |
91 | */ |
92 | private static function saveContent(string $path, Binary $data, bool $append): int |
93 | { |
94 | $flag = $append ? FILE_APPEND : 0; |
95 | |
96 | $result = ErrorHandler::trap(fn () => file_put_contents($path, $data->raw, LOCK_EX | $flag)); |
97 | if ($result->isFailureOrFalse()) { |
98 | throw new IOException($path); |
99 | } |
100 | |
101 | return $result->value; |
102 | } |
103 | |
104 | /** |
105 | * 書き込み。 |
106 | * |
107 | * @param string $path |
108 | * @param Binary $data |
109 | * @return int 書き込みサイズ。 |
110 | * @throws IOException |
111 | */ |
112 | public static function writeContent(string $path, Binary $data): int |
113 | { |
114 | return self::saveContent($path, $data, false); |
115 | } |
116 | |
117 | /** |
118 | * 追記。 |
119 | * |
120 | * @param string $path |
121 | * @param Binary $data |
122 | * @return int 書き込みサイズ。 |
123 | * @throws IOException |
124 | */ |
125 | public static function appendContent(string $path, Binary $data): int |
126 | { |
127 | return self::saveContent($path, $data, true); |
128 | } |
129 | |
130 | /** |
131 | * JSONとしてファイル読み込み。 |
132 | * |
133 | * @param string $path パス。 |
134 | * @return array<mixed> 応答JSON。 |
135 | * @param JsonSerializer|null $jsonSerializer JSON処理 |
136 | * @throws IOException |
137 | * @throws ParseException パース失敗。 |
138 | */ |
139 | public static function readJsonFile(string $path, JsonSerializer $jsonSerializer = null): array |
140 | { |
141 | $content = self::readContent($path); |
142 | |
143 | $jsonSerializer ??= new JsonSerializer(); |
144 | /** @var array<mixed> */ |
145 | $value = $jsonSerializer->load($content); |
146 | |
147 | return $value; |
148 | } |
149 | |
150 | /** |
151 | * JSONファイルとして出力。 |
152 | * |
153 | * @param string $path |
154 | * @param array<mixed>|object $data |
155 | * @param JsonSerializer|null $jsonSerializer JSON処理 |
156 | * @return int 書き込みサイズ。 |
157 | * @throws IOException |
158 | * @throws ParseException |
159 | */ |
160 | public static function writeJsonFile(string $path, array|object $data, ?JsonSerializer $jsonSerializer = null): int |
161 | { |
162 | $jsonSerializer ??= new JsonSerializer(); |
163 | $value = $jsonSerializer->save($data); |
164 | |
165 | return self::saveContent($path, $value, false); |
166 | } |
167 | |
168 | /** |
169 | * ファイルが存在するか。 |
170 | * |
171 | * `IOUtility::exists` より速い。 |
172 | * `file_exists`より`is_file`の方が速いらすぃ |
173 | * |
174 | * `is_file` ラッパー。 |
175 | * |
176 | * @param string $path |
177 | * @return boolean 存在するか。 |
178 | * @see https://www.php.net/manual/function.is-file.php |
179 | */ |
180 | public static function exists(string $path): bool |
181 | { |
182 | return is_file($path); |
183 | } |
184 | |
185 | /** |
186 | * ファイルコピー。 |
187 | * |
188 | * @param string $fromPath |
189 | * @param string $toPath |
190 | * @return bool |
191 | * @see https://www.php.net/manual/function.copy.php |
192 | */ |
193 | public static function copy(string $fromPath, string $toPath): bool |
194 | { |
195 | return \copy($fromPath, $toPath); |
196 | } |
197 | |
198 | /** |
199 | * ファイル削除。 |
200 | * |
201 | * @param string $filePath ファイルパス。 |
202 | * @throws IOException |
203 | */ |
204 | public static function removeFile(string $filePath): void |
205 | { |
206 | $result = ErrorHandler::trap(fn () => unlink($filePath)); |
207 | if ($result->isFailureOrFalse()) { |
208 | throw new IOException(); |
209 | } |
210 | } |
211 | |
212 | /** |
213 | * ファイルが存在する場合に削除する。 |
214 | * |
215 | * @param string $filePath |
216 | * @return bool |
217 | */ |
218 | public static function removeFileIfExists(string $filePath): bool |
219 | { |
220 | if (!IOUtility::exists($filePath)) { |
221 | return false; |
222 | } |
223 | |
224 | $result = ErrorHandler::trap(fn () => unlink($filePath)); |
225 | if (!$result->success) { |
226 | return false; |
227 | } |
228 | |
229 | return $result->value; |
230 | } |
231 | |
232 | /** |
233 | * 一意なファイルパスを取得。 |
234 | * |
235 | * `tempnam` ラッパー。 |
236 | * |
237 | * @param string $directoryPath ファイル名の親ディレクトリ。 |
238 | * @param string $prefix プレフィックス。 |
239 | * @return string |
240 | * @see https://www.php.net/manual/function.tempnam.php |
241 | */ |
242 | public static function createUniqueFilePath(string $directoryPath, string $prefix): string |
243 | { |
244 | if (Text::isNullOrWhiteSpace($directoryPath)) { |
245 | throw new ArgumentException('$directoryPath'); |
246 | } |
247 | |
248 | $result = tempnam($directoryPath, $prefix); |
249 | if ($result === false) { |
250 | throw new IOException(); |
251 | } |
252 | |
253 | return $result; |
254 | } |
255 | |
256 | /** |
257 | * 一時ファイルの取得。 |
258 | * |
259 | * @param string $prefix |
260 | * @return string |
261 | */ |
262 | public static function createTemporaryFilePath(string $prefix = ''): string |
263 | { |
264 | if (Text::isNullOrWhiteSpace($prefix)) { |
265 | $prefixLength = PHP_OS === 'Windows' ? 3 : 64; |
266 | $prefix = Cryptography::generateRandomString($prefixLength, Cryptography::FILE_RANDOM_STRING); |
267 | } |
268 | |
269 | return self::createUniqueFilePath(Directory::getTemporaryDirectory(), $prefix); |
270 | } |
271 | |
272 | /** |
273 | * 一時ファイルのストリーム作成。 |
274 | * |
275 | * メモリ・一時ファイル兼メモリのストリームを使用する場合は、 |
276 | * `Stream::openMemory`, `Stream::openTemporary` を参照のこと。 |
277 | * |
278 | * `tmpfile` ラッパー。 |
279 | * |
280 | * @param Encoding|null $encoding |
281 | * @return Stream |
282 | * @throws IOException |
283 | * @see https://www.php.net/manual/function.tmpfile.php |
284 | */ |
285 | public static function createTemporaryFileStream(?Encoding $encoding = null): Stream |
286 | { |
287 | $resource = tmpfile(); |
288 | if ($resource === false) { |
289 | throw new IOException(); |
290 | } |
291 | |
292 | return new Stream($resource, $encoding); |
293 | } |
294 | |
295 | #endregion |
296 | } |