Pretty new to laravel, so I'm not exactly sure how it handles errors and how best to catch them.
I'm using a 3rd party game server connection library that can query game servers in order to pull data such as players, current map etc..
This library is called Steam Condenser : https://github.com/koraktor/steam-condenser
I have imported this using composer in my project and all seems to be working fine, however I'm having trouble with catching exceptions that are thrown by the library.
One example is where the game server you are querying is offline.
Here is my code:
public function show($server_name)
{
try{
SteamSocket::setTimeout(3000);
$server = server::associatedServer($server_name);
$server_info = new SourceServer($server->server_ip);
$server_info->rconAuth($server->server_rcon);
$players = $server_info->getPlayers();
$total_players = count($players);
$more_info = $server_info->getServerInfo();
$maps = $server_info->rconExec('maps *');
preg_match_all("/(?<=fs\)).*?(?=\.bsp)/", $maps, $map_list);
}catch(SocketException $e){
dd("error");
}
return view('server', compact('server', 'server_info', 'total_players', 'players', 'more_info', 'map_list'));
}
If the server is offline, it will throw a SocketException, which I try to catch, however this never seems to happen. I then get the error page with the trace.
This causes a bit of a problem as I wish to simply tell the end user that the server is offline, however I cannot do this if I can't catch this error.
Is there something wrong with my try/catch? Does laravel handle catching errors in this way? Is this an issue with the 3rd party library?
A couple things:
Does the trace lead to the SocketException or to a different error? It's possible that a different error is being caught before the SocketException can be thrown.
Your catch statement is catching SocketException. Are you importing the full namespace at the top of your PHP file? use SteamCondenser\Exceptions\SocketException;
Also for debugging purposes, you could do an exception "catch all" and dump the type of exception:
try {
...
}catch(\Exception $e){
dd(get_class($e));
}
If you still get the stack trace after trying the above code, then an error is being thrown before the try/catch block starts.
Related
I'm developing my own PHP framework and my code althought is working like it should, it's getting bigger and bigger; that of course leads to multiple ways for my framework to break, so I have decided it is time to implement Exception handling like any other PHP framework does, that 'nice' error view that tells you what might went wrong.
I have done my research and kind of understand how the Extension PHP default class works, I know that I'm able to extend this class and customize the error messages.
I also know that to trigger an Exception you gotta throw it and catch it with a "try/catch" statement, somethin like this...
class MyCustomException extends \Exception()
{
// My stuff
}
public function dontBeZero($number)
{
if ($number == 0) {
throw new MyCustomException('You gave me zero!!');
}
}
try {
dontBeZero(0);
} catch (MyCustomException $e) {
echo '<pre>';
$e->getMessage();
echo '</pre>';
}
I understand that, but my real question is: How does this popular frameworks such as Laravel, Symfony, etc manage to throw you a pretty message showing you what the error was, where do they keep all the logic that verifies whether it should or should not throw an exception, and most importantly where did they catch them?.
Most frameworks show these errors via a custom error handler. A popular one used by laravel is whoops.
You just need to register it as a custom handler, and you'll see the pretty error pages:
$whoops = new \Whoops\Run;
$whoops->pushHandler(new \Whoops\Handler\PrettyPageHandler);
$whoops->register();
Keep in mind, you should disable these on production (so that your stack traces/code isn't exposed).
See the two functions set_error_handler and set_exception_handler. These functions allow you to register a callback function which is called when an error or exception occurs.
These callback functions are called by the Php runtime and provided with error details as arguments. The error details include error line number, stack trace, file name and more. The callback function can then format and display this information
This is a general question regarding exception handing for exceptions thrown in onther people's code.
I am using the following Codeigniter PHP library: https://github.com/sepehr/ci-mongodb-base-model
which relies upon this library for MongoDB: https://github.com/alexbilbie/codeigniter-mongodb-library/tree/v2
If I call a function in the first library, and it then calls one from the second. Sometimes the second library throws exceptions which I want to be able to deal with in my own code, but there is a try-catch statement around the exception throwing call, which means that it is dealt with before I get a chance to (I just prints the exception to the screen).
My question is:
Without modifying all of the functions in the first and second libraries (i.e. removing all of the try catches), how can I deal with the exception that is thrown?
EDIT
This is how the functions in the second library are arranged:
class SomeClass
{
function do_something()
{
...
try {
...
}
catch {
$this->_show_error('Update of data into MongoDB failed: ' . $exception->getMessage(), 500);
}
}
function _show_error($error_message = '', $response_code = 500)
{
//Inbuilt Codeigniter helper function which can be disabled from printing the error to the screen
show_error($error_message, $response_code);
}
}
Although I can disable the error from being printed (which is of course just for debugging), I still have no way of knowing that it occurred and handling it.
(should be a comment rather than an answer, but it's a bit long)
just prints the exception to the screen
Really? Are you sure?
Did you check it doesn't trigger an error instead of an exception and you're running this on a system which is not configured as a production server?
If so then I'd steer way clear of this as a library.
(I sincerely doubt anyone would write code that dumb and publish it without lots of warnings)
During the process of my PHP learning I have been trying to read up on the best practices for error reporting and handling, but statements vary person to person and I have struggled to come up with a clear concise way of handling errors in my applications. I use exceptions on things that could go wrong, but for the most part it is hard for me to understand whether an exception should kill the application and display an error page or just be caught and silently dealt with.
Something that seems to elude me is, is there such thing as too much reporting? Every single time you call a function something could go horribly wrong meaning that if you were to confirm every single function call you would have to fill pages with if statements and work out what effect one failure may have on the rest. Is there a concise document or idea for error reporting that could clear this up for me? Are there best practices? What are the best examples of good error handling?
Currently I do the following:
Add important event results to an array to be logged and emailed to me if a fatal error was to occur
Display abstract/generic errors for fatal errors.
Use exceptions for cases that are likely to fail
Turn on error reporting in a development environment and off for live environment
Validate all user input data
Sanitizing invalid user input
Display concise, informative error messages to users without providing a platform for exploitation.
Exceptions are the only thing that you haven't understood IMHO: exceptions are meant to be out of your control, are meant to be caught be dealt with from outside the scope they are thrown in. The try block has a specific limit: it should contain related actions. For example take a database try catch block:
$array = array();
try {
// connect throws exception on fail
// query throws exception on fail
// fetch results into $array
} catch (...) {
$array[0]['default'] = 'me';
$array[0]['default2'] = ...;
...
}
as you can see I put every database related function inside the try block. If the connection fails the query and the fetching is not performed because they would have no sense without a connection. If the querying fails the fetching is skipped because there would be no sense in fetching no results. And if anything goes wrong, I have an empty $array to deal with: so I can, for example, populate it with default data.
Using exceptions like:
$array = array();
try {
if (!file_exists('file.php')) throw new Exception('file does not exists');
include('file.php');
} catch (Exception $e) {
trigger_error($e->getMessage());
}
makes no sense. It just a longer version of:
if (!file_exists('file.php')) trigger_error('file does not exists');
include('file.php');
I'm using Kohana 2. I would like to catch a database exception to prevent an error page when no connection to the server can be established.
The error displayed is
system/libraries/drivers/Database/Mysql.php [61]:
mysql_connect() [function.mysql-connect]: Lost connection to MySQL server at
'reading initial communication packet', system error: 110
The database server is not reachable at all at this point.
I'm doing this from a model. I tried both
public function __construct()
{
// load database library into $this->db
try
{
parent::__construct();
}
catch (Exception $e)
{
die('Database error occured');
}
}
as well as
try
{
$hoststatus = $this->db->query('SELECT x FROM y WHERE z;');
}
catch (Exception $e)
{
die('Database error occured');
}
...but none of them seemed to work. It seems as if no exception gets passed on from the main model. Is there another way to catch the database error and use my own error handling?
Kohana 2 does not convert errors into exceptions. You will either need to attach your own error handler, or use error_reporting() to turn off the error (temporarily) then do some kind of handling yourself.
You can catch the exception, but you are probably trying to catch it in the wrong place. The problem with trying to catch that low-level of an exception is that it can be spawned from many different sources.
For example, if you use the database driver for your sessions that exception will be thrown from instantiation of the database driver in the session library (which is instantiated in a call to session which will probably happen before you instantiate any models).
Catching that exception can happen from the model, but it is more likely to happen from another source - in which case you would probably have to extend a few libraries, or be sure you are wrapping a base model parent::__construct call and the session library in a try-catch block.
(I would personally extend the Model library to do that instead of putting it in a base model)
Hope that helps.
I don't know Kohana, but a try .. catch block will not catch normal errors, only Exceptions. Are you sure Kohana throws Exceptions where you expect to receive Exceptions?
Edit based on you comments:
Well, first of all, in a production environment (meaning your live application) you should always disable the displaying of PHP errors to the screen. Displaying of these errors to the screen should only be done in a development environment to inform you, the developer. Visitors of your live application however have no business in knowing/reading PHP errors, as it might disclose sensitive information about your environment. You should however log the errors to a log file.
Furthermore, I just took a quick peek at Kohana, and indeed see that here and there Exceptions are thrown, but it doesn't seem to do this in a consistent manner.
If you want php errors to be treated as Exceptions have a look example #1 in this documentation.
This is written in PHP, but it's really language agnostic.
try
{
try
{
$issue = new DM_Issue($core->db->escape_string($_GET['issue']));
}
catch(DM_Exception $e)
{
throw new Error_Page($tpl, ERR_NOT_FOUND, $e->getMessage());
}
}
catch(Error_Page $e)
{
die($e);
}
Is nested try, catch blocks a good practice to follow? It seems a little bulky just for an error page - however my Issue Datamanager throws an Exception if an error occurs and I consider that to be a good way of error detecting.
The Error_Page exception is simply an error page compiler.
I might just be pedantic, but do you think this is a good way to report errors and if so can you suggest a better way to write this?
Thanks
You're using Exceptions for page logic, and I personally think that's not a good thing. Exceptions should be used to signal when bad or unexpected things happen, not to control the output of an error page. If you want to generate an error page based on Exceptions, consider using set_exception_handler. Any uncaught exceptions are run through whatever callback method you specify. Keep in mind that this doesn't stop the "fatalness" of an Exception. After an exception is passed through your callback, execution will stop like normal after any uncaught exception.
I think you'd be better off not nesting. If you expect multiple exception types, have multiple catches.
try{
Something();
}
catch( SpecificException se )
{blah();}
catch( AnotherException ae )
{blah();}
The ideal is for exceptions to be caught at the level which can handle them. Not before (waste of time), and not after (you lose context).
So, if $tpl and ERR_NOT_FOUND are information which is only "known" close to the new DM_Issue call, for example because there are other places where you create a DM_Issue and would want ERR_SOMETHING_ELSE, or because the value of $tpl varies, then you're catching the first exception at the right place.
How to get from that place to dying is another question. An alternative would be to die right there. But if you do that then there's no opportunity for intervening code to do anything (such as clearing something up in some way or modifying the error page) after the error but before exit. It's also good to have explicit control flow. So I think you're good.
I'm assuming that your example isn't a complete application - if it is then it's probably needlessly verbose, and you could just die in the DM_Exception catch clause. But for a real app I approve of the principle of not just dying in the middle of nowhere.
Depending on your needs this could be fine, but I am generally pretty hesitant to catch an exception, wrap the message in a new exception, and rethrow it because you loose the stack trace (and potentially other) information from the original exception in the wrapping exception. If you're sure that you don't need that information when examining the wrapping exception then it's probably alright.
I'm not sure about PHP but in e.g. C# you can have more then one catch-Block so there is no need for nested try/catch-combinations.
Generally I believe that errorhandling with try/catch/finally is always common-sense, also for showing "only" a error-page. It's a clean way to handle errors and avoid strange behavior on crashing.
I wouldn't throw an exception on issue not found - it's a valid state of an application, and you don't need a stack trace just to display a 404.
What you need to catch is unexpected failures, like sql errors - that's when exception handling comes in handy. I would change your code to look more like this:
try {
$issue = DM_Issue::fetch($core->db->escape_string($_GET['issue']));
}
catch (SQLException $e) {
log_error('SQL Error: DM_Issue::fetch()', $e->get_message());
}
catch (Exception $e) {
log_error('Exception: DM_Issue::fetch()', $e->get_message());
}
if(!$issue) {
display_error_page($tpl, ERR_NOT_FOUND);
}
else
{
// ... do stuff with $issue object.
}
Exceptions should be used only if there is a potentially site-breaking event - such as a database query not executing properly or something is misconfigured. A good example is that a cache or log directory is not writable by the Apache process.
The idea here is that exceptions are for you, the developer, to halt code that can break the entire site so you can fix them before deployment. They are also sanity checks to make sure that if the environment changes (i.e. somebody alters the permissions of the cache folder or change the database scheme) the site is halted before it can damage anything.
So, no; nested catch handlers are not a good idea. In my pages, my index.php file wraps its code in a try...cache block - and if something bad happens it checks to see if its in production or not; and either emails me and display a generic error page, or shows the error right on the screen.
Remember: PHP is not C#. C# is (with the exception (hehe, no pun intended :p) of ASP.net) for applications that contain state - whereas PHP is a stateless scripting language.