php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #76070 SoapServer::handle() catches some exceptions, but not all
Submitted: 2018-03-08 18:37 UTC Modified: -
Votes:4
Avg. Score:4.8 ± 0.4
Reproduced:4 of 4 (100.0%)
Same Version:2 (50.0%)
Same OS:3 (75.0%)
From: magnar at myrtveit dot com Assigned:
Status: Open Package: SOAP related
PHP Version: 7.1.15 OS: Windows
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: magnar at myrtveit dot com
New email:
PHP Version: OS:

 

 [2018-03-08 18:37 UTC] magnar at myrtveit dot com
Description:
------------
Sometimes exceptions are caught by SoapServer::handle(), and sometimes not. Particularly, exceptions of type Error are caught, while exceptions of type Exception are not.

Consider the test script. There are two lines throwing exceptions that are commented out.

By sending a request to the soap server, the file `response.txt` is created with the expected soap response.

By uncommenting the first exception `(1)` and sending a request to the soap server, the file `exception.txt` is created with the following contents:

Uncaught exception: bar

By uncommenting the second exception `(2)` and sending a request to the soap server, the file `exception.txt` is not created! But the file `response.txt` is created with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode><faultstring>foo</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

It is surprising that the second exception never reaches the exception handler. Instead, the exception seems to be caught inside SoapServer::handle(), and the exception message is sent as a soap fault to the client.

Test script:
---------------
Server script, uncomment the exceptions, one after the other, to see the strange behavior:
<?php

set_exception_handler(function(Throwable $Exception) {
 file_put_contents(__DIR__.'/exception.txt', 'Uncaught exception: '.$Exception->getMessage());
});

function SoapFunction() {
//   throw new Exception('bar'); // (1) Invokes the exception handler
//   throw new Error('foo'); // (2) Does not invoke the exception handler!
}

$Server = new SoapServer(null, ['uri' => 'MyNamespace']);
$Server->addFunction('SoapFunction');
ob_start();
$Server->handle();
file_put_contents(__DIR__.'/response.txt', ob_get_flush());



Client script, change http://www.example.com/server.php so that it points to the server script above:
<?php

$Client = new SoapClient(null, ['uri' => 'MyNamespace', 'location' => 'http://www.example.com/server.php']);
$Client->__soapCall('SoapFunction', []);


Expected result:
----------------
By uncommenting the second exception `(2)` and sending a request to the soap server, I would expect that the file `exception.txt` was created with the following contents:

Uncaught exception: foo

Actual result:
--------------
By uncommenting the second exception `(2)` and sending a request to the soap server, the file `response.txt` is created with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>SOAP-ENV:Server</faultcode><faultstring>foo</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-02-28 19:30 UTC] kontakt at beberlei dot de
This has always been the default behavior when SOAP ran into a fatal error in PHP 5 and it was ported to PHP 7 when the Error exception was introduced. 

In any case you should not let the SoapServer ever take control of the error handling, because you might want to convert exceptions gracefully into SoapFaults.

I recommend using a proxy object, or function in your case:

$server = new SOAPServer(null, ['uri' => 'http://localhost/soap.php', 'location' => 'http://localhost/soap.php']);

class SoapErrorProxy {
    public function __call($function, $args) {
        if (!in_array($function, ['SoapFunction'])) {
            throw new SoapFault('CLIENT', 'function doesnt exist');
        }

        try {
            $function(...$args);
        } catch (Throwable $e) {
            throw new SoapFault('SERVER', 'Internal Error');
        }
    }
}

$server->setObject(new SoapErrorProxy());
$server->handle();
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Dec 27 03:01:28 2024 UTC