<?php
/**
* This file is part of the ZBateson\StreamDecorators project.
*
* @license http://opensource.org/licenses/bsd-license.php BSD
*/
namespace ZBateson\StreamDecorators;
use GuzzleHttp\Psr7\BufferStream;
use GuzzleHttp\Psr7\StreamDecoratorTrait;
use Psr\Http\Message\StreamInterface;
use RuntimeException;
/**
* Calls preg_replace on each read operation with the passed pattern and
* replacement string. Should only really be used to find single characters,
* since a pattern intended to match more may be split across multiple read()
* operations.
*
* @author Zaahid Bateson
*/
class PregReplaceFilterStream implements StreamInterface
{
use StreamDecoratorTrait;
/**
* @var BufferStream Buffered stream of input from the underlying stream
*/
private readonly BufferStream $buffer;
public function __construct(
private readonly StreamInterface $stream,
private readonly string $pattern,
private readonly string $replacement,
) {
$this->buffer = new BufferStream();
}
/**
* Returns true if the end of stream has been reached.
*/
public function eof() : bool
{
return ($this->buffer->eof() && $this->stream->eof());
}
/**
* Not supported by PregReplaceFilterStream
*
* @param int $offset
* @param int $whence
* @throws RuntimeException
*/
public function seek($offset, $whence = SEEK_SET) : never
{
throw new RuntimeException('Cannot seek a PregReplaceFilterStream');
}
/**
* Overridden to return false
*/
public function isSeekable() : bool
{
return false;
}
/**
* Fills the BufferStream with at least 8192 characters of input for future
* read operations.
*/
private function fillBuffer(int $length) : void
{
$fill = (int) \max([$length, 8192]);
while ($this->buffer->getSize() < $length) {
$read = $this->stream->read($fill);
if ($read === '') {
break;
}
$this->buffer->write(\preg_replace($this->pattern, $this->replacement, $read));
}
}
/**
* Reads from the underlying stream, filters it and returns up to $length
* bytes.
*
* @param int $length
*/
public function read($length) : string
{
$this->fillBuffer($length);
return $this->buffer->read($length);
}
}