Viewing file: AppendStream.php (5.81 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
declare(strict_types=1);
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
/** * Reads from multiple streams, one after the other. * * This is a read-only stream decorator. */ final class AppendStream implements StreamInterface { /** @var StreamInterface[] Streams being decorated */ private $streams = [];
/** @var bool */ private $seekable = true;
/** @var int */ private $current = 0;
/** @var int */ private $pos = 0;
/** * @param StreamInterface[] $streams Streams to decorate. Each stream must * be readable. */ public function __construct(array $streams = []) { foreach ($streams as $stream) { $this->addStream($stream); } }
public function __toString(): string { try { $this->rewind();
return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } trigger_error(sprintf('%s::__toString exception: %s', self::class, (string) $e), E_USER_ERROR);
return ''; } }
/** * Add a stream to the AppendStream * * @param StreamInterface $stream Stream to append. Must be readable. * * @throws \InvalidArgumentException if the stream is not readable */ public function addStream(StreamInterface $stream): void { if (!$stream->isReadable()) { throw new \InvalidArgumentException('Each stream must be readable'); }
// The stream is only seekable if all streams are seekable if (!$stream->isSeekable()) { $this->seekable = false; }
$this->streams[] = $stream; }
public function getContents(): string { return Utils::copyToString($this); }
/** * Closes each attached stream. */ public function close(): void { $this->pos = $this->current = 0; $this->seekable = true;
foreach ($this->streams as $stream) { $stream->close(); }
$this->streams = []; }
/** * Detaches each attached stream. * * Returns null as it's not clear which underlying stream resource to return. */ public function detach() { $this->pos = $this->current = 0; $this->seekable = true;
foreach ($this->streams as $stream) { $stream->detach(); }
$this->streams = [];
return null; }
public function tell(): int { return $this->pos; }
/** * Tries to calculate the size by adding the size of each stream. * * If any of the streams do not return a valid number, then the size of the * append stream cannot be determined and null is returned. */ public function getSize(): ?int { $size = 0;
foreach ($this->streams as $stream) { $s = $stream->getSize(); if ($s === null) { return null; } $size += $s; }
return $size; }
public function eof(): bool { return !$this->streams || ($this->current >= count($this->streams) - 1 && $this->streams[$this->current]->eof()); }
public function rewind(): void { $this->seek(0); }
/** * Attempts to seek to the given position. Only supports SEEK_SET. */ public function seek($offset, $whence = SEEK_SET): void { if (!$this->seekable) { throw new \RuntimeException('This AppendStream is not seekable'); } elseif ($whence !== SEEK_SET) { throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); }
$this->pos = $this->current = 0;
// Rewind each stream foreach ($this->streams as $i => $stream) { try { $stream->rewind(); } catch (\Exception $e) { throw new \RuntimeException('Unable to seek stream ' .$i.' of the AppendStream', 0, $e); } }
// Seek to the actual position by reading from each stream while ($this->pos < $offset && !$this->eof()) { $result = $this->read(min(8096, $offset - $this->pos)); if ($result === '') { break; } } }
/** * Reads from all of the appended streams until the length is met or EOF. */ public function read($length): string { $buffer = ''; $total = count($this->streams) - 1; $remaining = $length; $progressToNext = false;
while ($remaining > 0) { // Progress to the next stream if needed. if ($progressToNext || $this->streams[$this->current]->eof()) { $progressToNext = false; if ($this->current === $total) { break; } ++$this->current; }
$result = $this->streams[$this->current]->read($remaining);
if ($result === '') { $progressToNext = true; continue; }
$buffer .= $result; $remaining = $length - strlen($buffer); }
$this->pos += strlen($buffer);
return $buffer; }
public function isReadable(): bool { return true; }
public function isWritable(): bool { return false; }
public function isSeekable(): bool { return $this->seekable; }
public function write($string): int { throw new \RuntimeException('Cannot write to an AppendStream'); }
/** * @return mixed */ public function getMetadata($key = null) { return $key ? null : []; } }
|