27

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";
}
?>
jasonlam604
  • 1,456
  • 2
  • 16
  • 25
David
  • 271
  • 1
  • 3
  • 3
  • 3
    Honestly, any fatal error in the SOAP extension should be reported as a bug since there is no case in which your code should cause a fatal error. 404 WSDL should be a SoapFault as you expect. – ColinM Jan 24 '14 at 21:32

7 Answers7

39

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.

MeatPopsicle
  • 832
  • 13
  • 28
  • `'exceptions' => true` is the default (on my system, anyway). – Adam Monsen Aug 02 '13 at 06:19
  • I tried set_error_handler, ob_start or register_shutdown_function and nothing could catch this error actually (windows php 7) except the php_errors.log :-( – François Breton Apr 05 '17 at 15:57
  • Thanks for the update! Can you give an example how to get the soapfault with a throwable? I need the faultcode and faultstring for further actions. – Koot Aug 28 '18 at 09:37
  • I haven't tried it but you probably need to use the original solution to get to the actual SoapFault returned. I recall the problem is that a native PHP function errors inside the SOAP logic that prevents the SoapFault from being thrown properly – MeatPopsicle Aug 28 '18 at 10:47
19

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. */
yokoloko
  • 2,820
  • 3
  • 20
  • 27
  • Hey, tnks for the reply, i get a response based on the username and password i give it, but i need to put in an error display if the server is down, which it is (as i have disconnected my network connection).. and i get that fatal error. – David Aug 05 '11 at 07:56
  • I edited my answer you have to try if the url is valid. and the do the soap thing – yokoloko Aug 05 '11 at 08:10
  • Tnk u, look like what im looking for, it just does not work xD and unfortunately i dnt reli have any experience with PHP cURL – David Aug 05 '11 at 08:31
  • 2
    Hey, got it to work, its not 404, it gives 0 if the service cannot be accessed and 200 if it can. Tnk u very much for all ur help :) – David Aug 05 '11 at 08:47
  • I don't like this answer, purely because then when you make your SOAP request it may very well be down again since it's an entirely separate network connection. – scrowler Oct 12 '15 at 23:14
  • I would check if the http code is different than 200. I would have also set the CURLOPT_CONNECTTIMEOUT and CURLOPT_TIMEOUT to 1, for it not to take too long if the web service is down. – Leeeeeeelo Oct 29 '16 at 10:25
12

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';
}
wranvaud
  • 1,073
  • 13
  • 18
Brad Kent
  • 4,982
  • 3
  • 22
  • 26
2

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).'

Jonathan
  • 1,355
  • 14
  • 22
2

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)

gsoft
  • 21
  • 2
1

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.

-8

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';
}