Framework: SoapFault exception cannot be caught when using SoapClient in Laravel

Created on 10 Dec 2014  路  26Comments  路  Source: laravel/framework

try {
$client = new SoapClient("wsdl-url-here",array('trace' => 1,'exceptions'=> true));
} catch (SoapFault $fault){
echo $fault->faultstring;
}

In the example above, if I have invalid WSDL URL, I am supposed to get SoapFault exception since I set exceptions to true. However, Laravel catches this as FATAL exception and the application crashes. This code works as expected outside of Laravel.

Most helpful comment

Actually, the SoapFault is successfully caught. However, Laravel is handling the last error in its handleShutdown method. Therefore, clear the last error to avoid the fatal error exception:

try {
    return $this->client = new SoapClient($this->getWsdlUrl(), [
        'cache_wsdl' => WSDL_CACHE_NONE,
        'exceptions' => true
    ]);
} catch (SoapFault $exception) {
    Log::error($exception);

    error_clear_last(); // The solution.
}

Tested in Laravel 5.2.

EDIT: Still working in Laravel 5.7. Also, updated comment to use error_clear_last() introduced in PHP 7. For versions below PHP 7.0 replace error_clear_last() with:

set_error_handler('var_dump', 0); // Never called because of empty mask.
@trigger_error("");
restore_error_handler();

All 26 comments

Are you in a namespaced file that has already included SoapClient? You may need to add use SoapFault; at the top of the document or add a backslash to the class name in the catch statement (\SoapFault $fault).

