Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
86.05% |
148 / 172 |
|
54.84% |
17 / 31 |
CRAP | |
0.00% |
0 / 2 |
Stream | |
87.06% |
148 / 170 |
|
58.62% |
17 / 29 |
91.19 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
new | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
create | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
open | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
7 | |||
openOrCreate | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
6 | |||
openStandardInput | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
openStandardOutput | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
openStandardError | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
openMemory | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
openTemporary | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
4 | |||
getState | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
getMetaData | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
seek | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
seekHead | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
seekTail | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getOffset | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
isEnd | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
flush | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
writeBinary | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
writeBom | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
writeString | |
33.33% |
4 / 12 |
|
0.00% |
0 / 1 |
12.41 | |||
writeLine | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
readBinary | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
readBom | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
4 | |||
readBinaryContents | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
readStringContents | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
readLine | |
98.04% |
50 / 51 |
|
0.00% |
0 / 1 |
16 | |||
release | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
isValidType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
LocalNoReleaseStream | |
0.00% |
0 / 2 |
|
0.00% |
0 / 2 |
6 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
release | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace PeServer\Core\IO; |
6 | |
7 | use PeServer\Core\Binary; |
8 | use PeServer\Core\Collection\Arr; |
9 | use PeServer\Core\Encoding; |
10 | use PeServer\Core\Errors\ErrorHandler; |
11 | use PeServer\Core\IO\File; |
12 | use PeServer\Core\IO\IOUtility; |
13 | use PeServer\Core\IO\StreamMetaData; |
14 | use PeServer\Core\ResourceBase; |
15 | use PeServer\Core\ResultData; |
16 | use PeServer\Core\Text; |
17 | use PeServer\Core\Throws\ArgumentException; |
18 | use PeServer\Core\Throws\IOException; |
19 | use PeServer\Core\Throws\StreamException; |
20 | |
21 | /** |
22 | * ストリーム。 |
23 | * |
24 | * @phpstan-extends ResourceBase<mixed> |
25 | */ |
26 | class Stream extends ResourceBase |
27 | { |
28 | #region define |
29 | |
30 | /** 読み込みモード。 */ |
31 | public const MODE_READ = 1; |
32 | /** 書き込みモード。 */ |
33 | public const MODE_WRITE = 2; |
34 | /** 読み書きモード。 */ |
35 | public const MODE_EDIT = 3; |
36 | |
37 | /** シーク位置: 絶対値。 */ |
38 | public const WHENCE_SET = SEEK_SET; |
39 | /** シーク位置: 現在位置。 */ |
40 | public const WHENCE_CURRENT = SEEK_CUR; |
41 | /** シーク位置: 末尾(設定値は負数となる)。 */ |
42 | public const WHENCE_TAIL = SEEK_END; |
43 | |
44 | #endregion |
45 | |
46 | #region variable |
47 | |
48 | /** 文字列として扱うエンコーディング。(バイナリデータは気にしなくてよい) */ |
49 | private Encoding $encoding; |
50 | |
51 | /** |
52 | * 改行文字。 |
53 | * |
54 | * 書き込み時に使用される(読み込み時は頑張る) |
55 | */ |
56 | public string $newLine = PHP_EOL; |
57 | |
58 | #endregion |
59 | |
60 | /** |
61 | * 生成。 |
62 | * |
63 | * 内部的にしか使わない。 |
64 | * |
65 | * @param $resource ファイルリソース。 |
66 | * @param Encoding|null $encoding |
67 | */ |
68 | public function __construct( |
69 | $resource, |
70 | ?Encoding $encoding = null |
71 | ) { |
72 | parent::__construct($resource); |
73 | |
74 | $this->encoding = $encoding ?? Encoding::getDefaultEncoding(); |
75 | } |
76 | |
77 | #region function |
78 | |
79 | /** |
80 | * `fopen` を使用してファイルストリームを生成。 |
81 | * |
82 | * 原則以下の処理を使ってればよい。 |
83 | * * `self::create` |
84 | * * `self::open` |
85 | * * `self::openOrCreate` |
86 | * * `self::openStandardInput` |
87 | * * `self::openStandardOutput` |
88 | * * `self::openStandardError` |
89 | * * `self::openMemory` |
90 | * * `self::openTemporary` |
91 | * |
92 | * @param string $path ファイルパス。 |
93 | * @param string $mode `fopen:mode` を参照。 |
94 | * @param Encoding|null $encoding |
95 | * @return self |
96 | * @throws IOException |
97 | * @see https://www.php.net/manual/function.fopen.php |
98 | */ |
99 | public static function new(string $path, string $mode, ?Encoding $encoding = null): self |
100 | { |
101 | if (strpos($mode, 'b', 0) === false) { |
102 | $mode .= 'b'; |
103 | } |
104 | |
105 | $result = ErrorHandler::trap(fn () => fopen($path, $mode)); |
106 | if ($result->isFailureOrFalse()) { |
107 | throw new IOException($path); |
108 | } |
109 | |
110 | return new self($result->value, $encoding); |
111 | } |
112 | |
113 | /** |
114 | * 新規ファイルのファイルストリームを生成。 |
115 | * |
116 | * @param string $path ファイルパス。 |
117 | * @param Encoding|null $encoding |
118 | * @return self |
119 | * @throws IOException 既にファイルが存在する。 |
120 | */ |
121 | public static function create(string $path, ?Encoding $encoding = null): self |
122 | { |
123 | if (File::exists($path)) { |
124 | throw new IOException($path); |
125 | } |
126 | |
127 | return self::new($path, 'x+', $encoding); |
128 | } |
129 | |
130 | /** |
131 | * 既存ファイルからファイルストリームを生成。 |
132 | * |
133 | * @param string $path ファイルパス。 |
134 | * @param self::MODE_* $mode `self::MODE_*` を指定。 `self::MODE_CRETE`: ファイルが存在しない場合失敗する。 |
135 | * @param Encoding|null $encoding |
136 | * @return self |
137 | * @throws IOException |
138 | */ |
139 | public static function open(string $path, int $mode, ?Encoding $encoding = null): self |
140 | { |
141 | $openMode = match ($mode) { |
142 | self::MODE_READ => 'r', |
143 | self::MODE_WRITE => 'a', |
144 | self::MODE_EDIT => 'r+', |
145 | }; |
146 | |
147 | if ($mode === self::MODE_WRITE || $mode == self::MODE_EDIT) { |
148 | if (!File::exists($path)) { |
149 | throw new IOException($path); |
150 | } |
151 | } |
152 | |
153 | return self::new($path, $openMode, $encoding); |
154 | } |
155 | |
156 | /** |
157 | * 既存ファイルからファイルストリームを生成、既存ファイルが存在しない場合新規ファイルを作成してファイルストリームを生成。 |
158 | * |
159 | * @param string $path ファイルパス。 |
160 | * @param int $mode `self::MODE_*` を指定。 `self::MODE_CRETE`: ファイルが存在しない場合に空ファイルが作成される(読むしかできないけど開ける。意味があるかは知らん)。 |
161 | * @phpstan-param self::MODE_* $mode |
162 | * @param Encoding|null $encoding |
163 | * @return self |
164 | * @throws IOException |
165 | */ |
166 | public static function openOrCreate(string $path, int $mode, ?Encoding $encoding = null): self |
167 | { |
168 | $openMode = match ($mode) { |
169 | self::MODE_READ => 'r', |
170 | self::MODE_WRITE => 'a', |
171 | self::MODE_EDIT => 'r+', |
172 | }; |
173 | |
174 | if ($mode === self::MODE_READ) { |
175 | if (!File::exists($path)) { |
176 | File::createEmptyFileIfNotExists($path); |
177 | } |
178 | } |
179 | |
180 | return self::new($path, $openMode, $encoding); |
181 | } |
182 | |
183 | /** |
184 | * 標準エラーストリームを開く。 |
185 | * |
186 | * @param Encoding|null $encoding |
187 | * @return self |
188 | */ |
189 | public static function openStandardInput(?Encoding $encoding = null): self |
190 | { |
191 | return new LocalNoReleaseStream(STDIN, $encoding); |
192 | } |
193 | /** |
194 | * 標準出力ストリームを開く。 |
195 | * |
196 | * @param Encoding|null $encoding |
197 | * @return self |
198 | */ |
199 | public static function openStandardOutput(?Encoding $encoding = null): self |
200 | { |
201 | return new LocalNoReleaseStream(STDOUT, $encoding); |
202 | } |
203 | /** |
204 | * 標準エラーストリームを開く。 |
205 | * |
206 | * @param Encoding|null $encoding |
207 | * @return self |
208 | */ |
209 | public static function openStandardError(?Encoding $encoding = null): self |
210 | { |
211 | return new LocalNoReleaseStream(STDERR, $encoding); |
212 | } |
213 | |
214 | /** |
215 | * メモリストリームを開く。 |
216 | * |
217 | * @param Encoding|null $encoding |
218 | * @return self |
219 | */ |
220 | public static function openMemory(?Encoding $encoding = null): self |
221 | { |
222 | return self::new('php://memory', 'r+', $encoding); |
223 | } |
224 | |
225 | /** |
226 | * 一時メモリストリームを開く。 |
227 | * |
228 | * @param int|null $memoryByteSize 指定した値を超過した際に一時ファイルに置き換わる。`null`の場合は 2MB(`php://temp` 参照のこと)。 |
229 | * @param Encoding|null $encoding |
230 | * @return self |
231 | */ |
232 | public static function openTemporary(?int $memoryByteSize = null, ?Encoding $encoding = null): self |
233 | { |
234 | $path = 'php://temp'; |
235 | |
236 | if ($memoryByteSize !== null) { |
237 | if ($memoryByteSize < 0) { |
238 | throw new ArgumentException('$byteSize: ' . $memoryByteSize); |
239 | } |
240 | if ($memoryByteSize) { |
241 | //cspell:disable-next-line |
242 | $path .= '/maxmemory:' . (string)$memoryByteSize; |
243 | } |
244 | } |
245 | |
246 | return self::new($path, 'r+', $encoding); |
247 | } |
248 | |
249 | public function getState(): IOState |
250 | { |
251 | $this->throwIfDisposed(); |
252 | |
253 | $result = ErrorHandler::trap(fn () => fstat($this->resource)); |
254 | if ($result->isFailureOrFalse()) { |
255 | throw new IOException(); |
256 | } |
257 | |
258 | return IOState::createFromStat($result->value); |
259 | } |
260 | |
261 | public function getMetaData(): StreamMetaData |
262 | { |
263 | $this->throwIfDisposed(); |
264 | |
265 | $values = stream_get_meta_data($this->resource); |
266 | return StreamMetaData::createFromStream($values); |
267 | } |
268 | |
269 | /** |
270 | * 現在位置をシーク。 |
271 | * |
272 | * @param int $offset |
273 | * @param int $whence |
274 | * @phpstan-param self::WHENCE_* $whence |
275 | * @return bool |
276 | * @see https://www.php.net/manual/function.fseek.php |
277 | */ |
278 | public function seek(int $offset, int $whence): bool |
279 | { |
280 | $this->throwIfDisposed(); |
281 | |
282 | $result = fseek($this->resource, $offset, $whence); |
283 | return $result === 0; |
284 | } |
285 | |
286 | /** |
287 | * 先頭へシーク。 |
288 | * |
289 | * @return bool |
290 | * @see https://www.php.net/manual/function.rewind.php |
291 | */ |
292 | public function seekHead(): bool |
293 | { |
294 | $this->throwIfDisposed(); |
295 | |
296 | return rewind($this->resource); |
297 | } |
298 | |
299 | /** |
300 | * 末尾へシーク。 |
301 | * |
302 | * @return bool |
303 | */ |
304 | public function seekTail(): bool |
305 | { |
306 | return $this->seek(0, self::WHENCE_TAIL); |
307 | } |
308 | |
309 | /** |
310 | * 現在位置を取得。 |
311 | * |
312 | * @return int |
313 | * @return-param non-negative-int |
314 | * @throws StreamException |
315 | * @see https://www.php.net/manual/function.ftell.php |
316 | */ |
317 | public function getOffset(): int |
318 | { |
319 | $this->throwIfDisposed(); |
320 | |
321 | $result = ftell($this->resource); |
322 | if ($result === false) { |
323 | throw new StreamException(); |
324 | } |
325 | return $result; |
326 | } |
327 | |
328 | /** |
329 | * EOFか。 |
330 | * |
331 | * `feof` ラッパー。 |
332 | * |
333 | * @return bool |
334 | * @see https://www.php.net/manual/function.feof.php |
335 | */ |
336 | public function isEnd(): bool |
337 | { |
338 | if ($this->isDisposed()) { |
339 | return false; |
340 | } |
341 | |
342 | return feof($this->resource); |
343 | } |
344 | |
345 | /** |
346 | * フラッシュ。 |
347 | * |
348 | * @throws StreamException |
349 | * @see https://www.php.net/manual/function.fflush.php |
350 | */ |
351 | public function flush(): void |
352 | { |
353 | $this->throwIfDisposed(); |
354 | |
355 | $result = fflush($this->resource); |
356 | if (!$result) { |
357 | throw new StreamException(); |
358 | } |
359 | } |
360 | |
361 | /** |
362 | * バイナリ書き込み。 |
363 | * |
364 | * @param Binary $data データ。 |
365 | * @param int|null $byteSize 書き込みサイズ。 |
366 | * @phpstan-param non-negative-int|null $byteSize |
367 | * @return int 書き込んだバイトサイズ。 |
368 | * @phpstan-return non-negative-int |
369 | * @throws StreamException |
370 | * @see https://www.php.net/manual/function.fwrite.php |
371 | */ |
372 | public function writeBinary(Binary $data, ?int $byteSize = null): int |
373 | { |
374 | $this->throwIfDisposed(); |
375 | |
376 | $result = ErrorHandler::trap(fn () => fwrite($this->resource, $data->raw, $byteSize)); |
377 | if ($result->isFailureOrFalse()) { |
378 | throw new StreamException(); |
379 | } |
380 | |
381 | return $result->value; |
382 | } |
383 | |
384 | /** |
385 | * 現在のエンコーディングを使用してBOMを書き込み。 |
386 | * |
387 | * * 現在位置に書き込む点に注意(シーク位置が先頭以外であれば無視される)。 |
388 | * * エンコーディングがBOM情報を持っていれば出力されるためBOM不要な場合は使用しないこと。 |
389 | * |
390 | * @return int 書き込まれたバイトサイズ。 |
391 | * @phpstan-return non-negative-int |
392 | */ |
393 | public function writeBom(): int |
394 | { |
395 | $this->throwIfDisposed(); |
396 | |
397 | if ($this->getOffset() !== 0) { |
398 | return 0; |
399 | } |
400 | |
401 | $bom = $this->encoding->getByteOrderMark(); |
402 | if ($bom->count()) { |
403 | return $this->writeBinary($bom); |
404 | } |
405 | |
406 | return 0; |
407 | } |
408 | |
409 | /** |
410 | * 文字列書き込み。 |
411 | * |
412 | * @param string $data データ。 |
413 | * @param int|null $count 文字数。 |
414 | * @phpstan-param non-negative-int|null $count |
415 | * @return int 書き込まれたバイト数。 |
416 | * @phpstan-return non-negative-int |
417 | */ |
418 | public function writeString(string $data, ?int $count = null): int |
419 | { |
420 | $this->throwIfDisposed(); |
421 | |
422 | if (!Text::getByteCount($data)) { |
423 | return 0; |
424 | } |
425 | |
426 | if ($count === null) { |
427 | return $this->writeBinary($this->encoding->getBinary($data)); |
428 | } |
429 | |
430 | if ($count === 0) { |
431 | return 0; |
432 | } |
433 | |
434 | $dataLength = Text::getLength($data); |
435 | if ($dataLength <= $count) { |
436 | return $this->writeBinary($this->encoding->getBinary($data)); |
437 | } |
438 | |
439 | $s = Text::substring($data, 0, $count); |
440 | return $this->writeBinary($this->encoding->getBinary($s)); |
441 | } |
442 | |
443 | /** |
444 | * 文字列を改行付きで書き込み。 |
445 | * |
446 | * @param string $data |
447 | * @return int 書き込まれたバイト数。 |
448 | * @phpstan-return non-negative-int |
449 | */ |
450 | public function writeLine(string $data): int |
451 | { |
452 | return $this->writeString($data . $this->newLine); |
453 | } |
454 | |
455 | /** |
456 | * バイナリ読み込み。 |
457 | * |
458 | * @param int $byteSize 読み込みバイトサイズ。 |
459 | * @phpstan-param positive-int $byteSize |
460 | * @return Binary 読み込んだデータ。 |
461 | * @throws StreamException |
462 | * @see https://www.php.net/manual/function.fread.php |
463 | */ |
464 | public function readBinary(int $byteSize): Binary |
465 | { |
466 | $this->throwIfDisposed(); |
467 | |
468 | $result = ErrorHandler::trap(fn () => fread($this->resource, $byteSize)); |
469 | if ($result->isFailureOrFalse()) { |
470 | throw new StreamException(); |
471 | } |
472 | |
473 | return new Binary($result->value); |
474 | } |
475 | |
476 | /** |
477 | * 現在のエンコーディングを使用してBOMを読み取る。 |
478 | * |
479 | * * 現在位置から読み込む点に注意(シーク位置が先頭以外であれば無視される)。 |
480 | * * 読み込まれた場合(エンコーディングがBOMを持っていて合致した場合)はその分読み進められる。 |
481 | * |
482 | * @return bool BOMが読み込まれたか。 |
483 | */ |
484 | public function readBom(): bool |
485 | { |
486 | $this->throwIfDisposed(); |
487 | |
488 | if ($this->getOffset() !== 0) { |
489 | return false; |
490 | } |
491 | |
492 | $bom = $this->encoding->getByteOrderMark(); |
493 | $bomLength = $bom->count(); |
494 | if (!$bomLength) { |
495 | return false; |
496 | } |
497 | |
498 | $readBuffer = $this->readBinary($bomLength); |
499 | |
500 | if ($bom->isEquals($readBuffer)) { |
501 | return true; |
502 | } |
503 | |
504 | $this->seek(-$readBuffer->count(), self::WHENCE_CURRENT); |
505 | return false; |
506 | } |
507 | |
508 | /** |
509 | * 残りのストリームを全てバイナリとして読み込み。 |
510 | * |
511 | * @param int|null $byteSize 読み込む最大バイト数。`null`で全て。 |
512 | * @phpstan-param non-negative-int|null $byteSize |
513 | * @param int $offset 読み込みを開始する前に移動する位置。 |
514 | * @return Binary |
515 | * @throws StreamException |
516 | * @see https://www.php.net/manual/function.stream-get-contents.php |
517 | */ |
518 | public function readBinaryContents(?int $byteSize = null, int $offset = -1): Binary |
519 | { |
520 | $this->throwIfDisposed(); |
521 | |
522 | $result = stream_get_contents($this->resource, $byteSize, $offset); |
523 | if ($result === false) { |
524 | throw new StreamException(); |
525 | } |
526 | |
527 | return new Binary($result); |
528 | } |
529 | |
530 | /** |
531 | * 残りのストリームを全て文字列として読み込み。 |
532 | * |
533 | * エンコーディングにより復元不可の可能性あり。 |
534 | * |
535 | * @return string |
536 | * @throws StreamException |
537 | */ |
538 | public function readStringContents(): string |
539 | { |
540 | $result = $this->readBinaryContents(); |
541 | if (!$result->count()) { |
542 | return Text::EMPTY; |
543 | } |
544 | |
545 | return $this->encoding->toString($result); |
546 | } |
547 | |
548 | /** |
549 | * 現在のストリーム位置から1行分のデータを取得。 |
550 | * |
551 | * * 位置を進めたり戻したりするので操作可能なストリームで処理すること。 |
552 | * * エンコーディングにより復元不可の可能性あり。 |
553 | * |
554 | * @param int $bufferByteSize 1回読み進めるにあたり予め取得するサイズ。このサイズが改行(CR,LF)のサイズより小さい場合は改行(CR,LF)のサイズが設定される(1指定のutf16とか) |
555 | * @phpstan-param positive-int $bufferByteSize |
556 | * @return string |
557 | */ |
558 | public function readLine(int $bufferByteSize = 1024): string |
559 | { |
560 | $this->throwIfDisposed(); |
561 | |
562 | if ($bufferByteSize < 1) { //@phpstan-ignore-line [DOCTYPE] |
563 | throw new ArgumentException('$bufferByteSize'); |
564 | } |
565 | |
566 | $cr = $this->encoding->getBinary("\r")->raw; |
567 | $lf = $this->encoding->getBinary("\n")->raw; |
568 | $newlineWidth = strlen($cr); |
569 | |
570 | if ($bufferByteSize < $newlineWidth) { |
571 | $bufferByteSize = $newlineWidth; |
572 | } |
573 | |
574 | $startOffset = $this->getOffset(); |
575 | |
576 | $totalCount = 0; |
577 | $totalBuffer = ''; |
578 | |
579 | $findCr = false; |
580 | $findLf = false; |
581 | $hasNewLine = false; |
582 | |
583 | while (!$this->isEnd()) { |
584 | $binary = $this->readBinary($bufferByteSize); |
585 | $currentLength = $binary->count(); |
586 | if (!$currentLength) { |
587 | break; |
588 | } |
589 | |
590 | $currentBuffer = $binary->raw; |
591 | $currentOffset = 0; |
592 | |
593 | while ($currentOffset < $currentLength) { |
594 | if (!$findCr) { |
595 | $findCr = !substr_compare($currentBuffer, $cr, $currentOffset, $newlineWidth, false); |
596 | if (!$findCr) { |
597 | $findLf = !substr_compare($currentBuffer, $lf, $currentOffset, $newlineWidth, false); |
598 | } |
599 | $currentOffset += $newlineWidth; |
600 | } |
601 | if ($findLf) { |
602 | $hasNewLine = true; |
603 | break; |
604 | } |
605 | if ($findCr && $currentOffset < $currentLength) { |
606 | $findLf = !substr_compare($currentBuffer, $lf, $currentOffset, $newlineWidth, false); |
607 | if ($findLf) { |
608 | $currentOffset += $newlineWidth; |
609 | } |
610 | $hasNewLine = true; |
611 | break; |
612 | } |
613 | } |
614 | |
615 | $totalBuffer .= $currentBuffer; |
616 | $totalCount += $currentOffset; |
617 | |
618 | if ($hasNewLine) { |
619 | break; |
620 | } |
621 | } |
622 | |
623 | if ($hasNewLine) { |
624 | $dropWidth = 0; |
625 | if ($findCr) { |
626 | $dropWidth += $newlineWidth; |
627 | } |
628 | if ($findLf) { |
629 | $dropWidth += $newlineWidth; |
630 | } |
631 | $this->seek($startOffset + $totalCount, self::WHENCE_SET); |
632 | $raw = substr($totalBuffer, 0, $totalCount - $dropWidth); |
633 | $str = $this->encoding->toString(new Binary($raw)); |
634 | |
635 | return $str; |
636 | } |
637 | |
638 | return $this->encoding->toString(new Binary($totalBuffer)); |
639 | } |
640 | |
641 | #endregion |
642 | |
643 | #region ResourceBase |
644 | |
645 | protected function release(): void |
646 | { |
647 | fclose($this->resource); |
648 | } |
649 | |
650 | protected function isValidType(string $resourceType): bool |
651 | { |
652 | return $resourceType === 'stream'; |
653 | } |
654 | |
655 | #endregion |
656 | } |
657 | |
658 | //phpcs:ignore PSR1.Classes.ClassDeclaration.MultipleClasses |
659 | class LocalNoReleaseStream extends Stream |
660 | { |
661 | /** |
662 | * 生成 |
663 | * |
664 | * @param $resource ファイルリソース。 |
665 | * @param Encoding|null $encoding |
666 | */ |
667 | public function __construct( |
668 | $resource, |
669 | ?Encoding $encoding = null |
670 | ) { |
671 | parent::__construct($resource, $encoding); |
672 | } |
673 | |
674 | protected function release(): void |
675 | { |
676 | //NOP |
677 | } |
678 | } |