1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
|
<?php
namespace MediaWiki\Rest;
use InvalidArgumentException;
use Stringable;
/**
* A stream class which uses a string as the underlying storage. Surprisingly,
* Guzzle does not appear to have one of these. BufferStream does not do what
* we want.
*
* The normal use of this class should be to first write to the stream, then
* rewind, then read back the whole buffer with getContents().
*
* Seeking is supported, however seeking past the end of the string does not
* fill with null bytes as in a real file, it throws an exception instead.
*/
class StringStream implements Stringable, CopyableStreamInterface {
/** @var string */
private $contents;
/** @var int */
private $offset = 0;
/**
* Construct a StringStream with the given contents.
*
* The offset will start at 0, ready for reading. If appending to the
* given string is desired, you should first seek to the end.
*
* @param string $contents
*/
public function __construct( $contents = '' ) {
$this->contents = $contents;
}
public function copyToStream( $stream ) {
fwrite( $stream, $this->getContents() );
}
public function __toString() {
return $this->contents;
}
public function close() {
}
public function detach() {
return null;
}
public function getSize() {
return strlen( $this->contents );
}
public function tell() {
return $this->offset;
}
public function eof() {
return $this->offset >= strlen( $this->contents );
}
public function isSeekable() {
return true;
}
public function seek( $offset, $whence = SEEK_SET ) {
switch ( $whence ) {
case SEEK_SET:
$this->offset = $offset;
break;
case SEEK_CUR:
$this->offset += $offset;
break;
case SEEK_END:
$this->offset = strlen( $this->contents ) + $offset;
break;
default:
throw new InvalidArgumentException( "Invalid value for \$whence" );
}
if ( $this->offset > strlen( $this->contents ) ) {
throw new InvalidArgumentException( "Cannot seek beyond the end of a StringStream" );
}
if ( $this->offset < 0 ) {
throw new InvalidArgumentException( "Cannot seek before the start of a StringStream" );
}
}
public function rewind() {
$this->offset = 0;
}
public function isWritable() {
return true;
}
public function write( $string ) {
if ( $this->offset === strlen( $this->contents ) ) {
$this->contents .= $string;
} else {
$this->contents = substr_replace( $this->contents, $string,
$this->offset, strlen( $string ) );
}
$this->offset += strlen( $string );
return strlen( $string );
}
public function isReadable() {
return true;
}
public function read( $length ) {
if ( $this->offset === 0 && $length >= strlen( $this->contents ) ) {
$ret = $this->contents;
} elseif ( $this->offset >= strlen( $this->contents ) ) {
$ret = '';
} else {
$ret = substr( $this->contents, $this->offset, $length );
}
$this->offset += strlen( $ret );
return $ret;
}
public function getContents() {
if ( $this->offset === 0 ) {
$ret = $this->contents;
} elseif ( $this->offset >= strlen( $this->contents ) ) {
$ret = '';
} else {
$ret = substr( $this->contents, $this->offset );
}
$this->offset = strlen( $this->contents );
return $ret;
}
public function getMetadata( $key = null ) {
return null;
}
}
|