NotionCommotion Posted March 9, 2017 Share Posted March 9, 2017 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(); } } Quote Link to comment Share on other sites More sharing options...
requinix Posted March 9, 2017 Share Posted March 9, 2017 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. Quote Link to comment Share on other sites More sharing options...
NotionCommotion Posted March 9, 2017 Author Share Posted March 9, 2017 Thanks requinix, An error emit was my original plan, but then I thought exceptions are much more standardized and the better approach. Is there any type of standardized error event pattern used or do I just make something up? Quote Link to comment Share on other sites More sharing options...
requinix Posted March 9, 2017 Share Posted March 9, 2017 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) { // ... } }); 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.