I am looking to get some advice on best practice for throwing exceptions and catching them. I am using a 3rd party library for connecting to Amazon mws API. This library already throws exceptions which I am catching within a function.
My question is, should this function pass the exception onto the main script by using a try-catch? e.g.
function myFunction() {
try {
$obj = new Object();
$obj->makeCall();
return $obj->getData();
} catch (Exception $ex) {
throw new Exception('There was a problem with the library '.$ex->getMessage());
}
}
then in the main script;
try {
//make the call
$response = myFunction();
} catch (Exception $e){
//log error
$logger->error("log error");
}
If you are catching the exception in the main script, you don't need to catch it in your function myFunction until you need to do some processing in your function.
The control will return to the exception being catched in the main script if any of it's children or children's children and so on throw an exception.
I am trying to use a try/catch block to add in some error handling for a custom WordPress plugin that gets Tweets via the Twitter API.
For testing purposes, I am throwing an exception in my class construct method.
class Twitter_Settings() {
public function __construct() {
throw new \Exception('test');
}
}
then in my plugin file, I am doing:
function twitter_init_settings() {
try {
return new Twitter_Settings();
} catch ( Exception $e ) {
echo $e->getMessage();
}
}
twitter_init_settings();
On the frontend, where I am spitting out $tweets = twitter_feed()->output_feed(); (with a foreach loop afterwards) I am getting an Uncaught Exception error. Oddly, it shows the custom message, 'test', so it must know about my exception, so why is it saying it is uncaught?
Uncaught Exception error happen while an exception is not catched (in try catch statements)
Remove the return statment because the catch might not be reached.
I have php code that execute python cgi and I want to pass python trace (returned from cgi) as extra data to php exception how can I do this and how can I get that value from catch(Exception e) { (It should check if that extra value exesit or not).
I have code like this:
$response = json_decode(curl_exec($ch));
if (isset($response->error)) {
// how to send $response->trace with exception.
throw new Exception($response->error);
}
return $response->result;
and I use json-rpc library that should return that data to the user:
} catch (Exception $e) {
//catch all exeption from user code
$msg = $e->getMessage();
echo response(null, $id, array("code"=>200, "message"=>$msg));
}
Do I need to write new type of exception or can I do this with normal Exception? I would like to send everything that was thrown in "data" =>
You need to extend Exception class:
<?php
class ResponseException extends Exception
{
private $_data = '';
public function __construct($message, $data)
{
$this->_data = $data;
parent::__construct($message);
}
public function getData()
{
return $this->_data;
}
}
When throwing:
<?php
...
throw new ResponseException($response->error, $someData);
...
And when catching:
catch(ResponseException $e) {
...
$data = $e->getData();
...
}
Dynamic Property (not recommended)
Please note that this will cause deprecation error in PHP 8.2 and will stop working in PHP 9 according to one of the PHP RFC https://wiki.php.net/rfc/deprecate_dynamic_properties
As the OP asking about doing this task without extending Exception class, you can totally skip ResponseException class declaration. I really not recommend do it this way, unless you've got really strong reason (see this topic for more details: https://softwareengineering.stackexchange.com/questions/186439/is-declaring-fields-on-classes-actually-harmful-in-php)
In throwing section:
...
$e = new Exception('Exception message');
$e->data = $customData; // we're creating object property on the fly
throw $e;
...
and when catching:
catch(Exception $e) {
$data = $e->data; // Access data property
}
September 2018 edit:
As some of readers found this answer useful, I have added a link to another Stack Overflow question which explains the downsides of using dynamically declared properties.
Currently, your code converts the response text directly into an object without any intermediate step. Instead, you could always just keep the serialized (via JSON) text it and append it to the end of the Exception message.
$responseText = curl_exec($ch);
$response = json_decode($responseText);
if (isset($response->error)) {
throw new Exception('Error when fetching resource. Response:'.$responseText);
}
return $response->result;
Then you could just recover everything after "Response:" in your error log and optionally de-serialize it or just read it.
As an aside, I would also not count on the server sending JSON, you should verify that the response text was actually parseable as JSON and return a separate error for that if it isn't.
i am trying to connect to a webservice. My webserviceHelper is:
class webserviceHelper {
public function __construct($params) {
$this->service_url = $params['service_url'];
try {
$this->soap = new SoapClient($this->service_url,
array('exceptions' => true));
}
catch (SoapFault $exc) {
echo 'SoapFault<br />';
die;
}
catch (Exception $exc) {
echo 'Exception<br />';
die;
}
}
...
}
When the service is down, i make a request to the page where the webserviceHelper object created. Before the response i make second request to the same page. At first one, i got "soapFault" as output but at the second, i got a fatal error.
Fatal error: SOAP-ERROR: Parsing WSDL: Couldn't load from 'WebService?wsdl' : failed to load external entity "WebService?wsdl" in webserviceHelper.php on line 40
How can i prevent this error?
use error_get_last() after $this->soap = new SoapClient(..... to get potential errors
I handled it by using a hook in codeigniter. Thanks to the blogger. How To Catch PHP Fatal Error In CodeIgniter
I'm getting desperate, all I want is simple error handling when the PHP SOAP Web Service is down to echo an error message login service down. Please help me!
At the moment it's still displaying the error (along with warnings...):
Fatal error: SOAP-ERROR: Parsing WSDL
Here is the script:
<?php
session_start();
$login="0000000000000nhfidsj"; //It is like this for testing, It will be changed to a GET
$username = substr($login,0,13); //as password is always 13 char long
//(the validation is done int he javascript)
$password = substr($login,13);
try
{
ini_set('default_socket_timeout', 5); //So time out is 5 seconds
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl"); //locally hosted
$array = $client->login(array('username'=>$username,
'password'=>$password));
$result = $array->return;
}catch(SoapFault $client){
$result = "0";
}
if($result == "true")//as this would be what the ws returns if login success
{
$_SESSION['user'] = $login;
echo "00";
}
else
{
echo "01 error: login failed";
}
?>
UPDATE July 2018
If you don't care about getting the SoapFault details and just want to catch any errors coming from the SoapClient you can catch "Throwable" in PHP 7+. The original problem was that SoapClient can "Fatal Error" before it throws a SoapFault so by catching both errors and exceptions with Throwable you will have very simple error handling e.g.
try{
soap connection...
}catch(Throwable $e){
echo 'sorry... our service is down';
}
If you need to catch the SoapFault specifically, try the original answer which should allow you to suppress the fatal error that prevents the SoapFault being thrown
Original answer relevant for older PHP versions
SOAP can fatal error calling the native php functions internally which prevents the SoapFaults being thrown so we need to log and suppress those native errors.
First you need to turn on exceptions handling:
try {
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => true,
));
} catch ( SoapFault $e ) { // Do NOT try and catch "Exception" here
echo 'sorry... our service is down';
}
AND THEN you also need to silently suppress any "PHP errors" that originate from SOAP using a custom error handler:
set_error_handler('handlePhpErrors');
function handlePhpErrors($errno, $errmsg, $filename, $linenum, $vars) {
if (stristr($errmsg, "SoapClient::SoapClient")) {
error_log($errmsg); // silently log error
return; // skip error handling
}
}
You will then find it now instead trips a SoapFault exception with the correct message "Soap error: SOAP-ERROR: Parsing WSDL: Couldn't load from '...'" and so you end up back in your catch statement able to handle the error more effectively.
Fatal error: SOAP-ERROR: Parsing WSDL Means the WSDL is wrong and maybe missing? so it's not related to soap. And you cannot handle FATAL ERROR with a try catch. See this link : http://ru2.php.net/set_error_handler#35622
What do you get when you try to access http://192.168.0.142:8080/services/Logon?wsdl in your browser?
You can check if the WSDL is present like this
$handle = curl_init($url);
curl_setopt($handle, CURLOPT_RETURNTRANSFER, TRUE);
$response = curl_exec($handle);
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
/* You don't have a WSDL Service is down. exit the function */
}
curl_close($handle);
/* Do your stuff with SOAP here. */
Unfortunately SOAP throws a fatal error when the service is down / unreachable rather than returning a SoapFault object.
That being said, you can set it to throw an exception. You probably omitted the part where you're setting the exceptions soap_client option to false
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => false, // change to true so it will throw an exception
));
Catch the exception when service is down:
try {
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => true,
));
}
catch ( Exception $e )
{
echo 'sorry... our service is down';
}
Perhaps a better alternative:
set_error_handler('my_error_handler');
set_exception_handler('my_exception_handler');
function my_exception_handler($e) {
exit('Error, something went terribly wrong: '.$e);
}
function my_error_handler($no,$str,$file,$line) {
$e = new ErrorException($str,$no,0,$file,$line);
my_exception_handler($e);
}
Where you can adjust error messages in the mentioned functions.
I use it to return a message in the same situation you do, as it can occur at any time.
Say you send a soap message after the initial login, and that response never arrives or arrives only partially, this way you can return a message without any script paths, names and linenumbers.
In such cases I do not return $e at all, instead I just output something like: 'Something went wrong, please try it again (later).'
I ended up handling it this way:
libxml_use_internal_errors(true);
$sxe = simplexml_load_string(file_get_contents($url));
if (!$sxe) {
return [
'error' => true,
'info' => 'WSDL does not return valid xml',
];
}
libxml_use_internal_errors(false);
Do your soap call after this check.
Everything turned out to be much more trivial - when using namespaces, be sure to specify the root ns!
Those catch (SoapFailt $fault) - is wrong, right way catch (\SoapFault $fault)
SoapFault doesn't extends Exception, catch the especific type works:
try {
$client = new SoapClient("http://192.168.0.142:8080/services/Logon?wsdl",array(
'exceptions' => true,
));
}
catch ( SoapFault $e )
{
echo 'sorry... our service is down';
}