Sup guys. I'm using ajax to load in a bunch of data with php. Sometimes exceptions go uncaught. This generates some error messages which javascript cannot parse (because it's expecting json). This crashes the page, that's not cool. So I'm building a custom exception handler. Basically it should just json_encode the Exception object. But json only works on objects of type stdClass. So here's the actual question:
How do you cast an exception object to stdClass, so that I can json_encode it? I've tried all the naive stuff I could think of, like
(stdClass) $ex and (Object) ((Array) $ex)
And yeah, I know that exceptions shouldn't be uncaught to begin with, but it's more like a development feature. It makes debugging less painful ;-)
Thanks!
PHP doesn't support object casting.
Using exceptions handler in this case is an horrible idea, especially when all you have to do is:
try {
// Current code here, including other try-catch blocks
} catch (Exception $e) {
// handle uncaught exception
}
When error occurs you should setup response correctly. Set Response Code to 500 (Internal Server Error) so JavaScript could easly handle that.
Exception object provides only getMessage() and getCode() methods that could be usefull, so response body could be generated like so:
$responseBody = json_encode(array(
'message' => $e->getMessage(),
'code' => $e->getCode()
));
One option would be to set a custom error handler and make shure when you echo them to the browser not to send 200 header code , that way you can deal with errors in you're js way elegant .
Other options would be to use ob_start , or a big try catch ...
Well, here's a simple function to do it for you (turn an object to an associative array, bypassing access restrictions):
function obj2array($object) {
$r = new ReflectionObject($object);
$params = array();
foreach ($r->getProperties() as $prop) {
$prop->setAccessible(true);
$params[$prop->name] = $prop->getValue($object);
}
return $params;
}
Then to encode it:
$json = json_encode(obj2array($e));
Related
I am working with a internal framework where every exception is catched by an error handler and returned in a proper JSON error response, suitable for a RESTFul API.
Then I have a suite of tests, which are API tests, that are mainly testing that the API returns the proper JSON responses with the expected error codes.
For every test, the global variables are modified (and then restored) to emulate a different HTTP request. I do it that way to avoid the overload of doing cURL tests (through Guzzle or similar), and cause under the CLI environment, the code does not know the server's url.
<?php
// ... example, part of a base ApiTestCase class:
// Override globals (should be backed up by PHPUnit)
$_SERVER['REQUEST_METHOD'] = $request->method;
$_SERVER['QUERY_STRING'] = http_build_query($request->parameters);
$_SERVER['PATH_INFO'] = $request->path;
$_SERVER['REQUEST_URI'] = $request->path . ($_SERVER['QUERY_STRING'] ? '?' : '') . $_SERVER['QUERY_STRING'];
$_SERVER['REQUEST_TIME'] = time();
$_SERVER['REQUEST_TIME_FLOAT'] = microtime(true);
$_SERVER['HTTP_COOKIE'] = '';
// Set headers, cookies and parameters
foreach ($request->headers as $k => $v) {
$_SERVER['HTTP_' . strtoupper(str_replace('-', '_', trim($k)))] = $v;
}
if ($_SERVER['HTTP_COOKIE']) {
$GLOBALS['_COOKIE'] = http_parse_cookie($_SERVER['HTTP_COOKIE']);
} else {
$GLOBALS['_COOKIE'] = [];
}
$GLOBALS['_REQUEST'] = $request->parameters;
$responseBody = $app->start();
$response->httpCode = http_response_code();
$response->body = $responseBody ? #json_decode($responseBody) : null;
$response->headers = headers_list();
(I know that changing globals this way is not nice, and the framework should not rely on globals directly, but I have still to deal with legacy code.)
Then here comes the problem: when I try to test JSON error responses: PHPUnit intercepts the thrown exception (before the handler I mentioned in the beginning), so the framework has no chance to convert it to JSON and return the proper response.
I tried to find something in the PHPUnit manual to disable the PHPUnit error handler with no luck.
What could I do in this case? Thanks
Just to be clear, it sounds like we're not actually talking about catching exceptions here; we're talking about using PHP's set_error_handler() to intercept a fatal error before it terminates the program. This will deal with both errors and uncaught exceptions.
One thing you will not be able to do is let those errors and exceptions fall through to your error handler function -- as you've already found out, phpUnit does its own error handling that you can't override (because it's kinda fundamental to how phpUnit works).
What you're going to have to do is tell phpUnit what kind of exception or error you're expecting; your test will then pass or fail according to whether the error occurs. You won't be running the error handler, but in truth, you shouldn't need to; you can test function that separately if you need to. For error conditions, you don't need to see that the error handler produces the right output every time, just that an error occurs that will trigger the handler.
For regular PHP exceptions, you can use phpUnit's #expectedException annotation above your test function, like so:
/**
* #expectedException YourExpectedExceptionClass
*/
function testThisWillThrowAnException() {
....
}
If the PHP code is expected to produce a PHP error (ie an error, not an exception), then you would use the same idea, but phpUnit provides a helper classname for the error: PHPUnit_Framework_Error. So your code would look like this:
/**
* #expectedException PHPUnit_Framework_Error
*/
function testThisWillProduceAPHPError() {
....
}
In either case, your test will pass if the expected error/exception occurs.
You can also test for specific exception messages and codes, in case the exception class itself isn't sufficient information for you to know whether the test has done what you want it to do. See the phpUnit manual page for annotations for more info.
The example above is also correct, mine only provide Exceptions as assertions and gives you knowladge of Exceptions Works.
/**
* #dataProvider fixturesProvider // its just example
*/
public function testDataIsWrong($fixtures)
{
try
{
//Some Code
$this->fail('Exception');
}
catch(Exception $ex)
{
$this->assertEquals($ex,'Exception');
}
}
This also provide in your code possibility ,that You can Test false or inncorect data and assert it is incorrect.
The only solution I implemented that solves my problem is to not delegate the exception handler the responsibility to build and send the API error responses, but catch exceptions in the top level of your application.
In the catch I have an exception-to-error-response converter that takes care of that (or re-throws the exception when convenient), so the errors that are not critical (like the ones producing HTTP 4xx responses) are not popping up in the PHPUnit tests anymore.
My PHPUnit tests are also now able to deal with PSR-7 HTTP Response objects, instead of capturing the output buffer.
I'm currently working on an open source personal project that provides a nice backend api for game developers. I'm in the early stages of development, but I plan to write tests as I go along, which is where I've hit a snag.
Through out the system when an error occurs such as incorrect api credentials or missing credentials, I throw a custom exception which stores a bit of extra data so that I can catch it and give a JSON encoded response.
The tests work fine for those thrown in my BaseController, but I also capture a few Laravel Exceptions so I can respond with my own, or at least, output JSON like below:
app/start/global.php
App::error(function(Exception $exception, $code) {
Log::error($exception);
});
App::missing(function(Exception $exception) {
return BaseController::error(
Config::get('response.method.code'),
Config::get('response.method.http'),
'Method not found'
);
});
App::error(function(Viper\Exception $exception) {
return BaseController::error(
$exception->getCode(),
$exception->getStatusCode(),
$exception->getMessage()
);
});
I'm using the try { } catch() { } approach as I need to check an extra value that isn't in the normal Exceptions.
public function testNoMethodGET() {
$config = Config::get('response.method');
try {
$this->call('GET', '/');
} catch(\Viper\Exception $e) {
$this->assertEquals($e->getCode(), $config['code']);
$this->assertEquals($e->getStatusCode(), $config['http']);
}
$this->fail('Exception not thrown');
}
This is all good and well, but I want to check a few things on the actual response, like for example, whether or not the json is valid, whether or not the response structure matches and whether or not the response values are correct.
If I set the return value of $this->call() to a variable, I'd be unable to access that variable within the catch block, so the question is this, how can I test the return value of $this->call() once the Exception has been caught?
According to Taylor Otwell:
"this can be solved by de-coupling your
test. You really want to test the handler and that the exception is
thrown totally separately anyways [sic] to isolate your tests. For
instance:
App::error(function(ErrorType $e)
{
App::make('ErrorTypeHandler')->handle($e);
});
Now you can write test cases for ErrorTypeHandler class separately
from the rest of your application. Then check that proper exceptions
are thrown by your app with #expectedException."
see How do you test your App::error implementations?
In your case, you already have isolated your error handler in BaseController::error(), so you can test the responses directly in separate unit tests, without the use of $this->call(). Instead, just call $response = BaseController::error() with the desired parameters and then inspect the response and apply relevant assertions.
I'm writing a PHP application which does a SOAP request. Sometimes their server isn't up or has some sort of problem and my application blows with an error like this:
Undefined property: stdClass::$getSomethingJSONResult
There could be a number of reasons why the SOAP request fails and I want to be able to check for those error conditions so the program can handle it.
I have two SOAP functions I use in PHP. One for logging in to the SOAP server and the other for making a query to get the soapFunction. They look something like this:
// --------------------- FUNCTIONS -----------------------
function login_soap($username,$password) {
global $soapClient;
$soapURL = "http://SOAPServerSomeplace.asmx?WSDL";
$ns = 'http://microsoft.com/webservices/'; //Namespace of the WS.
$soapParameters = array('userName' => $username, 'password' => $password);
$soapClient = new SoapClient($soapURL);
$header = new SoapHeader($ns,'UserCredentials',$soapParameters,false);
$soapClient->__setSoapHeaders($header);
}
// -------------------------------------------------------
function getSomething($option) {
global $soapClient;
$soapFunctionParameters = array('this' => $option) ;
$soapFunction = "getSomethingJSON" ;
$this_json = $soapClient->$soapFunction($soapFunctionParameters);
$stdClassObject = json_decode($this_json->getSomethingJSONResult);
$this_obj = $stdClassObject->this;
return $this_obj;
}
// -------------------------------------------------------
I was wondering if I could just do this:
if (login_soap($username,$password)) {
echo "This worked." . "\n";
} else {
echo "This failed." . "\n";
}
or with getSomething($option)?
I don't know how to simulate a SOAP request failure to test this, but I think the SOAP request failing would cause PHP to abort. I'd need to capture this so it doesn't abort PHP so I can direct it to do something else.
My first concern is to make sure the SOAP request worked. Then check the JSON file it returns to make sure it has all the data needed before the program proceeds. Checking the JSON file I have figured out, but not with the SOAP request. Thanks!
SOAP will throw a SoapFault Exception if there is an error. You just have to catch the Exception and you can handle the error.
http://www.php.net/manual/de/class.soapfault.php
The SoapClient can throw exceptions if there are errors.
If throwing of exceptions is disabled, those exception objects are only returned. You'd have to check whether the return value is an instance of SoapFault, but the better approach would be to throw them, and use try/catch.
This is even more true because the SoapClient cannot detect HTTP errors otherwise. You MUST throw errors as exceptions if you want to catch those errors as well (it does not work if XDebug is enabled, though, but this should only be the case in development).
Building upon a question I already asked regarding exceptions, I fear that I might be writing php functions wrong then, or abusing the use of exceptions. The reason I say this, is because if custom exceptions are to be caught using try/catch blocks then the following function:
public function get_specific_page($page) {
if (!is_array( $this->_page )){
throw new AisisCore_Template_TemplateException( "<div class='error'>Trying to get a property from a non array.</div>" );
}
return $this->_page[$page];
}
Would then be called such as:
try{
get_specific_page($page);
}
catch(Exception $e){
echo $e->getMessage();
}
The problem with this approach is that I have many functions that are written like this, either checking to see if a file exists, throwing an error. Checking to see if a value is set in an array, throwing an error and my issue is that the file which deals with these function calls may become over loaded with try catch.....
So my question is, how would I better write functions like this so that I don't have php files over loaded with try catch statements, yet still be able to have y own custom functions.
Is it as obvious as writing the try catch inside the function it's self?
The reason I ask, if because I am use to working with fameworks and in companies where we write our functions as you see above. How ver I have worked with code bases that have tons of these functions and I dont see half the files that are useing them doing a bunch of try catches...
Update:
I was looking through zend source to better understand exceptions and came across this:
public function setMessage($messageString, $messageKey = null)
{
if ($messageKey === null) {
$keys = array_keys($this->_messageTemplates);
foreach($keys as $key) {
$this->setMessage($messageString, $key);
}
return $this;
}
if (!isset($this->_messageTemplates[$messageKey])) {
require_once 'Zend/Validate/Exception.php';
throw new Zend_Validate_Exception("No message template exists for key '$messageKey'");
}
$this->_messageTemplates[$messageKey] = $messageString;
return $this;
}
You can see how they throw a new exception message near the bottom, this function is not called by doing:
try{}catch(){}
yet when it throws an exception, there is no issue with "uncaught exception with message"
In my opinion, your approach is correct in general. However, a few notes:
You should refrain from using HTML formatting in exception messages. Generally, you don't know how the exception that you throw will be handled. For example, an exception handler could just write the message to a log file (you don't want HTML formatting then), present it to the user in a special error view (in which case the view itself should contain the HTML formatting), or simply ignore it (no need for formatting then, anyway).
Catch only exceptions that you can handle. If you know that your function throws an AisisCore_Template_TemplateException, you should just catch that exception and let all other exceptions bubble up to an exception handler that can handle them. You can use set_exception_handler to define such an exception handler that catches all uncaught exceptions by default (this is probably the case in your example from Zend Framework). Plainly put: Only catch exceptions in places where you know how to handle them.
Only use exceptions as what the name implies: to handle (unexpected) exceptions in your control flow. Using exceptions to control the regular flow of your program is possible, but generally considered bad design (just as a side note, your code samples look alright).
For the sake of completeness, some alternatives to using exceptions:
Use return codes instead of exceptions. This is old-school C-style. The advantage is that you don't need to wrap statements with try/catch-statements. However, you have to check the return values of each procedure, which is easy to forget. When using exceptions on the other hand, you reduce the risk of unexpected errors, since uncaught exceptions produce a fatal error per default.
Use PHP errors. See the trigger_error function for this. Custom errors are however nearly impossible to catch in PHP (except by using set_error_handler, which only works at global level).
Few days ago I deal with errors like this...
exit( 'Error!' );
or exit( 'Error!' );
Doh, right? =] Now I'm trying to learn Exceptions. This is how far I got...
http://pastie.org/1555970
Is that correct use of them? It would be cool that I can have more info about 'em. Like file where exception is thrown and line of that file. I know that there are build-in methods (in Exception class), but I want to somehow extend it so I don't need to write...
throw new My_Exception( '...' );
catch( My_Exception $e ) {
echo $e->my_method();
}
...but use old syntax.
throw new Exception( '...' );
catch( Exception $e ) {
echo $e->getMessage();
}
...or maybe you have any greater thought of Exceptions? How to deal with them? Help me! =]
In general you only need to echo/log exceptions, that cannot be otherwise handled. This pretty much means, that if you put your entire application within try block, there's only one place where you need to put echoing/logging logic (i.e. the catch block associated with the outermost try block).
If on the other hand the exception can be handled without stopping the application (in your example this could be providing a default numeric value, instead of incorrect value), then there's usually no need to echo/log it.
If you do want to log such exceptions (for debugging for example), then your application should contain a logging framework, so that it's enough to do in your catch blocks something like
} catch (Exception $e) {
ExceptionLogger::log($e); //static method == ugly, but it's for simplicity in this example
// do whatever needs to be done
}
log() method above would check if the logging is enabled, and if it is savenecessary data to a file.
Exceptions should be typed based upon the error that you find. The Spl Exceptions are a good start, but you really should be creating your own exceptions as well. Some common ones that I use:
FileNotFoundException extends RuntimeException <- self explanatory
HTTPException extends RuntimeException <- Used for http classes when a non-200 result is encountered
DatabaseQueryException extends LogicException <- Used for database query errors
Now, by typing them specifically, it lets you handle the errors in your code. So let's say that you want to fetch a HTTP resource. If that fails with anything but a 404, you want to try a backup URL. You could do that with:
try {
return getHttp($url1):
} catch (HttpException $e) {
if ($e->getCode() != 404) {
try {
return getHttp($url2);
} catch (HttpException $e2) {
//It's ok to ignore this, since why know it's an HTTP error and not something worse
return false;
}
} else {
return false;
}
}
As far as your example code that you posted, I would change a few things:
Change the thrown exception to InvalidArgumentException since it has more semantic meaning (I almost never throw a raw exception).
You should try to avoid catch(Exception $e) at all costs. You have no idea what exception was thrown, so how can you possibly handle it?
Only catch exceptions that you are reasonably sure you know how to handle (and outputting an error/logging is not handling, it's removing the usefulness of the exception). You should never see something like catch($e) { logerror($e); } or catch($e) { print $e->getMessage(); } since netiher is actually handling the exception.
If you don't fix or workaround the cause of the exception in your catch block, you should re-throw it. Let the code above you in the stack try to handle it. This is especially true with libraries and classes that are reused all over the place.
Now, with user interfaces, it may be acceptable to catch the exception and show the user an error message. So your example where you print the exception's message might be ok, but you'd really need to think about the use-cases of it. Are you calling it from a model or controller? If so, it may be ok display an error message. Are you calling it from a library? If so, it's probably better to let the exception bubble up.
Also, don't use a global try{} catch() {} block. Instead, install an Exception Handler to handle it for you. It's cleaner, and more semantically correct (since any try{}catch{} implies that you know how to handle the exception that you caught, whereas the exception handler is precisely meant for exceptions that weren't handled because you didn't know how to handle them.
Exceptions are for exceptional circumstances. Do not use them for all error conditions. If a user submits a password that's too short, don't throw an exception, handle that in validation. But if your hash function is expecting sha256 to be available and it isn't, that's a time for an exception. Exceptions are useful for program errors (when a condition that is unexpected happens, such as invalid input to a function), state errors (when the application enters a state that is unknown or unstable, such as if the requested view does not exist) and runtime errors (when the application encounters an error that can only be detected at runtime, such as a file-not-found error).
There is an entire page of the PHP manual devoted to extending exceptions and that page also gives you a lot of information on the methods to identify file/line number, backtrace etc. where the exception was thrown. This is the type of information that is extremely useful for debugging.