Asked  7 Months ago    Answers:  5   Viewed   46 times

Is there available any tool for PHP which can be used to generate code for consuming a web service based on its WSDL? Something comparable to clicking "Add Web Reference" in Visual Studio or the Eclipse plugin which does the same thing for Java.

 Answers

92

I've had great success with wsdl2php. It will automatically create wrapper classes for all objects and methods used in your web service.

Wednesday, March 31, 2021
 
Blacksonic
answered 7 Months ago
93

As referenced that - you will have to hand code your "proxy" for this web service.

One example of manually making a web service call - you may have to tweak the method some.

private string MakeWebServiceCall(string methodName, string requestXmlString)
        {
            WebRequest webRequest = WebRequest.Create("https://subreg.forpsi.com/robot2/subreg_command.php");

            HttpWebRequest httpRequest = (HttpWebRequest)webRequest;
            httpRequest.Method = "POST";
            httpRequest.ContentType = "text/xml";
            httpRequest.Headers.Add("SOAPAction: https://subreg.forpsi.com/robot2/subreg_command.php/" + methodName);
            Stream requestStream = httpRequest.GetRequestStream();

            //Create Stream and Complete Request
            StreamWriter streamWriter = new StreamWriter(requestStream);
            streamWriter.Write(String.Format(this.GetSoapString(), requestXmlString));
            streamWriter.Close();

            //Get the Response
            WebResponse webResponse = httpRequest.GetResponse();
            Stream responseStream = webResponse.GetResponseStream();
            StreamReader streamReader = new StreamReader(responseStream);

            //Read the response into an xml document
            System.Xml.XmlDocument soapResonseXMLDocument = new System.Xml.XmlDocument();
            soapResonseXMLDocument.LoadXml(streamReader.ReadToEnd());

            //return only the xml representing the response details (inner request)
            return soapResonseXMLDocument.GetElementsByTagName(methodName + "Result")[0].InnerXml;
        }

I would recommend creating xsd's which can be used to generate objects (using xsd.exe) and then you can serialized/deserialize responses and requests to actually objects.

EDIT: GetSoapString() method