Thanks for your response. SoapFault is an exception thrown by PHP SoapClient class (http://php.net/manual/en/class.soapfault.php). I have tried adding backslash but that didn't help. What I have found is that the SOAP exception is thrown and I can catch it, however, Laravel still crashes with FATAL SOAP exception. I have implemented the following workaround in my global.php to fix the issue temporarily.

App::fatal(function($exception)
{   //If SOAP Error is found, we don't want to FATALLY crash but catch it instead
    if(strpos($exception->getMessage(), 'SOAP-ERROR') !== FALSE)
    {
    return '';
    }
});

If you catch all exception instead of catch SoapFault is it working?

try {
    $client = new SoapClient("wsdl-url-here", ['trace' => 1, 'exceptions' => true]);
} catch (Exception $e) {
    echo 'Catch it!';
}

I have tried that as well, it still crashed with FATAL exception.

Can you paste the complete exception's message?

When I use invalid WSDL URL, this is the message I get without using any workaround:

Symfony Component Debug Exception FatalErrorException (E_ERROR)
SOAP-ERROR: Parsing WSDL: Couldn't load from 'https://www.example.com/v4_6_release/apis/1.5/ServiceTicketApi.asmx?wsdl' : failed to load external entity "https://www.example.com/v4_6_release/apis/1.5/ServiceTicketApi.asmx?wsdl"

Do you want to throw a backslash in front of @RomainLanz's solution too, the catch statement really should pickup on that exception.

@dwightwatson He said that he tried: "I have tried adding backslash but that didn't help."

Yes, tried both Exception and Exception and get the same error as before.

I was under the impression he had added a backslash to the SoapFault, was suggesting that it be tried again with the raw Exception class.

I am wondering if I am having the issue outlined here: https://bugs.php.net/bug.php?id=47584
I am not sure if Laravel uses xdebug

I believe the Homestead environment has XDebug but that isn't anything specific to Laravel. Looks like that bug could be the cause of your grief unfortunately.

I am actually using XAMPP and have disabled xdebug completely in my php.ini, and that's why I was thinking it could be a Laravel issue. FYI- I tried the try/catch outside of Laravel within the same XAMPP environment and it works as expected (no FATAL crash)

Laravel think that E_ERROR that genereted by SoapClient is a fatal error. So laravel will throw FatalException. It's happen before you catch that exception in L4.

Set your error_reporting to not include E_ERROR.

Same problem here "SymfonyComponentDebugExceptionFatalErrorException' with message 'SOAP-ERROR: Parsing WSDL: Couldn't load from http:...."

You have to disable your error_handler. I got this problem when i was coding my own framework. The error_handler by passing the try...catch.

Have you tried catching Exception instead of a SoapFault?

My workaround in AppExceptionsHandler for console application:

    protected function shouldntReport(Exception $e)
    {
        if ($this->isSoapError($e)) {
            return true;
        }
        return parent::shouldntReport($e);
    }

    public function renderForConsole($output, Exception $e)
    {
        if ($this->isSoapError($e)) {
            return;
        }
        return parent::renderForConsole($output, $e);
    }

    private function isSoapError(Exception $e)
    {
        return strpos($e->getMessage(), 'SOAP-ERROR: Parsing WSDL:') !== false;
    }

Actually, the SoapFault is successfully caught. However, Laravel is handling the last error in its handleShutdown method. Therefore, clear the last error to avoid the fatal error exception:

try {
    return $this->client = new SoapClient($this->getWsdlUrl(), [
        'cache_wsdl' => WSDL_CACHE_NONE,
        'exceptions' => true
    ]);
} catch (SoapFault $exception) {
    Log::error($exception);

    error_clear_last(); // The solution.
}

Tested in Laravel 5.2.

EDIT: Still working in Laravel 5.7. Also, updated comment to use error_clear_last() introduced in PHP 7. For versions below PHP 7.0 replace error_clear_last() with:

set_error_handler('var_dump', 0); // Never called because of empty mask.
@trigger_error("");
restore_error_handler();

Have you tried catching Throwable instead of a SoapFault?

hi
i have this problem and it is uncatchable i think.

set_error_handler('var_dump', 0); // Never called because of empty mask.
@trigger_error("");
restore_error_handler();

This is working solution, soap thow exception and error than can not be catch.
In catch SoapFault on php 7 can do this:

error_clear_last();

Less code.

The solution is to actually ask the Soap client to throw a SoapFault instead of reporting an E_ERROR.

When the Soap client reports an E_ERROR, there is nothing for you to catch.

To fix this initialise you SoapClient like this:

        $clientOptions = array(
            'exceptions' => true,
        );

        try {
            $client = new \SoapClient("foo.wsdl", $clientOptions);
        } catch (\SoapFault $e) {
            // Do what you need to do!;
        }

        try {
            $result = $client->__soapCall($method, $data);
        } catch (\SoapFault $e) {
            // Do what you need to do!;
        }

The solution is to actually ask the Soap client to throw a SoapFault instead of reporting an E_ERROR.

When the Soap client reports an E_ERROR, there is nothing for you to catch.

To fix this initialise you SoapClient like this:

        $clientOptions = array(
            'exceptions' => true,
        );

        try {
            $client = new \SoapClient("foo.wsdl", $clientOptions);
        } catch (\SoapFault $e) {
            // Do what you need to do!;
        }

        try {
            $result = $client->__soapCall($method, $data);
        } catch (\SoapFault $e) {
            // Do what you need to do!;
        }

This is the correct solution. Thank you.

This is the correct solution. Thank you.

Have you tried this? It doesn't actually work. In fact I've tried every single workaround on this issue, nothing actually works.

Bumping this issue, this is an ongoing problem even in 6.4. We need a reasonable way to override Laravel's exception handling.

Actually, the SoapFault is successfully caught. However, Laravel is handling the last error in its handleShutdown method. Therefore, clear the last error to avoid the fatal error exception:

try {
    return $this->client = new SoapClient($this->getWsdlUrl(), [
        'cache_wsdl' => WSDL_CACHE_NONE,
        'exceptions' => true
    ]);
} catch (SoapFault $exception) {
    Log::error($exception);

    error_clear_last(); // The solution.
}

Tested in Laravel 5.2.

EDIT: Still working in Laravel 5.7. Also, updated comment to use error_clear_last() introduced in PHP 7. For versions below PHP 7.0 replace error_clear_last() with:

set_error_handler('var_dump', 0); // Never called because of empty mask.
@trigger_error("");
restore_error_handler();

1st step: Make sure you include
use SoapFailt;
use SoapClient;
use Log;

2nd step:
Use the above example from the quoted guy

3rd step: I had to change his code a little. Added a backslach like this "...} catch (SoapFault $exception) {..."

Was this page helpful?
0 / 5 - 0 ratings