10

I need to use SOAP to retrieve some data from a database. I'm not an experienced PHP programmer, that's why I need some help. The company which provides the webservice (WSDL) gave me login info and links to the svc and wsdl files. They also gave me an example in C# of how to connect:

var proxy = new ChannelFactory<ServiceReferenceWCF.IWebService2>("custom");
            proxy.Credentials.UserName.UserName = login;
            proxy.Credentials.UserName.Password = pass;
            var result = proxy.CreateChannel();
            var logged_in = result.loggedIn();

Here's my PHP code:

$wsdl_proto = 'https';
$wsdl_host = 'their_wsdl_host';
$wsdl_host_path = 'their_wsdl_path';
$namespace_proto = 'https';
$namespace_host = 'their_namespace_host';
$namespace_path = 'their_namespace_path';
$location = $namespace_proto.'://'.$namespace_host.$namespace_path;
$wsdl_url = $wsdl_proto.'://'.$wsdl_host.$wsdl_host_path;
$connection = new SoapClient($wsdl_url, array('location' => $location, 'soap_version' => SOAP_1_1, 'connection_timeout'=> 600,
    'proxy_login' => "my_login", 'proxy_password' => "my_password"));
$functions = $connection->__getFunctions();
var_dump($functions);
$logged_in = $connection->loggedIn();

It hangs during the loggedIn() function call. This function is listed in the $functions variable, so it is valid. I tried some other functions provided by the service - the result is always the same: the script simply freezes. And by that I mean there is no response from the service and PHP waits for the loggedIn() function to finish. After it exceeds the timeout, I get an error: Error Fetching http headers in... What am I doing wrong? How can I debug it?

UPDATE:

I tried every single thing you guys suggested. But I still didn't manage to solve the problem. I don't use a proxy. You can find the results below:

1. I installed the SoapUI. After configuring the request for the some_method function (creating a basic Auth with credentials) I received a response: An error occurred when verifying security for the message. Ticking the Authenticate pre-emptively option didn't help. I searched for a solution to this error, but I didn't find anything.

2. I tried almost every imaginable combination of options for the SoapClient class. Here are some of them:

$connection = new SoapClient($wsdl_url, array(
    'login' => "login",
    'password' => "pass",
    'trace' => 1,
));

The response headers are empty. The request headers:

REQUEST HEADERS:
POST /file.svc HTTP/1.1
Host: host
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.6.16
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/file/some_method"
Content-Length: 221
Authorization: Basic HASH


REQUEST:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Body><ns1:some_method/></SOAP-ENV:Body></SOAP-ENV:Envelope>

Next combination:

$connection = new SoapClient($wsdl,array(
    'login' => "login",
    'password' => "pass",
    'trace' => 1,
    'connection_timeout' => 500000,
    'cache_wsdl' => WSDL_CACHE_BOTH,
    'keep_alive' => false,
));

The response headers are empty. The request headers are the same, as before.

3. Using this:

$connection->__setLocation('https://host.org/file.svc');

doesn't help. However when I set the location to the WSDL file instead of the SVC file, I get the following response:

HTTP/1.1 405 Method Not Allowed
Connection: Keep-Alive
Content-Length: 1293
Date: Wed, 23 Dec 2015 14:28:53 GMT
Content-Type: text/html
Server: Microsoft-IIS/7.5
Allow: GET, HEAD, OPTIONS, TRACE
X-Powered-By: ASP.NET

I'm sure that the WSDL service is not slow enough, to exceed the timeout (Ricardo Velhote suggested it).

4. I've got an XML configuration file provided with the C# example I mentioned earlier:

<client>
    <endpoint address="https://host/file.svc" binding="customBinding" bindingConfiguration="custom" contract="ServiceReference1.file" name="custom" />
</client>

<bindings>
    <customBinding>
        <binding name="custom">
            <security defaultAlgorithmSuite="Default" authenticationMode="UserNameOverTransport" requireDerivedKeys="true" includeTimestamp="true" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
                <localClientSettings detectReplays="false" />
                <localServiceSettings detectReplays="false" />
            </security>
            <textMessageEncoding messageVersion="Soap11WSAddressing10" />
            <httpsTransport maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" />
        </binding>
    </customBinding>
</bindings>

I tried to extend the SoapClient class as suggested here, but as you can guess it didn't work - the behaviour of the script is still the same.

Community
  • 1
  • 1
