1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Charcoal\App\Helper; |
4
|
|
|
|
5
|
|
|
use Psr\Http\Message\StreamInterface; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Callback-based stream implementation. |
9
|
|
|
* Wraps a callback, and invokes it in order to stream it. |
10
|
|
|
* Only one invocation is allowed; multiple invocations will return an empty |
11
|
|
|
* string for the second and subsequent calls. |
12
|
|
|
*/ |
13
|
|
|
class CallbackStream implements StreamInterface |
14
|
|
|
{ |
15
|
|
|
/** |
16
|
|
|
* @var callable |
17
|
|
|
*/ |
18
|
|
|
private $callback; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* |
22
|
|
|
* Whether or not the callback has been previously invoked. |
23
|
|
|
* @var boolean |
24
|
|
|
*/ |
25
|
|
|
private $called = false; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* CallbackStream constructor. |
29
|
|
|
* @param callable $callback The callback stream. |
30
|
|
|
*/ |
31
|
|
|
public function __construct(callable $callback) |
32
|
|
|
{ |
33
|
|
|
$this->callback = $callback; |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* |
38
|
|
|
* @return string |
39
|
|
|
*/ |
40
|
|
|
public function __toString() |
41
|
|
|
{ |
42
|
|
|
return $this->output(); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* |
47
|
|
|
* Execute the callback with output buffering. |
48
|
|
|
* |
49
|
|
|
* @return null|string Null on second and subsequent calls. |
50
|
|
|
*/ |
51
|
|
|
public function output() |
52
|
|
|
{ |
53
|
|
|
if ($this->called) { |
54
|
|
|
return; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
$this->called = true; |
58
|
|
|
|
59
|
|
|
call_user_func($this->callback); |
60
|
|
|
|
61
|
|
|
return; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* |
66
|
|
|
* @return void |
67
|
|
|
*/ |
68
|
|
|
public function close() |
69
|
|
|
{ |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* |
74
|
|
|
* @return null|callable |
75
|
|
|
*/ |
76
|
|
|
public function detach() |
77
|
|
|
{ |
78
|
|
|
$callback = $this->callback; |
79
|
|
|
$this->callback = null; |
80
|
|
|
|
81
|
|
|
return $callback; |
|
|
|
|
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* |
86
|
|
|
* @return integer|null Returns the size in bytes if known, or null if unknown. |
87
|
|
|
*/ |
88
|
|
|
public function getSize() |
89
|
|
|
{ |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* |
94
|
|
|
* @return integer|boolean Position of the file pointer or false on error. |
95
|
|
|
*/ |
96
|
|
|
public function tell() |
97
|
|
|
{ |
98
|
|
|
return 0; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* |
103
|
|
|
* @return boolean |
104
|
|
|
*/ |
105
|
|
|
public function eof() |
106
|
|
|
{ |
107
|
|
|
return $this->called; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* |
112
|
|
|
* @return boolean |
113
|
|
|
*/ |
114
|
|
|
public function isSeekable() |
115
|
|
|
{ |
116
|
|
|
return false; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* |
121
|
|
|
* @link http://www.php.net/manual/en/function.fseek.php 1 |
122
|
|
|
* @param integer $offset Stream offset |
123
|
|
|
* @param integer $whence Specifies how the cursor position will be calculated |
124
|
|
|
* based on the seek offset. Valid values are identical to the built-in |
125
|
|
|
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to |
126
|
|
|
* offset bytes SEEK_CUR: Set position to current location plus offset |
127
|
|
|
* SEEK_END: Set position to end-of-stream plus offset. |
128
|
|
|
* @return boolean Returns TRUE on success or FALSE on failure. |
129
|
|
|
*/ |
130
|
|
|
public function seek($offset, $whence = SEEK_SET) |
131
|
|
|
{ |
132
|
|
|
return false; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* |
137
|
|
|
* @see seek() |
138
|
|
|
* @link http://www.php.net/manual/en/function.fseek.php 1 |
139
|
|
|
* @return boolean Returns TRUE on success or FALSE on failure. |
140
|
|
|
*/ |
141
|
|
|
public function rewind() |
142
|
|
|
{ |
143
|
|
|
return false; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* |
148
|
|
|
* @return boolean |
149
|
|
|
*/ |
150
|
|
|
public function isWritable() |
151
|
|
|
{ |
152
|
|
|
return false; |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* |
157
|
|
|
* @param string $string The string that is to be written. |
158
|
|
|
* @return integer|boolean Returns the number of bytes written to the stream on |
159
|
|
|
* success or FALSE on failure. |
160
|
|
|
*/ |
161
|
|
|
public function write($string) |
162
|
|
|
{ |
163
|
|
|
return false; |
|
|
|
|
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* |
168
|
|
|
* @return boolean |
169
|
|
|
*/ |
170
|
|
|
public function isReadable() |
171
|
|
|
{ |
172
|
|
|
return true; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* |
177
|
|
|
* @param integer $length Read up to $length bytes from the object and return |
178
|
|
|
* them. Fewer than $length bytes may be returned if underlying stream |
179
|
|
|
* call returns fewer bytes. |
180
|
|
|
* @return string|false Returns the data read from the stream, false if |
181
|
|
|
* unable to read or if an error occurs. |
182
|
|
|
*/ |
183
|
|
|
public function read($length) |
184
|
|
|
{ |
185
|
|
|
return $this->output(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* |
190
|
|
|
* @return string |
191
|
|
|
*/ |
192
|
|
|
public function getContents() |
193
|
|
|
{ |
194
|
|
|
return $this->output(); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* |
199
|
|
|
* @link http://php.net/manual/en/function.stream-get-meta-data.php 2 |
200
|
|
|
* @param string $key Specific metadata to retrieve. |
201
|
|
|
* @return array|mixed|null Returns an associative array if no key is |
202
|
|
|
* provided. Returns a specific key value if a key is provided and the |
203
|
|
|
* value is found, or null if the key is not found. |
204
|
|
|
*/ |
205
|
|
|
public function getMetadata($key = null) |
206
|
|
|
{ |
207
|
|
|
if ($key === null) { |
208
|
|
|
return []; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
return null; |
212
|
|
|
} |
213
|
|
|
} |
214
|
|
|
|
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.