| 
<?php
 /**
 * This file is part of the PHP Generics package.
 *
 * @package Generics
 */
 namespace Generics\Streams;
 
 use Generics\Util\Interpolator;
 
 /**
 * This class provides a memory stream for both input and output
 *
 * @author Maik Greubel <[email protected]>
 */
 class MemoryStream implements InputOutputStream
 {
 use Interpolator {
 interpolate as tinterpolate;
 }
 
 /**
 * The local memory buffer
 *
 * @var string
 */
 private $memory;
 
 /**
 * Current position in memory buffer
 *
 * @var int
 */
 private $current;
 
 /**
 * Whether it is possible to perform reading action
 *
 * @var boolean
 */
 private $ready;
 
 /**
 * Whether stream is closed
 *
 * @var boolean
 */
 private $closed;
 
 /**
 * Create a new MemoryStream
 *
 * @param InputStream $in
 *            optional existing input stream - will be copied
 */
 public function __construct(InputStream $in = null)
 {
 $this->memory = "";
 if ($in != null) {
 $copy = clone $in;
 $copy->reset();
 while ($copy->ready()) {
 $this->memory .= $copy->read();
 }
 $copy->close();
 }
 $this->current = 0;
 $this->ready = true;
 $this->closed = false;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::close()
 */
 public function close()
 {
 unset($this->memory);
 $this->current = 0;
 $this->ready = false;
 $this->closed = true;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::ready()
 */
 public function ready(): bool
 {
 return $this->ready;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\OutputStream::write()
 */
 public function write($buffer)
 {
 if ($this->closed) {
 throw new StreamException("Stream is not open");
 }
 $this->memory .= $buffer;
 $this->ready = true;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\InputStream::read()
 */
 public function read($length = 1, $offset = null): string
 {
 if ($this->closed) {
 throw new StreamException("Stream is not open");
 }
 
 if ($offset !== null) {
 $this->current = intval($offset);
 }
 
 if (strlen($this->memory) <= $this->current) {
 $this->ready = false;
 return "";
 }
 
 if (strlen($this->memory) - $this->current < $length) {
 $length = strlen($this->memory) - $this->current;
 }
 
 $out = substr($this->memory, $this->current, $length);
 $this->current += $length;
 
 if ($this->current == strlen($this->memory)) {
 $this->ready = false;
 }
 
 return $out;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Countable::count()
 */
 public function count(): int
 {
 if ($this->closed) {
 throw new StreamException("Stream is not open");
 }
 if (! isset($this->memory)) {
 return 0;
 }
 return strlen($this->memory);
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Resettable::reset()
 */
 public function reset()
 {
 if ($this->closed) {
 throw new StreamException("Stream is not open");
 }
 $this->current = 0;
 $this->ready = true;
 }
 
 /**
 * Write to stream by interpolation of context vars into a string
 *
 * @param string $string
 *            The string to interpolate, may contains placeholders in format {placeholder}.
 * @param array $context
 *            The context array containing the associative replacers and its values.
 */
 public function interpolate($string, array $context)
 {
 $this->write($this->tinterpolate($string, $context));
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\OutputStream::isWriteable()
 */
 public function isWriteable(): bool
 {
 return true;
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\OutputStream::flush()
 */
 public function flush()
 {
 if ($this->closed) {
 throw new StreamException("Stream is not open");
 }
 
 unset($this->memory);
 $this->memory = "";
 $this->reset();
 }
 
 /**
 *
 * {@inheritdoc}
 * @see \Generics\Streams\Stream::isOpen()
 */
 public function isOpen(): bool
 {
 return true;
 }
 
 /**
 * Retrieve the whole memory string content
 *
 * @return string
 */
 public function slurp(): string
 {
 $str = "";
 while ($this->ready()) {
 $str .= $this->read($this->count());
 }
 
 return $str;
 }
 }
 
 |