private string GetSoapString()
        {
            StringBuilder soapRequest = new StringBuilder("<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"");
            soapRequest.Append(" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ");
            soapRequest.Append("xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>");
            soapRequest.Append("{0}");
            soapRequest.Append("</soap:Body></soap:Envelope>");
            return soapRequest.ToString();
        }

For Steve's Reference

Call one looks like:

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<get_contact>
<id>123</id>
</get_contact>
</soap:Body>
</soap:Envelope>

Response:

<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:t3" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
   <SOAP-ENV:Body>
      <ns1:get_contactResponse>
         <get_contactReturn xsi:type="xsd:boolean">false</get_contactReturn>
      </ns1:get_contactResponse>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Saturday, May 29, 2021
 
EzzDev
answered 5 Months ago
71

I just solved this issue. I used nusoap_client to solve this issue. And I change the request format too, like this

$msg = "<soapenv:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:urn='urn:sj_service'>";

And so on..

Thanks

Saturday, May 29, 2021
 
Tapha
answered 5 Months ago
32

Introduction

Obviously, documentation is missing here. Unfortunately, $soapClient->__getTypes() doesn't tell much. It only shows available complex types supported by a web service, but it doesn't show us the relationship among them. Even if You have a list of all available operations with their input and output types returned by $soapClient->__getFunctions(), there is no guarantee You can proceed without knowing the exact nature of the complex types or without having any kind of documentation. But fortunately, this is a SOAP-based web service that provides a WSDL document. The WSDL document describes all the supported operations and complex types, as well as their relationships. So, we can figure out how to use the service by only examining the WSDL document.

There are two ways of examining the WSDL document:

  1. generate artifacts (client classes) from the WSDL document and examine the artifacts
  2. look through the WSDL document and the XML Schema documents.

1. Artifacts

The artifacts can be generated by tools provided by the strong typed languages like Java or C#. The https://qa-api.ukmail.com/Services/UKMAuthenticationServices/ page suggests to use the svcutil.exe tool to generate the artifacts for the C# programming language, or You can also use the wsimport tool to generate the artifacts for the Java programming language. I doubt that there can be any good tool for generating the artifacts for the PHP programming language.

2. WSDL document and XML Schemas

If You're not familiar with C# or Java, You can always examine the WSDL document by looking through it and the XML Schemas. The XML Schemas can be included in the WSDL document or imported from an external file. While the WSDL document describes the operations that can be performed on the web service, the XML Schemas describe the complex types and their relationships.

Action

I wrote the Introduction part so that You know how to do it on Your own. Below I want to show an example of it. For the purpose of examining the WSDL document I used both ways. First I generated the artifacts using the wsimport tool, then I read a lot of XML.

The WSDL document for this service is divided into several files using import statements. So, in order to find all the operations and complex types You have to follow the import statements.

Authentication

If We look at the Authentication Service's WSDL document (location), We can see that it imports another WSDL document:

<wsdl:import namespace="http://tempuri.org/" location="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?wsdl=wsdl1"/>

The latter (location), in its turn, imports another one:

<wsdl:import namespace="http://www.UKMail.com/Services/Contracts/ServiceContracts" location="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?wsdl=wsdl0"/>

The final one (location), imports all the related XML Schemas:

<wsdl:types>
  <xsd:schema targetNamespace="http://www.UKMail.com/Services/Contracts/ServiceContracts/Imports">
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd0" namespace="http://www.UKMail.com/Services/Contracts/ServiceContracts"/>
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd1" namespace="http://schemas.microsoft.com/2003/10/Serialization/"/>
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd2" namespace="http://www.UKMail.com/Services/Contracts/DataContracts"/>
    <xsd:import schemaLocation="https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?xsd=xsd3" namespace="http://schemas.datacontract.org/2004/07/UKMWebAPICommon.WebResponses"/>
  </xsd:schema>
</wsdl:types>

It also describes the operations which can also be viewed by calling $soapClient->__getFunctions():

Array
(
    [0] => LoginResponse Login(Login $parameters)
    [1] => LogoutResponse Logout(Logout $parameters)
)

Here, We see that the Login() operation accepts $parameters of type Login as its argument and returns a response of type LoginResponse. This is how it looks in the WSDL document:

<wsdl:operation name="Login">
  <wsdl:input wsaw:Action="http://www.UKMail.com/Services/IUKMAuthenticationService/Login" message="tns:IUKMAuthenticationService_Login_InputMessage"/>
  <wsdl:output wsaw:Action="http://www.UKMail.com/Services/Contracts/ServiceContracts/IUKMAuthenticationService/LoginResponse" message="tns:IUKMAuthenticationService_Login_OutputMessage"/>
</wsdl:operation>

Login is a complex type, this can be seen in one of the imported XML Schema documents (schemaLocation):

<xs:element name="Login">
  <xs:complexType>
    <xs:sequence>
      <xs:element xmlns:q1="http://www.UKMail.com/Services/Contracts/DataContracts" minOccurs="0" name="loginWebRequest" nillable="true" type="q1:LoginWebRequest"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

It has an element named loginWebRequest which is also a complex type called LoginWebRequest which is described in another imported XML Schema:

<xs:complexType name="LoginWebRequest">
  <xs:sequence>
    <xs:element name="Password" nillable="true" type="xs:string"/>
    <xs:element name="Username" nillable="true" type="xs:string"/>
  </xs:sequence>
</xs:complexType>

LoginWebRequest is simpler. It has two simple types Username and Password of type String.

In PHP complex types are represented by objects of stdClass. So, in order to call the Login() operation, We have to create two objects Login and LoginWebRequest:

$LoginWebRequest = new stdClass();
$LoginWebRequest->Username = 'Username';
$LoginWebRequest->Password = 'p@$$w0rd';

$Login = new stdClass();
$Login->loginWebRequest = $LoginWebRequest;

$soapClient = new SoapClient('https://qa-api.ukmail.com/Services/UKMAuthenticationServices/UKMAuthenticationService.svc?wsdl');
$LoginResponse = $soapClient->Login($Login);

This gives us a result of type LoginResponse:

<xs:element name="LoginResponse">
  <xs:complexType>
    <xs:sequence>
      <xs:element xmlns:q2="http://www.UKMail.com/Services/Contracts/DataContracts" minOccurs="0" name="LoginResult" nillable="true" type="q2:UKMLoginResponse"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

, which contains an element named LoginResult which has a type of UKMLoginResponse:

<xs:complexType name="UKMLoginResponse">
  <xs:complexContent mixed="false">
    <xs:extension base="tns:UKMWebResponse">
      <xs:sequence>
        <xs:element minOccurs="0" name="Accounts" nillable="true" type="tns:ArrayOfAccountWebModel"/>
        <xs:element name="AuthenticationToken" nillable="true" type="xs:string"/>
      </xs:sequence>
    </xs:extension>
  </xs:complexContent>
</xs:complexType>

UKMLoginResponse has two elements of its own Accounts of type ArrayOfAccountWebModel and AuthenticationToken of type String and three more elements inhereted from UKMWebResponse (note the extension statement) Errors of type ArrayOfUKMWebError, Warnings of type ArrayOfUKMWebWarning and Result of type UKMResultState:

<xs:complexType name="UKMWebResponse">
  <xs:sequence>
    <xs:element minOccurs="0" name="Errors" nillable="true" type="tns:ArrayOfUKMWebError"/>
    <xs:element xmlns:q1="http://schemas.datacontract.org/2004/07/UKMWebAPICommon.WebResponses" name="Result" type="q1:UKMResultState"/>
    <xs:element minOccurs="0" name="Warnings" nillable="true" type="tns:ArrayOfUKMWebWarning"/>
  </xs:sequence>
</xs:complexType>

In the artifacts generated by the wsimport tool it looks like this:

public class UKMLoginResponse extends UKMWebResponse { ... }

So, in order to get the authentication token from the LoginResponse, we do the following:

$LoginResponse = $soapClient->Login($Login);
$AuthenticationToken = $LoginResponse->LoginResult->AuthenticationToken;

Calling a method

I won't be very specific here, because it's very similar to what we did above.

As an example, let's call a AddDomesticConsignment() method. According to the WSDL document of the Consignment Service and the result returned by $soapClient->__getFunctions() the AddDomesticConsignment() method takes one $parameters argument of type AddDomesticConsignment and returns a result of type AddDomesticConsignmentResponse. By analyzing the AddDomesticConsignment complex type, we see that it has an element named request of type AddDomesticConsignmentWebRequest which extends AddConsignmentWebRequest which itself extends WebRequest. Following is the list of all of the elements of the AddDomesticConsignmentWebRequest type:

// AddDomesticConsignmentWebRequest's own elements
boolean BookIn
decimal CODAmount
string ConfirmationEmail
string ConfirmationTelephone
boolean ExchangeOnDelivery
int ExtendedCover
boolean LongLength
PreDeliveryNotificationType PreDeliveryNotification
string SecureLocation1
string SecureLocation2
boolean SignatureOptional

// elements inhereted from AddConsignmentWebRequest
string AccountNumber
AddressWebModel Address
string AlternativeRef
string BusinessName
string CollectionJobNumber
boolean ConfirmationOfDelivery
string ContactName
string CustomersRef
string Email
int Items
int ServiceKey
string SpecialInstructions1
string SpecialInstructions2
string Telephone
decimal Weight

// elements inhereted from WebRequest
string Username
string AuthenticationToken

Note that not all of the elements are required. Those which are optional have the minOccurs attribute set to 0 in the XML Schema.

<xs:element minOccurs="0" name="PreDeliveryNotification" type="tns:PreDeliveryNotificationType"/>

So, eventually this is how We call the method:

$AddDomesticConsignmentWebRequest = new stdClass();
$AddDomesticConsignmentWebRequest->Username = 'Username';
// setting the Authentication Token from the previous step
$AddDomesticConsignmentWebRequest->AuthenticationToken = $AuthenticationToken;
// other properties are set here...

$AddDomesticConsignment = new stdClass();
$AddDomesticConsignment->request = $AddDomesticConsignmentWebRequest;

$soapClient = new SoapClient('https://qa-api.ukmail.com/Services/UKMConsignmentServices/UKMConsignmentService.svc?wsdl');
$AddDomesticConsignmentResponse = $soapClient->AddDomesticConsignment($AddDomesticConsignment);

The AddDomesticConsignmentResponse is parsed as We parsed the LoginResponse according to its definition in the XML Schema document.

Well, I guess this is all to it. I didn't try it myself, but in theory it should work. Hope this helps.

UPDATE

According to the documentation tracking the consignment should be as easy as doing the following:

// create the SOAP client
$soapClient = new SoapClient('http://web-service/?wsdl');
// call the `ConsignmentTrackingSearchV1` method and pass the search parameters
$ConsignmentTrackingSearchV1Response = $soapClient->ConsignmentTrackingSearchV1(
    'mail.com', // Username
    '123',      // Password
    '',         // Token
    '01161',    // ConsignmentNumber
    'false',    // IsPartialConsignmentNumber
    '',         // CustomerReference
    'false'     // IsPartialCustomerReference
    '',         // DeliveryPostCode
    '',         // MailingID
    100         // MaxResults
);
// parse the response
$ConsignmentTrackingSearchV1Result = $ConsignmentTrackingSearchV1Response->ConsignmentTrackingSearchV1Result;
$ResultState = $ConsignmentTrackingSearchV1Result->ResultState; // Successful
$ConsignmentResults = $ConsignmentTrackingSearchV1Result->ConsignmentResults;
// loop through the `ConsignmentResults`
foreach ($ConsignmentResults as $ConsignmentSearchResult) {
    $ConsignmentNumber = $ConsignmentSearchResult->ConsignmentNumber;
    $ConsignmentStatus = $ConsignmentSearchResult->ConsignmentStatus;
    // other properties
}

That's it!

Wednesday, June 23, 2021
 
dimitarvp
answered 4 Months ago
10

This is covered by the JAX-WS Specification. Basically, set the username/password as properties on the request context:

((BindingProvider)proxy).getRequestContext().put(
    BindingProvider.USERNAME_PROPERTY, "joe");
((BindingProvider)proxy).getRequestContext().put(
    BindingProvider.PASSWORD_PROPERTY, "pswd");

The runtime puts them into the HTTP header.

Wednesday, August 4, 2021
 
Joegramming
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share