PHP SoapClient port bug workaround

PHP’s SoapClient has a bug (PHP 5.2.10 here, still not fixed apparently) when the SOAP service must be accessed on a different port other than 80. The WSDL file is fetched correctly, but all subsequent requests are made without any port in the Host field. This causes a SoapFault exception when trying to call any of the service’s methods.

So if the WSDL location is:

All requests after fetching the WSDL file will be made to:

The simplest way i could work around this was to extend SoapClient and intercept the constructor and the __doRequest method to inject the port in the location on each request:

It works for me, and I would like to know if it doesn’t cover your particular case :)

20 thoughts on “PHP SoapClient port bug workaround

  1. Mihaly Koles

    Thanks for this “little” hack!
    PHP SOAP is driving me crazy, your class helped me to solve a very frustrating issue! Thanks again pal, you saved my day, seriously! :)

    Reply
  2. Guy Incognito

    I wouldn’t call that a PHP bug, since the service URL is defined within the WSDL file, where the port is missing already.

    Reply
  3. Stéphaen

    Great ! it solved my problem and saved my day !!

    One point : to make it works I had to change the _doRequest declaration by :

    (…)
    function __doRequest($request, $location, $action, $version, $one_way = 0)
    (…)
    php 5.3.5

    Thank’s

    Reply
  4. yusman

    hi Victor Stanciu, thank u for this tutorial this verry usefull fro newbee like me, but i use codeigniter, can u give a sample how to implement this code on codeiginiter framework ..?

    Reply
  5. DissidentRage

    SoapClient::__doRequest now has an additional parameter, $one_way, so you get a Strict Standards warning with this code.

    To fix it, append this to the parameters of the override function:

    $one_way = 0

    Then pass it to the superclass function after parsing the URL.

    Reply
  6. Angel

    How do I use this in my SOAP function. Right now I have:

    try {
    $Client = @new SoapClient(“https://myfeed.whatever.asmx?WSDL”);
    // Prepare SoapHeader parameters
    $Parms = array(‘Username’=>’xxxxx’,’Password’=>’xxxxx’);
    $Client-> Login ($Parms);

    }
    catch (Exception $e) {
    echo “Error!”;
    echo $e -> getMessage ();
    }

    I’m getting the error message:

    Server was unable to process request. —> You are not authenticated. Please run the Login function first. It isn’t recognizing the Username.

    Ideas?

    Reply
  7. Raymond

    Many thanks for sharing you solution with. I really appreciate it.

    I just found out that the issue is still present in php 5.3.10 but it will work if you call the method using __soapCall():

    $rt = $client->__soapCall(‘getCustomers’, array($parameters));

    Reply
  8. Kelzang

    Also make sure curl is available.If the SoapClientAuth class is uablne to authenticate to the https server then it will generate an exception with code=401 and message= Access Denied .Otherwise it returns the error encountered by curl.Should curl return the status code 200 when downloading the wsdl then control is passed over to SoapClient and errors at this stage are what you would normally encounter if the wsdl did not request authentication for downloading the wsdl.I’d figure the next step, if curl is installed and working properly. Would be to switch on tracing and call __getLastResponse to see what the SOAP server returned.

    Reply
  9. heack

    Thanks you very much! it really helps a lot.
    And I also add a method to change host:

    class My_SoapClient extends SoapClient {

    private $altered_host = NULL;

    public function setAlteredHost($newHost)
    {
    $this->altered_host = $newHost;
    }

    public function __construct($wsdl, $options) {
    $url = parse_url($wsdl);
    if (isset($url['port'])) {
    $this->_port = $url['port'];
    }
    return parent::__construct($wsdl, $options);
    }

    public function __doRequest($request, $location, $action, $version) {
    // echo “***”.$location.”**”;
    if (!empty($this->altered_host)) {
    $location = $this->altered_host;
    } else {
    $parts = parse_url($location);
    if ($this->_port) {
    $parts['port'] = $this->_port;

    }
    $location = $this->buildLocation($parts);
    }

    $return = parent::__doRequest($request, $location, $action, $version);
    return $return;
    }

    public function buildLocation($parts = array()) {
    $location = ”;

    if (isset($parts['scheme'])) {
    $location .= $parts['scheme'].'://';
    }
    if (isset($parts['user']) || isset($parts['pass'])) {
    $location .= $parts['user'].':’.$parts['pass'].’@';
    }
    $location .= $parts['host'];
    if (isset($parts['port'])) {
    $location .= ‘:’.$parts['port'];
    }
    $location .= $parts['path'];
    if (isset($parts['query'])) {
    $location .= ‘?’.$parts['query'];
    }

    return $location;
    }
    }

    Reply
  10. Michał

    Adding parameter “location” solve problem:

    $wsdlUrl = “http://example.com:33080/soap/server/path?WSDL”
    $client = new SoapClient($wsdlUrl, array(“location” => $wsdlUrl));

    OR

    $wsdlUrl = “http://example.com:33080/soap/server/path?WSDL”
    $client = new SoapClient($wsdlUrl);
    $client->__setLocation($wsdlUrl);

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">