Jump to content

Why can't I catch this exception?


NotionCommotion

Recommended Posts

Server:start() wraps LengthPrefixStream in a try/catch block.

 

LengthPrefixStream throws an exception.

 

Why can't I catch it?

<?php
namespace Base\Server;


class Server {
    public function start() {
        $loop = \React\EventLoop\Factory::create(); // Or \React\EventLoop\StreamSelectLoop()?
        $socket = new \React\Socket\Server($loop);
        $socket->on('connection', function (\React\Socket\ConnectionInterface $stream) {
            try {
                $client = new LengthPrefixStream($stream);
                $client->on('data', function($data) use ($client){
                    //....
                });
            }
            catch(LengthPrefixStreamException $e) {
                $this->app->logger->logExceptionError($e);
            }
            catch(Exception $e) {
                $this->app->logger->logExceptionError($e);
            }
        });
        $socket->listen($this->host['port'],$this->host['url']);
        $loop->run();
    }
}
<?php
namespace Base\Server;


class LengthPrefixStreamException extends \Exception {}
<?php
namespace Base\Server;


use Evenement\EventEmitterInterface;
use Evenement\EventEmitterTrait;
use React\Stream\DuplexStreamInterface;


class LengthPrefixStream implements EventEmitterInterface {


    use EventEmitterTrait;


    private $socket=false,
    $buffer='',
    $parseJson; //0=>no, 1=>yes and return object, 2=>yes and return array


    public function __construct(DuplexStreamInterface $socket, $parseJson=1){
        $this->parseJson = $parseJson;
        $this->socket = $socket;
        $this->socket->on('data', function($data){
            $this->buffer .= $data;
            $this->parseBuffer();
        });
    }


    public function send($msg){
        //Still need to implement drain
        if($this->isConnected()) {
            if($this->parseJson) {
                $msg=json_encode($msg);
            }
            $this->socket->write(pack("V", strlen($msg)).$msg);
            return true;
        }
        else {
            return false;
        }
    }


    private function parseBuffer(){
        do {
            $checkAgain = false;
            $bufferLength = strlen($this->buffer);
            $length = unpack('Vlen', substr($this->buffer, 0, 4))['len'];
            if($bufferLength >= $length + 4){
                $msg=substr($this->buffer, 4, $length).'bogus JSON';
                if($this->parseJson) {
                    $msg=json_decode($msg,$this->parseJson-1);
                    if (json_last_error() != JSON_ERROR_NONE){
                        throw new LengthPrefixStreamException('Invalid JSON provided: '.substr($this->buffer, 4, $length));
                    }
                }
                $this->emit('data', [$msg]);
                $this->buffer = substr($this->buffer, $length+4);
                $checkAgain = strlen($this->buffer)>=4;
            } 
        } while ($checkAgain);
    }


    public function isConnected()
    {
        return $this->socket?true:false;
    }


    public function close()
    {
        $this->socket->close();
    }
}

Link to comment
Share on other sites

The exception is thrown in parseBuffer, which is called during the socket's ondata event. Though that's defined in LengthPrefixStream's constructor, that's not when it actually executes. Instead that happens sometime during the run().

 

Rather than wrap the run() in a try/catch, which would necessarily terminate the run-ing in case of an error, have LengthPrefixStream emit an error event (instead of throwing the exception) and set up a callback for that.

Link to comment
Share on other sites

Exceptions don't work well for a callback-oriented architecture (eg, using events) so making one for errors too is typical. However you can still use Exception objects as the callback arguments, then you can do the same sort of basic filtering that a catch allows:

$object->on("error", function($e) {
	if ($e instanceof \FirstDesiredException) {
		// ...
	}
});
$object->on("error", function($e) {
	if ($e instanceof \SecondDesiredException) {
		// ...
	}
});
  • Like 1
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.