aboutsummaryrefslogtreecommitdiffstats
path: root/includes/Rest/StringStream.php
blob: 18fb6b1812d544796e7b2d9456bf70218248ab70 (plain) (blame)
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
<?php

namespace MediaWiki\Rest;

/**
 * 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 CopyableStreamInterface {
	private $contents = '';
	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 ) {
		if ( $this->offset !== 0 ) {
			$block = substr( $this->contents, $this->offset );
		} else {
			$block = $this->contents;
		}
		fwrite( $stream, $block );
	}

	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;
		} else {
			$ret = substr( $this->contents, $this->offset, $length );
		}
		$this->offset += strlen( $ret );
		return $ret;
	}

	public function getContents() {
		if ( $this->offset === 0 ) {
			$ret = $this->contents;
		} else {
			$ret = substr( $this->contents, $this->offset );
		}
		$this->offset = strlen( $this->contents );
		return $ret;
	}

	public function getMetadata( $key = null ) {
		return null;
	}
}