@@ -220,7 +220,7 @@ private function doConnect($options)
220220
221221 private function doSend ($ connection , $ content )
222222 {
223- return fwrite ($ connection , $ content );
223+ return $ this -> fwrite ($ connection , $ content );
224224 }
225225
226226 private function doGet ($ connection )
@@ -254,4 +254,54 @@ private static function handleInternalError($type, $message, $file, $line)
254254 {
255255 throw new \ErrorException ($ message , 0 , $ type , $ file , $ line );
256256 }
257+
258+ /**
259+ * Replace fwrite behavior as api is broken in PHP.
260+ *
261+ * @see https://secure.phabricator.com/rPHU69490c53c9c2ef2002bc2dd4cecfe9a4b080b497
262+ *
263+ * @param resource $stream The stream resource
264+ * @param string $bytes Bytes written in the stream
265+ *
266+ * @return bool|int false if pipe is broken, number of bytes written otherwise
267+ */
268+ private function fwrite ($ stream , $ bytes )
269+ {
270+ if (!strlen ($ bytes )) {
271+ return 0 ;
272+ }
273+ $ result = @fwrite ($ stream , $ bytes );
274+ if (0 !== $ result ) {
275+ // In cases where some bytes are witten (`$result > 0`) or
276+ // an error occurs (`$result === false`), the behavior of fwrite() is
277+ // correct. We can return the value as-is.
278+ return $ result ;
279+ }
280+ // If we make it here, we performed a 0-length write. Try to distinguish
281+ // between EAGAIN and EPIPE. To do this, we're going to `stream_select()`
282+ // the stream, write to it again if PHP claims that it's writable, and
283+ // consider the pipe broken if the write fails.
284+ $ read = [];
285+ $ write = [$ stream ];
286+ $ except = [];
287+ @stream_select ($ read , $ write , $ except , 0 );
288+ if (!$ write ) {
289+ // The stream isn't writable, so we conclude that it probably really is
290+ // blocked and the underlying error was EAGAIN. Return 0 to indicate that
291+ // no data could be written yet.
292+ return 0 ;
293+ }
294+ // If we make it here, PHP **just** claimed that this stream is writable, so
295+ // perform a write. If the write also fails, conclude that these failures are
296+ // EPIPE or some other permanent failure.
297+ $ result = @fwrite ($ stream , $ bytes );
298+ if (0 !== $ result ) {
299+ // The write worked or failed explicitly. This value is fine to return.
300+ return $ result ;
301+ }
302+ // We performed a 0-length write, were told that the stream was writable, and
303+ // then immediately performed another 0-length write. Conclude that the pipe
304+ // is broken and return `false`.
305+ return false ;
306+ }
257307}
0 commit comments