Jump to content

Catching exceptions with namespace


NotionCommotion

Recommended Posts

How do I catch the second exception?

 

<?php
require '../vendor/autoload.php';
$ErrorServer=new \DataLogger\Server\ErrorServer();
$ErrorServer->start();
<?php


namespace DataLogger\Server;


class ErrorServer
{
    public function start() {
        try {
            throw new \Exception ('This error is caught.');
        }
        catch(\Exception $e) {
            echo($e->getMessage().PHP_EOL);
        }
        $loop = \React\EventLoop\Factory::create();
        $socket = new \React\Socket\Server($loop);
        $socket->on('connection', function (\React\Socket\ConnectionInterface $stream) use($loop) {
            try {
                $loop->addPeriodicTimer(2, function() {
                    throw new \Exception('This error is uncaught');
                });
            }
            catch(\Exception $e) {
                echo($e->getMessage().PHP_EOL);
            }
        });
        $socket->listen(1337, '0.0.0.0');
        $loop->run();
    }
}
This error is caught. 
Fatal error: Uncaught exception 'Exception' with message 'This error is uncaught' in /var/www/datalogger/src/server/ErrorServer.php:19 Stack trace: #0 [internal function]: DataLogger\Server\ErrorServer->DataLogger\Server\{closure}(Object(React\EventLoop\Timer\Timer)) #1 /var/www/datalogger/vendor/react/event-loop/src/Timer/Timers.php(90): call_user_func(Object(Closure), Object(React\EventLoop\Timer\Timer)) #2 /var/www/datalogger/vendor/react/event-loop/src/StreamSelectLoop.php(177): React\EventLoop\Timer\Timers->tick() #3 /var/www/datalogger/src/server/ErrorServer.php(27): React\EventLoop\StreamSelectLoop->run() #4 /var/www/datalogger/public/error.php(4): DataLogger\Server\ErrorServer->start() #5 {main} thrown in /var/www/datalogger/src/server/ErrorServer.php on line 19

 

 

Link to comment
Share on other sites

This has nothing to do with namespaces, as you can tell from the fact that the first exception does get caught.

 

It has to do with understanding exceptions. A try statement only catches exceptions which are thrown during execution of the enclosed code. It does not and cannot trace every problem which is somehow indirectly related to this code block. So catching exceptions from the registered callback is the job of the caller (in your case the Timer class) or one of the caller's callers (e. g. the loop).

Link to comment
Share on other sites

Exceptions bubble up directly through the call stack, so you can look at the call stack to see where you could catch it. Besides in main, the only place where you could stick a try/catch would be

/var/www/datalogger/src/server/ErrorServer.php(27): React\EventLoop\StreamSelectLoop->run()
around that.
Link to comment
Share on other sites

Exceptions bubble up directly through the call stack, so you can look at the call stack to see where you could catch it. Besides in main, the only place where you could stick a try/catch would be

/var/www/datalogger/src/server/ErrorServer.php(27): React\EventLoop\StreamSelectLoop->run()
around that.

 

 

 

Same question.  I just realized my original code didn’t show the $stream->close() that I meant to show.  My desire is to close any client connection which is related to the exception.  I can put the try/catch around run(), but how can I have access to $stream?

Link to comment
Share on other sites

Is there a way to catch exceptions (and standard errors as well for that mater) globally, and determine within that handler whether a client connection is defined within the scope?

Well... you shouldn't. You can catch exceptions globally but there's no way to recover at that point - the call stack is totally unwound and you can't get back to where you were before. Really, the global exception handler should only be for preventing your script from barfing a white page of death upon an uncaught exception and instead doing something like logging the exception and showing a 500 page.

 

Same question.  I just realized my original code didn’t show the $stream->close() that I meant to show.  My desire is to close any client connection which is related to the exception.  I can put the try/catch around run(), but how can I have access to $stream?

You can't do it in the code you've shown: the onconnection stuff only happens once (useless to try to catch an exception there) and I believe the periodic timer runs separately from connection handling (exceptions thrown in there have nothing to do with connections).

 

You have to catch the exception between the place that does the work and React's code. That may mean introducing a layer:

// before
$react->on("event", function() {
	// work...
	// exceptions thrown here cannot be reliably caught and dealt with
});
// after
function do_the_work() {
	// work...
	// exceptions thrown here cannot be caught in here (without refactoring)...
}

$react->on("event", function() {
	try {
		do_the_work(); // (some sort of function or method call)
	} catch (\Exception $e) {
		// ...but could be caught here
	}
});
The point is that the event handler adds a layer between your working code (can't add a try/catch, presumably) and the React code (can't add a try/catch) where you can add a try/catch.
Link to comment
Share on other sites

Well... you shouldn't. You can catch exceptions globally but there's no way to recover at that point - the call stack is totally unwound and you can't get back to where you were before. Really, the global exception handler should only be for preventing your script from barfing a white page of death upon an uncaught exception and instead doing something like logging the exception and showing a 500 page.

 

You can't do it in the code you've shown: the onconnection stuff only happens once (useless to try to catch an exception there) and I believe the periodic timer runs separately from connection handling (exceptions thrown in there have nothing to do with connections).

 

The script is a sockets server and not a HTTP server.  If something real bad happened, the desired next step is not a 500 page, but to kill the process and restart it.  Probably need to use exec(), right?

 

And the onconnection stuff will happen more than once as multiple clients can connect.  If an exception occurred due to a specific client, I don't wish to stop the server, but just log the issue and close the connection to that client.

 

$react->on("event", function() {
	try {
		do_the_work(); // (some sort of function or method call)
	} catch (\Exception $e) {
		// ...but could be caught here
	}
});

 

Thanks requnix,  Your solution to place the try/catch blocks within each on event closure will work.  I might need a couple of them, but not too many.

Link to comment
Share on other sites

Yes, my desire is to try to recovery, and thank you for acknowledging so.  Broken packets has been identified as a risk, and has a mitigation plan in place.  But whether or not I have a good excuse, dead servers are not a good thing, there are always unforeseen circumstances, and we all need a global backup plan.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • 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.