PHP7 and inherited class

Issue #37 invalid
Former user created an issue

Hello, found a bug with PHP 7.0, and EventBufferEvent object created in inherited class. Here is the code with Redis connection example:

class Generic
{
    public $base = NULL;
    public function start()
    {
        $this->base = new \EventBase();
        $this->onStart();

        // move onStart code here to fix //

    $this->base->dispatch();
    }
}

class Worker extends Generic
{
    protected function onStart()
    {
        $redis = new \EventBufferEvent($this->base);

        $redis->setCallbacks(
                function ($bev, $events, $unused=null)
                {
                        echo "onRead\n";
                        while (($line = $bev->input->readLine(\EventBuffer::EOL_ANY)) !== null) {
                                echo $line."\r\n";
                        }
                },
                null,
                function ($bev, $events, $unused=null)
                {
                        echo "onEvent\n";

                        if ($events & (\EventBufferEvent::EOF | \EventBufferEvent::ERROR)) {
                                $bev->free();
                                $bev = NULL;
                        } elseif ($events & \EventBufferEvent::CONNECTED) {
                                echo "redis: connected\n";

                                $command = 'PSUBSCRIBE';
                                $args = ['*'];
                                $data = '*' . (count($args) + 1) . "\r\n$" . strlen($command) . "\r\n" . $command . "\r\n";
                                foreach ($args as $arg) {
                                        $data .= '$' . strlen($arg) . "\r\n" . $arg . "\r\n";
                                }

                                $bev->write($data);
                        }
                }
        );

        if (!$redis->connect('127.0.0.1:6379')) {
                trigger_error("conn: Failed to connect to socket", E_USER_ERROR);
        } else {
                echo("socket to redis connected\n");
        }

        if (!$redis->enable(\Event::READ|\Event::WRITE|\Event::TIMEOUT|\Event::PERSIST)) {
                trigger_error("conn: Failed to enable socket", E_USER_ERROR);
        } else {
                echo("socket to redis event enabled\n");
        }
        $redis->setWatermark(\Event::READ, 16, 0xFFFF);
        $redis->setTimeouts(0, 0);
    }
}

$worker = new Worker();
$worker->start();

note when the code moved from onStart() to start(), the problem is fixed.

Comments (4)

  1. Fred Brown

    It could be that you need to make $redis into a class property. As things stand it goes out of scope as soon as onStart returns.

  2. Dmitry Cidious

    Indeed, making it into property fixes the problem. Note that in PHP5.6 it worked OK. Thank you!

  3. Fred Brown

    Ah ok, that's interesting. I'm only familiar with event on php >7.0. Glad you sorted it.

    I think it makes more sense that you should have to maintain a reference to your EventBufferEvent object. Seems a bit ambiguous how it would work otherwise, as the object would become hidden away in a scope of its own with no way to further interact with it - in effect it would be leaked, and we don't want that.

  4. Ruslan Osmanov repo owner

    @cidious, @fredfredfred is right. You should keep a reference to the EventBufferEvent ($redis) object. Otherwise, it will be destroyed when it goes out of scope as any other variable in PHP. In particular, EventBufferEvent's destructor is called between $this->onStart() and $this->base->dispatch().

  5. Log in to comment