Tomasz Kasperczyk
  • 1,991
  • 3
  • 22
  • 43
  • 2
    Just a suggestion but download a copy of SOAP UI and point it to the web service and then create a sample request. Once you have the request working on SOAP UI I would capture the SOAP request issued by the PHP program (use Wireshark) to see if I could spot a difference. It might be that the request is somehow malformed and causing problems. – Namphibian Dec 15 '15 at 21:01
  • have a look at http://stackoverflow.com/questions/19010165/php-soap-error-fetching-http-headers it may help you – Chetan Ameta Dec 17 '15 at 09:10
  • Is it mandatory you use the native PHP's SoapClient? Can you do with a third-party library such as nu-soap? – NaijaProgrammer Dec 21 '15 at 13:56
  • @NaijaProgrammer Yes, I can use external libraries. So far I wasn't able to test all the answers and suggestions (I'm on holiday). But I found that providing the `proxy_host` and `proxy_port` properties, or changing `proxy_login` and `proxy_password` to `login` and `password` didn't solve the problem. Also removing the `location` and `soap_version` properties didn't work at all. I'll try to use SOAP UI and sniff the SOAP request as @Namphibian suggested, when I'll get back from holiday. – Tomasz Kasperczyk Dec 21 '15 at 14:54
  • You are using https protocol kindly be sure to use correct options in your sending request – Vineet1982 Dec 21 '15 at 15:11
  • hard to guess, but can you try setting endpoint url with $client->__setLocation('http://www.somethirdparty.com'); sometimes wsdls contains more than one endpoint. "waiting" makes me think that you are connecting some local endpoint. just a guess. – anilyeni Dec 21 '15 at 15:31
  • 1
    @user3125731 I've edited my answer after this recent update. According to your update and the XML configuration file provided with the example this Webservice appears to be using WS-Addressing so the regular SOAPClient will not work. – Ricardo Velhote Dec 27 '15 at 14:22

3 Answers3

1

According to your update and the XML configuration file provided with the example this Webservice appears to be using WS-Addressing so the regular SOAPClient will not work and requires it to be extended in order to support WS-Addressing.

This is the line that gives it away

<textMessageEncoding messageVersion="Soap11WSAddressing10" />

I have yet to use WS-Addressing but I was recently analysing an API for project we will be working on in the future that requires it.

Please take note of this project or this other project which may be useful (it's easy to search for more PHP WS-Addressing).

Again, I have only done research and do not have any hands-on experience to help you with actual code :)


[EDIT: Obsolete answer after the update]

First of all, you might be being mislead by the use of the variable proxy in the example code. They are probably referring to HTTP Basic Authentication and not a proxy.

Try replacing proxy_login and proxy_password with login and password.

However, having said that, if you are getting the WSDL it means that at least it's connecting and obtaining the information about the service (which is good).

In normal situations you do not need to specify location in SoapClient as it should be defined by the WSDL file. By setting the location parameter you are overriding what is set in the WSDL file and you may be pointing it to a location that does not exist.

Try ommiting the location and soap_version from the SoapClient constructor and let the library handle those parameter automatically:

$connection = new SoapClient($wsdl_url, array('connection_timeout'=> 600,
    'proxy_login' => "my_login", 'proxy_password' => "my_password"));

On the other hand, perhaps you are dealing with an extremely slow Web Service. There are many parameters in PHP that may be affecting the time it takes to timeout and most likely they are well below your connection_timeout parameter:

Ricardo Velhote
  • 4,630
  • 1
  • 24
  • 30
  • 1
    Ok, thanks for the answer. At least I know that using SOAPClient is pointless. I will check out the projects you linked. – Tomasz Kasperczyk Dec 28 '15 at 11:07
  • 1
    @user3125731 Sorry, I did not make it clear in my answer (will edit). The projects I provided are extensions to SOAPClient not replacements :) – Ricardo Velhote Dec 28 '15 at 11:09
0

You have missed proxy_host and proxy_port in the options... If you need proxy to work provide theese parameters:

....
$connection = new SoapClient($wsdl_url, array(
    'location' => $location, 
    'soap_version' => SOAP_1_1, 
    'connection_timeout'=> 600,
    'proxy_host' => '....',     // Your proxy host
    'proxy_port' => 8080,       // Your proxy port
    'proxy_login' => "my_login", 
    'proxy_password' => "my_password"
));
Gennadiy Litvinyuk
  • 1,536
  • 12
  • 21
0

Your first action when coding in the language you don't really know should be using manuals and examples.

Here is a link to PHP Manual on SoapClient - http://php.net/manual/en/soapclient.soapclient.php

For debugging SoapClient you can pass "trace" argument. Quote from the manual: Setting the boolean trace option enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders.

$client = new SoapClient("some.wsdl", array('trace'   => true));

So, in your case if you want to see what is going wrong do this:

$client = SoapClient($wsdl_url, array('trace' => 1));
$result = $client->SomeFunction();

echo "REQUEST HEADERS:\n" . $client->__getLastRequestHeaders() . "\n";
echo "REQUEST:\n" . $client->__getLastRequest() . "\n";
echo "Response headers:\n" . $client->__getLastResponseHeaders() . "\n";
echo "Response:\n" . $client->__getLastResponse() . "\n";

Also, as was noted in another answer - you are setting proxy_login and proxy_password parameters in the request, which should only be used if you are using proxy server to connect to that WSDL service.

Anatoliy Kim
  • 768
  • 4
  • 13