amphp / beanstalk

Asynchronous Beanstalk Client for PHP.
https://amphp.org/beanstalk/
MIT License
66 stars 24 forks source link

Call to a member function resolve on null #25

Closed mmenozzi closed 5 years ago

mmenozzi commented 5 years ago

Hi, I use the AMP's beanstalk client with https://github.com/webgriffe/esb. Sometimes (but I never found the cause) the ESB application crashes with the following error:

PHP Fatal error:  Uncaught Error: Call to a member function resolve() on null in vendor/amphp/beanstalk/src/BeanstalkClient.php:38

That resolve is the one inside the response event callback of the client:

        $this->connection->addEventHandler("response", function ($response) {
            /** @var Deferred $deferred */
            $deferred = array_shift($this->deferreds);
            if ($response instanceof Throwable) {
                $deferred->fail($response);
            } else {
                $deferred->resolve($response);
            }
        });

And this is strange because only Deferred instances are added to the BeanstalkClient::$deferreds array.

https://github.com/amphp/beanstalk/blob/9973d81a1a292ec09d8ab5ffd08e61dc435c664a/src/BeanstalkClient.php#L55 https://github.com/amphp/beanstalk/blob/9973d81a1a292ec09d8ab5ffd08e61dc435c664a/src/BeanstalkClient.php#L68

So it seems that, sometimes, the response event is fired when the BeanstalkClient::$deferreds array is empty. Indeed, array_shift returns null if the array is empty (http://php.net/manual/en/function.array-shift.php).

I don't know how this could happen but happens...

Maybe we can prevent such error with a simple check:

        $this->connection->addEventHandler("response", function ($response) {
            if (empty($this->deferreds)) {
                return;
            }
            /** @var Deferred $deferred */
            $deferred = array_shift($this->deferreds);
            if ($response instanceof Throwable) {
                $deferred->fail($response);
            } else {
                $deferred->resolve($response);
            }
        });

But I don't know if the right thing to do. @kelunik what do you think?

kelunik commented 5 years ago

No, the check just hides the error. We have to find the cause.

kelunik commented 5 years ago

My initial guess would be that it might happen on connection closes?

mmenozzi commented 5 years ago

I don't think so. I did some other investigation and what can I say is that it happens during the execution of this method (but only sometimes, I think it depends on what is in the tube):

https://github.com/webgriffe/esb/blob/e2570d31533e47a95f5aa0e25693c7c2f06b80cc/src/Console/Controller/TubeController.php#L48

Do you see any unexpected usage in this method?

kelunik commented 5 years ago

Closing after #26 is fixed, that's probably the cause.

Example

10\r\n
..........\r\n

If the following is in the buffer

10\r\n
..........

Then the if with the return is skipped: 4 + 2 + 14 < 10 + 4