StreamManager  v0.1.1
PHP stream manager
StreamWrapper.php
1 <?php
2 
4 
7 
8 class StreamWrapper implements \Countable
9 {
10  public $context;
11  protected $rawStream;
12  protected $filteredStream;
13  protected $outputBuffer = '';
14 
15  // @codingStandardsIgnoreStart
16  public function stream_open($path, $mode, $options, &$opened_path)
17  {
18  // @codingStandardsIgnoreEnd
19  if (null === $this->context) {
20  throw new \InvalidArgumentException('A valid stream context is required');
21  }
22 
23  $ctxOptions = stream_context_get_options($this->context);
24  $wrapper = StreamManager::WRAPPER_NAME;
25  if (!isset($ctxOptions[$wrapper]['stream']) || !self::isStream($ctxOptions[$wrapper]['stream'])) {
26  throw new \InvalidArgumentException('No stream specified in context');
27  }
28 
29  $this->filteredStream = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
30  if (false === $this->filteredStream) {
31  throw new \RuntimeException('Could not create a wrapper');
32  }
33 
34  if (false === stream_set_blocking($this->filteredStream[0], false) ||
35  false === stream_set_blocking($this->filteredStream[1], false)) {
36  throw new \RuntimeException('Could not set streams into non-blocking mode');
37  }
38 
39  if (array_key_exists('readCallback', $ctxOptions[$wrapper]) &&
40  null !== $ctxOptions[$wrapper]['readCallback'] &&
41  !is_callable($ctxOptions[$wrapper]['readCallback'])) {
42  throw new \InvalidArgumentException('Invalid read callback');
43  }
44 
45  if (array_key_exists('closeCallback', $ctxOptions[$wrapper]) &&
46  null !== $ctxOptions[$wrapper]['closeCallback'] &&
47  !is_callable($ctxOptions[$wrapper]['closeCallback'])) {
48  throw new \InvalidArgumentException('Invalid close callback');
49  }
50 
51  $this->rawStream = $ctxOptions[$wrapper]['stream'];
52  $this->filters = array();
53  $opened_path = $path;
54  return true;
55  }
56 
57  public function count()
58  {
59  return strlen($this->outputBuffer);
60  }
61 
62  protected static function isStream($value)
63  {
64  return is_resource($value) && 'stream' === get_resource_type($value);
65  }
66 
67  // @codingStandardsIgnoreStart
68  public function stream_close()
69  {
70  // @codingStandardsIgnoreEnd
71  fclose($this->filteredStream[0]);
72  fclose($this->filteredStream[1]);
73  fclose($this->rawStream);
74  }
75 
76  // @codingStandardsIgnoreStart
77  public function stream_eof()
78  {
79  // @codingStandardsIgnoreEnd
80  return feof($this->rawStream);
81  }
82 
83  // @codingStandardsIgnoreStart
84  public function stream_flush()
85  {
86  // @codingStandardsIgnoreEnd
87  return $this->sendOutput() && fflush($this->rawStream);
88  }
89 
90  // @codingStandardsIgnoreStart
91  public function stream_lock($operation)
92  {
93  // @codingStandardsIgnoreEnd
94  return flock($this->rawStream, $operation);
95  }
96 
97  // @codingStandardsIgnoreStart
98  public function stream_seek($offset, $whence = SEEK_SET)
99  {
100  // @codingStandardsIgnoreEnd
101  return !fseek($this->rawStream, $offset, $whence);
102  }
103 
104  // @codingStandardsIgnoreStart
105  public function stream_set_option($option, $arg1, $arg2)
106  {
107  // @codingStandardsIgnoreEnd
108  switch ($option) {
109  case STREAM_OPTION_BLOCKING:
110  return stream_set_blocking($this->rawStream, $arg1);
111 
112  case STREAM_OPTION_READ_TIMEOUT:
113  // HACK: stream_set_timeout() is used to flush the output buffer.
114  if (-1 === $arg1 && -1 === $arg2) {
115  return $this->sendOutput();
116  }
117  return stream_set_timeout($this->rawStream, $arg1, $arg2);
118 
119  case STREAM_OPTION_WRITE_BUFFER:
120  return stream_set_write_buffer($this->rawStream, $arg2);
121 
122  default:
123  throw new \RuntimeException('Invalid option');
124  }
125  }
126 
127  // @codingStandardsIgnoreStart
128  public function stream_stat()
129  {
130  // @codingStandardsIgnoreEnd
131  // HACK: we use fstat() to pass information about the output buffer
132  // back to the stream manager.
133  return array('size' => strlen($this->outputBuffer));
134  }
135 
136  // @codingStandardsIgnoreStart
137  public function stream_tell()
138  {
139  // @codingStandardsIgnoreEnd
140  return ftell($this->rawStream);
141  }
142 
143  // @codingStandardsIgnoreStart
144  public function stream_truncate($new_size)
145  {
146  // @codingStandardsIgnoreEnd
147  return ftruncate($this->rawStream, $rawSize);
148  }
149 
150  // @codingStandardsIgnoreStart
151  public function stream_read($count)
152  {
153  // @codingStandardsIgnoreEnd
154  $data = fread($this->rawStream, $count);
155  if (false === $data) {
156  return false;
157  }
158 
159  if ('' === $data && feof($this->rawStream)) {
160  throw new EOFException();
161  }
162 
163  while (strlen($data) > 0) {
164  $written = fwrite($this->filteredStream[1], $data);
165  if (false === $written) {
166  return false;
167  }
168  $data = (string) substr($data, $written);
169  }
170 
171  $res = fread($this->filteredStream[0], 2 * $count);
172  return $res;
173  }
174 
175  // @codingStandardsIgnoreStart
176  public function stream_write($data)
177  {
178  // @codingStandardsIgnoreEnd
179  $res = 0;
180  while (strlen($data) > 0) {
181  $written = fwrite($this->filteredStream[0], $data);
182  if (false === $written) {
183  throw new \RuntimeException('Error during write');
184  }
185  $data = (string) substr($data, $written);
186  $res += $written;
187 
188  while (false !== ($read = fread($this->filteredStream[1], 8192))) {
189  if ('' === $read) {
190  break;
191  }
192  $this->outputBuffer .= $read;
193  }
194 
195  if (!$written) {
196  break;
197  }
198  }
199 
200  if (false === $this->sendOutput()) {
201  throw new \RuntimeException('Error while sending output');
202  }
203  return $res;
204  }
205 
206  public function sendOutput()
207  {
208  while (strlen($this->outputBuffer) > 0) {
209  $written = fwrite($this->rawStream, $this->outputBuffer);
210  if (false === $written) {
211  return false;
212  }
213 
214  if (!$written) {
215  break;
216  }
217 
218  $this->outputBuffer = (string) substr($this->outputBuffer, $written);
219  }
220  return true;
221  }
222 }
Classes implementing Countable can be used with the count() function.
Definition: Countable.php:11