1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Thruster\Component\HttpMessage; |
4
|
|
|
|
5
|
|
|
use Psr\Http\Message\StreamInterface; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Class BufferStream |
9
|
|
|
* |
10
|
|
|
* @package Thruster\Component\HttpMessage |
11
|
|
|
* @author Aurimas Niekis <[email protected]> |
12
|
|
|
*/ |
13
|
|
|
class BufferStream implements StreamInterface |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var int |
17
|
|
|
*/ |
18
|
|
|
private $hwm; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @var string |
22
|
|
|
*/ |
23
|
|
|
private $buffer; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @param int $hwm High water mark, representing the preferred maximum |
27
|
|
|
* buffer size. If the size of the buffer exceeds the high |
28
|
|
|
* water mark, then calls to write will continue to succeed |
29
|
|
|
* but will return false to inform writers to slow down |
30
|
|
|
* until the buffer has been drained by reading from it. |
31
|
|
|
*/ |
32
|
11 |
|
public function __construct($hwm = 16384) |
33
|
|
|
{ |
34
|
11 |
|
$this->buffer = ''; |
35
|
11 |
|
$this->hwm = $hwm; |
36
|
11 |
|
} |
37
|
|
|
|
38
|
2 |
|
public function __toString() |
39
|
|
|
{ |
40
|
2 |
|
return $this->getContents(); |
41
|
|
|
} |
42
|
|
|
|
43
|
2 |
|
public function getContents() |
44
|
|
|
{ |
45
|
2 |
|
$buffer = $this->buffer; |
46
|
2 |
|
$this->buffer = ''; |
47
|
|
|
|
48
|
2 |
|
return $buffer; |
49
|
|
|
} |
50
|
|
|
|
51
|
1 |
|
public function close() |
52
|
|
|
{ |
53
|
1 |
|
$this->buffer = ''; |
54
|
1 |
|
} |
55
|
|
|
|
56
|
1 |
|
public function detach() |
57
|
|
|
{ |
58
|
1 |
|
$this->close(); |
59
|
1 |
|
} |
60
|
|
|
|
61
|
2 |
|
public function getSize() |
62
|
|
|
{ |
63
|
2 |
|
return strlen($this->buffer); |
64
|
|
|
} |
65
|
|
|
|
66
|
1 |
|
public function isReadable() : bool |
67
|
|
|
{ |
68
|
1 |
|
return true; |
69
|
|
|
} |
70
|
|
|
|
71
|
1 |
|
public function isWritable() : bool |
72
|
|
|
{ |
73
|
1 |
|
return true; |
74
|
|
|
} |
75
|
|
|
|
76
|
2 |
|
public function isSeekable() : bool |
77
|
|
|
{ |
78
|
2 |
|
return false; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
public function rewind() |
82
|
|
|
{ |
83
|
|
|
$this->seek(0); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
public function seek($offset, $whence = SEEK_SET) |
87
|
|
|
{ |
88
|
|
|
throw new \RuntimeException('Cannot seek a BufferStream'); |
89
|
|
|
} |
90
|
|
|
|
91
|
3 |
|
public function eof() : bool |
92
|
|
|
{ |
93
|
3 |
|
return strlen($this->buffer) === 0; |
94
|
|
|
} |
95
|
|
|
|
96
|
1 |
|
public function tell() |
97
|
|
|
{ |
98
|
1 |
|
throw new \RuntimeException('Cannot determine the position of a BufferStream'); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Reads data from the buffer. |
103
|
|
|
*/ |
104
|
8 |
|
public function read($length) |
105
|
|
|
{ |
106
|
8 |
|
$currentLength = strlen($this->buffer); |
107
|
|
|
|
108
|
8 |
|
if ($length >= $currentLength) { |
109
|
|
|
// No need to slice the buffer because we don't have enough data. |
110
|
7 |
|
$result = $this->buffer; |
111
|
7 |
|
$this->buffer = ''; |
112
|
|
|
} else { |
113
|
|
|
// Slice up the result to provide a subset of the buffer. |
114
|
2 |
|
$result = substr($this->buffer, 0, $length); |
115
|
2 |
|
$this->buffer = substr($this->buffer, $length); |
116
|
|
|
} |
117
|
|
|
|
118
|
8 |
|
return $result; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Writes data to the buffer. |
123
|
|
|
*/ |
124
|
8 |
|
public function write($string) |
125
|
|
|
{ |
126
|
8 |
|
$this->buffer .= $string; |
127
|
|
|
|
128
|
|
|
// TODO: What should happen here? |
129
|
8 |
|
if (strlen($this->buffer) >= $this->hwm) { |
130
|
1 |
|
return false; |
|
|
|
|
131
|
|
|
} |
132
|
|
|
|
133
|
8 |
|
return strlen($string); |
134
|
|
|
} |
135
|
|
|
|
136
|
1 |
|
public function getMetadata($key = null) |
137
|
|
|
{ |
138
|
1 |
|
if ($key == 'hwm') { |
139
|
1 |
|
return $this->hwm; |
140
|
|
|
} |
141
|
|
|
|
142
|
1 |
|
return $key ? null : []; |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.