I have reimplemented safe_read_message to fix various bugs. The original function does a busy-read on a non-blocking socket. Even though the implementation tries to usleep() in between it is still a busy loop. It manages to eat 80% CPU on my AMD quad-core just waiting for reserve() to return a job.
This new implementation preserves the features of the original and fixes a couple of other bugs. It is implemented with a blocking fread() on the socket so that it does not use too much CPU resources. This makes the old timeout parameter unnecessary. Instead it has a new "recoverable" parameter that helps fixing several possible bugs.
The original implementation would always reconnect and reissue the last command when an error was encountered. This is not always desirable. When a response from beanstalkd consists of a response and a body of data then BeanQueue uses two calls to safe_read_message(). If the first one succeeds but the second one fails, then when reconnecting the second call would return not the data but the response header again, leading to errors. The new "recoverable" parameter fixes this by not allowing the command to be reissued in such circumstances.
This new implementation also fixes bug 2729099 and a couple of other errors (e.g. in the old implementation it was possible that $this->rbuflen did not contain the correct length of $this->rbuf).
New safe_read_message implementation
I am attaching a new version of my patch. The old one could deadlock under certain circumstances under new PHP versions because the behavior of fread() has changed. With the new PHP behavior fread() will wait until it has read the number of bytes you wanted to read.
This new patch solves this by using fgets() instead of fread().
Updated safe_read_message patch