This problem is linked to the timeout issues discussed in
THRIFT-347, and in fact has been exacerbated by the "fixes" applied for those tickets.
THRIFT-347 was addressed by detecting timeouts with the following expression:
if (true === $md['timed_out'] && false === $md['blocked'])
The issue here is that $md['blocked'] denotes whether a stream is a blocking (true) or non-blocking (false) stream; since all TSocket streams are blocking (they never change the default, nor should they) this always evaluates to false: The result is, TException is never thrown even for legitimate timeouts.
This doesn't cause the problem itself, but it handily hides it away by making read() block excessively when there's not enough data to fill the fread() buffer instead of erroneously throwing a TException as it did before. It's also the reason the stream_socket family of functions are reportedly ignoring timeouts. They don't, TSocket is.
Chris correctly points out that this issue is resolved by replacing fread() calls with stream_socket_recv() calls, however, it's not a bug in PHP (the bug report linked is invalid). fread() is buffered by design - from the php.net docs:
Reading stops as soon as one of the following conditions is met: length bytes have been read, EOF (end of file) is reached or a packet becomes available or the socket timeout occurs (for network streams)
My understanding of TSocket is that it's meant as a raw socket abstraction and that any desired buffering should be done in a wrapping TTransport (e.g. TBufferedTransport or TFramedTransport), if that's the case then we should use stream_socket_recv() for all socket reading and stream_socket_sendto() for all socket writing.