php soapclient examples. Writing a SOAP client-server application in PHP

I need to connect to one web service and this is all I have:

    Call example:

    username Password

    I know this function returns a string;

This is what I have done so far:

    Created a Service Reference adding only the WSDL address specified in point 1.

    Created a webservice instance and called the function with all the required parameter but no user and password for the header.

How can I continue? Thank you in advance.

1 answer

This might be a good place to start if you need to add your credentials; i think you might have to since you got them somehow. The part where you add your credentials is listed below:

UsernameToken userToken = new UsernameToken(userName, password, PasswordOption.SendHashed); Service1 serviceProxy = new Service1(); SoapContext requestContext = serviceProxy.RequestSoapContext; requestContext.Security.Tokens.Add(userToken);

  1. Add your credentials by inserting them into a specific token - the type of which belongs to the Microsoft.Web.Services2.Security.Tokens namespace
  2. Create a proxy for your service (in the above example, serviceProxy)
  3. Access your request header through your RequestSoapContext service
  4. Add token to request

Also, I think you could skip the "?wsdl" part of the address as it refers to the web service specification. Once that's done, you can try calling the function and see how it all pans out: if the function should return something, check what you expected.

I'm used to writing PHP code, but don't use object oriented coding much. Now I need to interact with SOAP (as a client) and I can't seem to get the syntax right. I have a WSDL file which allows me to properly set up a new connection using the SoapClient class. However, I can't make the right call and get the data back. I need to send the following (simplified) data:

  • contact ID
  • contact name
  • general description
  • Sum

There are two functions in the WSDL document, but I only need one ("FirstFunction" below). Here is the script I am running to get information about available functions and types:

$client = new SoapClient("http://example.com/webservices?wsdl"); var_dump($client->__getFunctions()); var_dump($client->__getTypes());

And here is the output it generates:

Array( => "FirstFunction Function1(FirstFunction $parameters)", => "SecondFunction Function2(SecondFunction $parameters)",); array( => struct Contact ( id id; name name; ) => string "string description" => string "int amount" )

Suppose I want to call FirstFunction with data:

  • Contact ID: 100
  • Contact person: John
  • General Description: Barrel of Oil
  • Amount: 500

What would be the correct syntax? I've tried all sorts of variations, but the soap structure seems to be quite flexible, so there are so many ways to do it. Couldn't figure it out from the manual...

UPDATE 1: Tried sample from MMK:

$client = new SoapClient("http://example.com/webservices?wsdl"); $params = array("id" => 100, "name" => "John", "description" => "Barrel of Oil", "amount" => 500,); $response = $client->__soapCall("Function1", array($params));

But I get this response: Object has no "Contact" property . As you can see in the output of getTypes() , there is a struct called Contact , so I think I somehow need to clarify that my parameters include Contact data, but the question is: how?

UPDATE 2: I also tried these structures, same error.

$params = array(array("id" => 100, "name" => "John",), "Barrel of Oil", 500,);

As well as:

$params = array("Contact" => array("id" => 100, "name" => "John",), "description" => "Barrel of Oil", "amount" => 500,);

Error in both cases: Object does not have "Contact" property

8 responses

This is what you need to do.

Just to know, I tried to recreate your situation...

  • In this example, I have created a .NET web service using a WebMethod called Function1 and these are the parameters:

Function1 (contact pin, string description, int count)

    In which Contact is just a bean class that has getters and setters for id and name , like in your case.

    You can download this .NET web service with:

The code.

This is what you need to do on the side PHP:

(Tested and working)

id = $id; $this->name = $name; ) ) /* Initialize webservice with your WSDL */ $client = new SoapClient("http://localhost:10139/Service1.asmx?wsdl"); /* Fill your Contact Object */ $contact = new Contact(100, "John"); /* Set your parameters for the request */ $params = array("Contact" => $contact, "description" => "Barrel of Oil", "amount" => 500,); /* Invoke webservice method with your parameters, in this case: Function1 */ $response = $client->__soapCall("Function1", array($params)); /* Print webservice response */ var_dump($response); ?>

How do I know it's working?

  • If you do print_r($params); , you will see this output as your web service is expecting:

Array ([Contact] => Contact object ( => 100 => John) [description] => Oil barrel [amount] => 500)

  • When I debugged the sample .NET webservice I got the following:

(As you can see, the Contact object is not null, as well as other parameters, which means that your request was successfully completed from the PHP side).

  • The response from the .NET webservice was as expected and shown on the PHP side:

object (stdClass) public "Function1Result" => string "Details for your request! id: 100, name: John, description: Barrel of oil, quantity: 500" (length = 98)

Hope this helps :-)

You can also use SOAP services:

"Spain", "CityName" => "Alicante"); $response = $soapclient->getWeather($params); var_dump($response); // Get the Cities By Country $param = array("CountryName" => "Spain"); $response = $soapclient->getCitiesByCountry($param); var_dump($response);

This is an example with a real service and it works.

Hope this helps.

First, initialize the web services:

$client = new SoapClient("http://example.com/webservices?wsdl");

Then set and pass parameters:

$params = array ("arg0" => $contactid, "arg1" => $desc, "arg2" => $contactname); $response = $client->__soapCall("methodname", array($params));

Note that the method name is available in the WSDL as the name of the operation, for example:

I don't know why my web service has the same structure as yours, but it doesn't need a class for the parameter, just an array.

For example:- My WSDL:

5390a7006cee11e0ae3e0800200c9a66 831f8c1ad25e1dc89cf2d8f23d2af...fa85155f5c67627 VITS-STAELENS Zoethout thee 0.100 10K24 2012-12-31 Gladys Roldan de Moras

Calle General Oraá 26 (4º edition) 28006 Madrid ES
[email protected] es

Var_dump($client->getFunctions()); var_dump($client->getTypes());

Here is the result:

Array 0 => string "OrderConfirmation createOrder(OrderRequest $createOrder)" (length=56) array 0 => string "struct OrderRequest ( Identification identification; Delivery delivery; Parcel parcel; Receiver receiver; string reference; )" (length=130) 1 => string "struct Identification ( string sender; string hash; string originator; )" (length=75) 2 => string "struct Delivery ( Node from; Node to; )" (length=41) 3 => string " struct Node ( string country; string node; )" (length=46) 4 => string "struct Parcel ( string description; decimal weight; string orderNumber; date orderDate; )" (length=93) 5 => string "struct Receiver ( string firstName; string surname; Address address; string email; string language; )" (length=106) 6 => string "struct Address ( string line1; string line2; string postalCode; string city; string country; )" (length =99) 7 => string "struct OrderConfirmation ( string trackingNumber; string reference; )" (length=71) 8 => string "str uctOrderServiceException(stringcode; OrderServiceException faultInfo; string message; )" (length=97)

So in my code:

$client = new SoapClient("http://packandship-ws.kiala.com/psws/order?wsdl"); $params = array("reference" => $orderId, "identification" => array("sender" => param("kiala", "sender_id"), "hash" => hash("sha512", $orderId . param("kiala", "sender_id") .param("kiala", "password")), "originator" => null,), "delivery" => array("from" => array("country" = > "es", "node" => "",), "to" => array("country" => "es", "node" => "0299")),), "parcel" => array( "description" => "Description", "weight" => 0.200, "orderNumber" => $orderId, "orderDate" => date("Ymd")), "receiver" => array("firstName" => " Customer First Name", "surname" => "Customer Sur Name", "address" => array("line1" => "Line 1 Adress", "line2" => "Line 2 Adress", "postalCode" => 28006, "city" => "Madrid", "country" => "es",), "email" => " [email protected]", "language" => "es")); $result = $client->createOrder($params); var_dump($result);

but he is successful!

This is a good example for the SOAP "__call" function. However, it is outdated.

Envio Internacional: "; $vem = $cliente->__call("CustoEMSInternacional",array($int_zona, $int_peso)); print $vem; print "

"; ?>

SOAP (Simple Object Access Protocol) is an XML-based protocol for exchanging structured information between distributed applications over existing web protocols such as HTTP. The SOAP specification defines the format used by XML messages, how they should be handled by a set of encoding rules for the standard, data types, and conventions for remote procedure calls and call responses.

Web services are a trendy and modern technology. The list of technologies related to web services is growing almost daily, but SOAP is probably the most important of them all. It is rapidly becoming the standard protocol for accessing web services. It uses XML messages to exchange information between endpoints while providing some of the benefits of binary protocols. Support for RPC (Remote Procedure Calls) was one of the minor features of the SOAP protocol in the beginning, but now it has become one of the most used features.

The SOAP extension for PHP 5 is the first attempt to implement SOAP support in PHP in C. It has several advantages over existing SOAP implementations written in PHP, the most important of which is speed. At the moment, the extension is considered experimental, but gradually it will become more and more reliable and stable.

The SOAP extension implements large subsets of the SOAP 1.1, SOAP 1.2, and WSDL 1.1 specifications. The main goal is to make the most of SOAP's RPC capabilities. Wherever possible, WSDL is used to make the implementation of web services easier.

First SOAP client

To demonstrate how to create a simple SOAP client, we use the "Delayed Stock Quote" demo service from the XMethods site. Before we start writing PHP code, we need to collect the following information about this particular service:

  • Method name
  • The URL where this service is located
  • The value of the SOAPAction method header
  • Method namespace
  • Names and types of method input and output parameters

Fortunately, all this information is available on the XMethods website at http://www.xmethods.com/ from the service's RPC profile:

Method name getQuote
Service URL http://66.28.98.121:9090/soap
SOAPAction urn:xmethods-delayed-quotes#getQuote
Method namespace urn:xmethods-delayed-quotes
Input parameters Symbol (String)
output parameters result (float)

Example 1 (client1.php)

$client = new SoapClient(NULL ,
array(
"location" => "http://66.28.98.121:9090/soap",
"uri" =>
"style" => SOAP_RPC ,
"use" => SOAP_ENCODED
));

Print($client -> __call(
/* SOAP method name */
"getQuote" ,
/* Parameters */
array(
new SoapParam(
/* Parameter value */
"ibm" ,
/* Parameter name */
"symbol"
)),
/* Options */
array(
/* SOAP method namespace */
"uri" => "urn:xmethods-delayed-quotes" ,
/* SOAPAction HTTP header for SOAP method */
"soap action" => "urn:xmethods-delayed-quotes#getQuote"
)). "\n");
?>

As you can see, solving this simple problem required quite a lot of work.

Fortunately, web services can describe themselves to clients using WSDL, which is generally quite convenient. The WSDL for the "Delayed Stock Quote" service is available on its info page on xmethods.com - http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl .

Here is a version of the same client rewritten to work with this WSDL document. Here we no longer need to specify the server URI, namespace, SOAPAction header, encoding method, and parameter types. All this information is taken from the WSDL file.

Example 2 (client2.php)

$client = new
SoapClient(
"http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl"
);

Print($client -> getQuote("ibm" ));
?>

That's a little easier, right?

What problems arise when using WSDL? The only argument against using it is that the client must read the WSDL from the server before any procedure can be called, which can take quite a long time on the web. To speed things up, the SOAP extension provides the following configuration options: soap.wsdl_cache_enabled, soap.wsdl_cache_dir and soap.wsdl_cache_ttl. They can be set in the php.ini file or with ini_set() (see Example 4). By default, WSDL caching is enabled and WSDL files are cached for 1 day.

Here is the SOAP section of the php.ini file with the default values. You can copy them to your php.ini.

[soap]

soap. wsdl_cache_enabled = "1"
; enable or disable WSDL caching

soap. wsdl_cache_dir = "/tmp"
; specifies the name of the directory in which the SOAP- extension will store cached files

soap. wsdl_cache_ttl="86400"
; (lifetime ) sets the time(in seconds) which files from the cache can be used

First SOAP Server

Let's try to write our own SOAP web service that will do the same as the "Delayed Stock Quote" service with XMethods.

The first thing to do is create a WSDL document that describes our service in a format that clients can understand. To do this, you will need to slightly modify the original document taken from the Xmethods website, so we will start by looking at it in more detail.

The message section defines two messages. The first is getQuoteRequest , which sends a message to getQuote and takes a single-string parameter named symbol. The second message is getQuoteResponse, the response to the getQuote request, passing a single float value named Result.

The portType section defines a single getQuote operation that specifies which of the messages described in the message section will be used for the request and which for the response.

The binding section defines how the message is to be encoded and transmitted. In this case, it says that we will send the RPC request over HTTP using SOAP encoding. It also defines the namespace and value of the SOAPAction header for the getQuote method.

Finally, the service section defines the URL where the service is located.

Example 3 (stockquote.wsdl)


targetNamespace="http://example.org/StockQuote"
xmlns:tns="http://example.org/StockQuote"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">












transport="http://schemas.xmlsoap.org/soap/http"/>



encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>


encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>








Note: WSDL caching is enabled by default. While developing and debugging your WSDL, caching is best disabled.

Now it's time to start creating our server.

First of all, we will develop a getQuote() function that will process incoming requests from the web. Next, we will create an object of the SoapServer class and attach our function to it using the SoapServer::addFunction() method. As you will see later, the SoapServer() constructor has only one parameter - the path to the WSDL document that describes the service.

Example 4 (server1.php)

$quotes = array(
"ibm" => 98.42
);


global $quotes ;
return $quotes [ $symbol ];
}

ini_set("soap.wsdl_cache_enabled" , "0" ); // disable WSDL caching
$server = new SoapServer("stockquote1.wsdl" );
$server -> addFunction("getQuote");
$server -> handle();
?>

SoapServer can work without WSDL, much like a client, but this option does not have any advantages that would make it worth using. If you still want to work this way, you must make sure that the return values ​​are objects of the SoapParam and SoapVar classes (as in the first example.

Here is the client to access our SOAP server. Compared to the previous example, only a link to the location of the WSDL has been added. The file "stockquote1.wsdl" is supposed to be in the same directory as the SOAP server.

Example 5 (client3.php)

$client = new SoapClient("stockquote1.wsdl" );
print($client -> getQuote("ibm" ));
?>

What are the main problems in our client and server?

For starters, they don't handle errors. What happens when the server does not find a suitable result for the symbol value passed to it? SOAP has a special message format for error messages, SoapFault. To generate such a message, the server must throw an exception using the SoapFault object. The first parameter of the SoapFault() constructor is a string with the error code, the second is a string with a description of the error. The client must be written to handle SoapFault exceptions.

Secondly, the web service functionality is best encapsulated in a PHP class. In this case, we will not need to use global variables and add each SOAP method to the server separately. Instead, we can add an entire class, and all of its methods will be available via SOAP. Here are the client and server versions modified accordingly.

Example 6 (server2.php)

class QuoteService(
private $quotes = array("ibm" => 98.42 );

Function getQuote ($symbol ) (
if (isset($this -> quotes [ $symbol ])) (
return $this -> quotes [ $symbol ];
) else (
throw new
SoapFault("Server" , "Unknown Symbol "$symbol"." );
}
}
}

$server = new SoapServer("stockquote2.wsdl" );
$server -> setClass("QuoteService");
$server -> handle();
?>

As you can see, I used the SoapServer::setClass() method to connect the SoapServer object to the QuoteService class.

Example 7 (client4.php)

$client = new SoapClient("stockquote2.wsdl");
try(
echo "

\n" ; 
print($client -> getQuote("ibm" ));
echo "\n" ;
print($client -> getQuote("microsoft" ));
echo "\n
\n" ;
) catch (SoapFault $exception ) (
echo $exception ;
}
?>

What's inside?

If you want to understand the format of SOAP messages, or want to debug a SOAP client yourself, then this section is for you.

As you saw in the first example, the SoapClient() constructor takes an associative array as its second parameter. With this array, we can call various options on the server.

Let's see two of them:

  • trace - allows the client to store SOAP requests and responses (off by default).
  • exceptions - allows the client to control the exception mechanism (enabled by default).

Let's look at the following SOAP client example. This is a modified client from Example 5, showing exactly what is passed between the client and the server. To obtain this information, the __getLastRequest() and __getLastResponse() methods are used.

Example 8 (client5.php)

$client = new SoapClient("stockquote1.wsdl" ,array(
"trace" => 1 ,
"exceptions" => 0 ));
$client -> getQuote("ibm" );
print"

\n" ; 
print "Request:\n" . htmlspecialchars ($client -> __getLastRequest()) . "\n" ;
print "Answer:\n" . htmlspecialchars($client -> __getLastResponse()). "\n" ;
print"
" ;
?>

Here is the output of the script. It has been slightly modified to make it easier to understand.

Inquiry:



xmlns:xsd="http://www.w3.org/2001/XMLSchema"

SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">


ibm


Answer:


xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:xmethods-delayed-quotes"
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:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">


98.42


Other SOAP implementations for PHP

They are all written in PHP, not C.

Summary

In this article, I have only described the main features of the SOAP extension. In fact, it can do much more, but it is simply impossible to demonstrate all its capabilities within the framework of one short article. Here is a list of the main ones:

  1. Support for complex data types (arrays, objects)
  2. Support for SOAP - headers
  3. Dynamic support for SOAP 1.1 and SOAP 1.2

Perhaps they will be discussed in more detail in future articles.

Detailed documentation for the SOAP extension is located at http://www.php.net/manual/en/ref.soap.php .

The development of this extension is in its early stages, so your feedback will help make it more stable, reliable, user-friendly and faster. Please report any problems you encounter while using it at http://bugs.php.net/ .

Links

about the author

To create a SOAP web service in PHP, you first need to install and configure PHP, as well as a local HTTP server. In this project, the XAMP package was used, which allows for a quick installation of the Apache HTTP server and PHP.

Separately, you need to make changes to the Apache and PHP configuration files.

In the configuration file of the Apache HTTP server httpd.conf, you need to find the following code snippet:

To run PHP scripts on a local server, you need to store them in the root directory of the Apache server, by default, the htdocs folder in the Apache root folder.

To change the root directory of the site, you need to make changes to the httpd.conf file in the line below:

To enable PHP support, the following lines must be added to the httpd.conf file:


To start the Apache HTTP server, you can use console commands, or the XAMMP control panel, which allows you to manage the state of Apache, as well as configure it.

The view of this utility is shown in Figure 2.

Figure 2. - XAMMP Control Panel utility:


To create a SOAP web service, you need to write PHP documents that implement the client and server sides of the application, as well as a WSDL page to describe the web service.

Let's create a web service that provides a single web service that returns to the client the result of performing an arithmetic operation on two numbers. The input elements are two numbers specified by the client, as well as a character variable that defines the arithmetic operation.

The client side is implemented by two PHP documents. In the form.php file using the element

the form in which the user enters data is described. The form properties have a POST method that is used to extract information from the form and pass it to the PHP file. In this case, the data entered by the user is transferred to afqk code.php. Elements describe text fields for entering data and a button, when pressed, the data is transferred to the code.php file.

The code snippet that implements the form.php file is shown below:


In the code.php file, an instance of the client class is created and the data entered by the user is received and sent in a SOAP message to the server. The code fragment that implements the creation of an instance of the client class is shown below:

This snippet creates a $client instance of the SoapClient class that executes the SOAP message according to the configuration file cacl.wsdl.

In the code.php file, the variables $a, $b, $action are also assigned the values ​​of the members of the $_POST associative array, which stores the data received from form.php.

The code fragment that implements these operations is shown below:

Where getCalcEntry() is the operation described in the WSDL file.

In this web service, the role of the server part is played by the calc-server.php file.

In this file, the getCalcEntry function is declared and called.

This function checks the value of the $action variable, and in accordance with it, performs an operation on the $a and $b variables, writing the result to the $c variable. In this fragment, the addFunction() method provides access to the getCalcentry function to the remote client. The handle() method will process the SOAP request, call the necessary functions, and send the SOAP response. The code snippet that implements this function is shown below:


The description file calc.wsdl plays an important role in the operation of this SOAP web service.

This file defines the basic parameters of SOAP messages. The code snippet responsible for implementing this is given below:


In this fragment, the elements getCalcRequest and getCalcResponse are SOAP request and SOAP response respectively. In nested in The elements define the names of the passed variables and their types. It should be noted that it is possible to pass strings (type string) that will be converted to numbers implicitly, thanks to dynamic typing in PHP. Also an important element of the WSDL file is the description of the web service interface performed by the element

In this fragment, the element describes a specific getCalcEntry service consisting of two actions:

  • - the first action of getCalcRequest is sending a message;
  • - the second action of getCalcResponse is to receive a response.

For successful operation of the web service on the local server, you must:

  • 1) Create a separate directory for the distributed application in the root directory of the Apache server. Put the form.php, code.php, calc-server.php and calc.wsdl files into it;
  • 2) Start Apache using the console or the XAMMP control panel;
  • 3) In the address bar of the browser, enter the address of the web service client;
  • 4) Enter the data in the text fields and click the Calculate button;
  • 5) As a result, a PHP page with the displayed result of the calculations will open in the browser tab.

Figure 3. - The result of the modified web service:


In order to consider the work of SOAP in more detail, we modify the code of the code.php file in such a way that listings of the latest SOAP messages - request and response - will be displayed in the browser along with the result of the calculation.

The code fragment that performs these functions is shown below:


web server programming

This fragment contains standard system class methods:

Returning the text of SOAP messages. When considering the SOAP messages received in this way, the data exchange between the client and the server is clearly visible. In the case of a query, the data is contained in the code snippet below. This fragment contains the body of the SOAP envelope, in which the element is nested A that describes the web service.


It contains nested elements that store the values ​​of the passed variables and their data types. The values ​​of the numbers 5 and 3 are passed to the server, as well as the symbol of the subtraction operation performed on them.

A similar code snippet for the answer is below:


In this code fragment, you can see the value of the subtraction result passed to the client - 2.

Hello!
It so happened that recently I have become involved in the development of web services. But today the topic is not about me, but about how we can write our own XML Web Service based on the SOAP 1.2 protocol.

I hope that after reading the topic, you will be able to:

  • write your own server implementation of a web application;
  • write your own client implementation of a web application;
  • write your own web service description (WSDL);
  • send arrays of the same type of data to the server by the client.

As you might guess, all the magic will be done using PHP and the built-in SoapClient and SoapServer classes. As a rabbit, we will have a service for sending sms messages.

1 Problem statement

1.1 Borders

In the beginning, I propose to deal with the result that we will achieve at the end of the topic. As it was announced above, we will write a service for sending sms messages, and more precisely, we will receive messages from different sources using the SOAP protocol. After that, we will consider in what form they come to the server. The process of queuing messages for their further provider, unfortunately, is beyond the scope of this post for many reasons.

1.2 What data will be changed?

Alright, we've got the limits! The next step that needs to be done is to decide what data we will exchange between the server and the client. On this topic, I propose not to be wiser for a long time and immediately answer the main questions for yourself:

  • What minimum data must be sent to the server in order to send an SMS message to a subscriber?
  • What is the minimum amount of data that must be sent from the server to satisfy the needs of the client?

Something tells me that for this it is necessary to send the following:

  • mobile phone number, and
  • SMS text.

In principle, these two characteristics are enough to send, but it immediately seems to me that an sms with birthday greetings comes to you at 3 o'clock in the morning, or 4! At this moment, I will be very grateful to everyone for not forgetting about me! Therefore, we will also send to the server and

  • the date the SMS message was sent.

The next thing I would like to send to the server is

  • Message type.

This parameter is not mandatory, but it can be very useful to us if we quickly need to tell the boss how many of our clients we have “delighted” with our news, and also draw some beautiful statistics on this matter.

And yet, I forgot something! If we reflect a little more, it is worth noting that the client can send one SMS message to the server at a time, or a certain number of them. In other words, in one data packet there can be from one to infinity of messages.

As a result, we get that in order to send an SMS message, we need the following data:

  • cell phone number,
  • sms text,
  • the time of sending an SMS message to a subscriber,
  • message type.

We answered the first question, now it is necessary to answer the second question. And perhaps I will allow myself to cheat a little. Therefore, from the server we will send only boolean data, the value of which has the following meaning:

  • TRUE - the packet has successfully reached the server, passed authentication and queued for sending to the sms provider
  • FALSE - in all other cases

This concludes the description of the problem statement! And finally, let's get down to the most interesting part - we will figure out what kind of outlandish beast this SOAP is!

2 What is SOAP?

In general, initially I did not plan to write anything about what SOAP is and wanted to limit myself to links to the w3.org site with the necessary specifications, as well as links to Wikipedia. But at the very end I decided to write a short reference about this protocol.

And I'll start my story with the fact that this data exchange protocol belongs to a subset of protocols based on the so-called RPC (Remote Procedure Call) paradigm, the antipode of which is REST (Representational State Transfer, representative state transfer). You can read more about this in Wikipedia, links to articles are at the very end of the topic. From these articles, we need to understand the following: “The RPC approach allows you to use a small amount of network resources with a large number of methods and a complex protocol. With a REST approach, the number of methods and the complexity of the protocol are severely limited, which can lead to a large number of individual resources.” That is, in relation to us, this means that in the case of the RPC approach, the site will always have one input (link) to the service and which procedure to call to process the incoming data we pass along with the data, while with the REST approach on our The site has many inputs (links), each of which accepts and processes only certain data. If someone reading knows how to explain the difference in these approaches even easier, then be sure to write in the comments!

The next thing we need to know about SOAP is that this protocol uses the same XML as a transport, which, on the one hand, is very good, because. our arsenal immediately includes the full power of the stack of technologies based on this markup language, namely XML-Schema - a language for describing the structure of an XML document (thanks to Wikipedia!), which allows automatic validation of data arriving at the server from clients.

And so, now we know that SOAP is the protocol used to implement remote procedure call and it uses XML as a transport! If you read the article on Wikipedia, then from there you can also learn that it can be used over any application layer protocol, and not just paired with HTTP (unfortunately, in this topic we will only consider SOAP over HTTP). And you know what I like most about all this? If there are no guesses, then I'll give you a hint - SOAP!… There are no guesses anyway?… Did you definitely read the article on Wikipedia?… In general, I won't torture you further. Therefore, I will immediately move on to the answer: “SOAP (from the English. Simple Object Access Protocol - a simple protocol access to objects; up to specification 1.2)". The highlight of this line is in italics! I don’t know what conclusions you drew from all this, but I see the following - since this protocol by no means can be called “simple” (and apparently even w3 agrees with this), then since version 1.2 it has ceased to be decrypted at all! And it became known as SOAP, just SOAP and period.

Well, okay, I beg your pardon, brought a little to the side. As I wrote earlier, XML is used as a transport, and the packets that run between the client and the server are called SOAP envelopes. If we consider the generalized structure of the envelope, then it will seem very familiar to you, because resembles the markup of an HTML page. It has a main section - Envelop, which includes sections header And Body, or Fault. IN Body data is transmitted and it is a mandatory section of the envelope, while header is optional. IN header authorization can be transmitted, or any other data that is not directly related to the input data of the web service procedures. Pro Fault there is nothing special to tell, except that it comes to the client from the server in case of any errors.

This is where my overview story about the SOAP protocol ends (we will look at the envelopes themselves and their structure in more detail when our client and server finally learn how to run them into each other) and a new one begins - about a SOAP companion called WSDL(Web Services Description Language). Yes, yes, this is the very thing that scares most of us from the very attempt to take and implement our own API on this protocol. As a result, we usually reinvent our wheel with JSON as a transport. So, what is WSDL? WSDL is a language for describing web services and accessing them, based on the XML (c) Wikipedia language. If from this definition the whole sacred meaning of this technology does not become clear to you, then I will try to describe it in my own words!

The WSDL is designed to allow our clients to communicate normally with the server. To do this, the following information is described in the file with the “*.wsdl” extension:

  • What namespaces were used,
  • What data schemes were used,
  • What types of messages the web service expects from clients,
  • Which data belongs to which web service procedures,
  • What procedures does the web service contain,
  • How should the client call the web service procedures,
  • To which address client calls should be sent.

As you can see, this file is the entire web service. By specifying the address of the WSDL file in the client, we will know everything about any web service! As a result, we don't need to know absolutely anything about where the web service itself is located. It is enough to know the location of its WSDL file! Soon we will find out that SOAP is not as scary as it is painted (c) Russian proverb.

3 Introduction to XML Schema

Now we know a lot about what SOAP is, what is inside it, and we have an overview of what kind of technology stack surrounds it. Since, first of all, SOAP is a method of interaction between a client and a server, and the XML markup language is used as a transport for it, in this section we will understand a little how automatic data validation occurs through XML schemas.

The main task of the schema is to describe the structure of the data that we are going to process. All data in XML schemas is divided into simple(scalar) and complex(structs) types. Simple types include such types as:

  • line,
  • number,
  • boolean,
  • date of.

Something very simple that has no extensions inside. Their antipode is complex complex types. The simplest example of a complex type that comes to everyone's mind is objects. For example, a book. The book consists of properties: author, title, price, ISBN number etc. And these properties, in turn, can be both simple types and complex ones. And the task of the XML schema is to describe it.

I propose not to go far and write an XML schema for our sms message! Below is the xml description of the sms message:

71239876543 Test message 2013-07-20T12:00:00 12

Our complex type schema will look like this:

This entry reads as follows: we have a variable " message» type « message" and there is a complex type named " message", which consists of a sequential set of elements " phone» type string, « text» type string, « date» type dateTime, « type» type decimal. These types are simple and are already defined in the schema definition. Congratulations! We have just written our first XML Schema!

I think that the meaning of the elements " element" And " complexType» everything became more or less clear to you, so we will not focus on them anymore and switch immediately to the composer element « sequence". When we use the compositor element " sequence» we inform you that the elements included in it must always be in the sequence specified in the scheme, and also all of them are mandatory. But do not despair! There are two more composer elements in XML Schemas: choice" And " all". Composer choice" indicates that there should be one of the elements listed in it, and the composer " all» – any combination of the listed elements.

As you remember, in the first section of the topic, we agreed that the package can be transmitted from one to infinity of sms messages. Therefore, I propose to understand how such data is declared in the XML schema. The general package structure might look like this:

71239876543 Test message 1 2013-07-20T12:00:00 12 71239876543 Test message N 2013-07-20T12:00:00 12

The schema for such a complex type would look like this:

The first block contains the familiar declaration of the complex type “ message". If you notice, then in each simple type included in " message”, new qualifying attributes have been added “ minOccurs" And " maxOccurs". As it is not difficult to guess from the name, the first ( minOccurs) indicates that the given sequence must contain at least one element of the type " phone», « text», « date" And " type”, while the next ( maxOccurs) attribute declares to us that there is at most one such element in our sequence. As a result, when we write our schemas for any data, we are given the widest choice in how to configure them!

The second block of the schema declares the element " messageList» type « MessageList". It's clear that " MessageList' is a complex type that includes at least one element ' message”, but the maximum number of such elements is not limited!

4 Writing your WSDL

Do you remember that WSDL is our web service? Hope you remember! As we write it, our little web service will float on it. So I suggest you don't cheat.

In general, in order for everything to work correctly for us, we need to transfer a WSDL file with the correct MIME type to the client. To do this, you need to configure your web server accordingly, namely, set the MIME type for files with the *.wsdl extension to the following line:

Application/wsdl+xml

But in practice, I usually sent the HTTP header via PHP " text/xml»:

Header("Content-Type: text/xml; charset=utf-8");

and everything worked great!

I want to warn you right away, our simple web service will have a rather impressive description, so do not be alarmed, because. most of the text is mandatory water and once written it can be constantly copied from one web service to another!

Since WSDL is XML, then in the very first line you need to write about it directly. The root element of a file must always be named " definitions»:

Usually, WSDL consists of 4-5 main blocks. The very first block is the definition of a web service, or in other words, an entry point.

It says here that we have a service called - " SmsService". In principle, all names in the WSDL file can be changed by you to whatever you wish, because they play absolutely no role.

After that, we declare that in our web service " SmsService" there is an entry point ("port"), which is called " SmsServicePort". It is to this entry point that all requests from clients to the server will be sent. And we specify in the element " address» a link to a handler file that will accept requests.

After we have defined a web service and specified an entry point for it, we need to bind supported procedures to it:

To do this, it lists which operations and in what form y will be called. Those. for the port SmsServicePort» a binding named « SmsServiceBinding", which has the call type " rpc” and HTTP is used as the transfer protocol (transport). Thus, we have indicated here that we will make an RPC call over HTTP. After that, we describe which procedures ( operation) are supported in the web service. We will support only one procedure - " sendSms". Through this procedure, our wonderful messages will be sent to the server! After the procedure has been declared, it is necessary to indicate in what form the data will be transmitted. In this case, it is specified that standard SOAP envelopes will be used.

After that, we need to bind the procedure to the messages:

To do this, we specify that our binding ("binding") is of type " SmsServicePortType" and in the element " portType» with the same type name, specify the binding of procedures to messages. And so, the incoming message (from the client to the server) will be called " sendSmsRequest", and outgoing (from the server to the client)" sendSmsResponse". Like all names in the WSDL, the names of incoming and outgoing messages are arbitrary.

Now we need to describe the messages themselves, i.e. incoming and outgoing:

To do this, we add the elements " message» with names « sendSmsRequest" And " sendSmsResponse" respectively. In them, we indicate that an envelope should come to the input, the structure of which corresponds to the data type " Request". After that, an envelope containing the data type is returned from the server - " Response».

Now we need to do just a little - add a description of these types to our WSDL file! And how do you think the WSDL describes the incoming and outgoing data? I think that you have understood everything for a long time and said to yourself that with the help of XML schemas! And you will be absolutely right!

You can congratulate us! Our first WSDL has been written! And we are one step closer to achieving our goal.
Next, we will deal with what PHP provides us with for developing our own distributed applications.

5 Our first SOAP server

Earlier, I wrote that to create a SOAP server in PHP, we will use the built-in SoapServer class. In order for all further actions to occur the same way as mine, you will need to tweak your PHP a little. To be even more precise, you need to make sure that you have the "php-soap" extension installed. How to put it on your web server is best read on the official PHP website (see references).

After everything has been installed and configured, we will need to create the file “ smsservice.php» with the following content:

setClass("SoapSmsGateWay"); //Start the server $server->handle();

What is above the line with the “ini_set” function, I hope, does not need to be explained. Because it defines which HTTP headers we will send from the server to the client and configures the environment. In the "ini_set" line, we disable caching of the WSDL file so that our changes to it immediately take effect on the client.

Now we come to the server! As you can see, the entire SOAP server is only three lines long! In the first line, we create a new instance of the SoapServer object and pass the address of our WSDL web service description to its constructor. Now we know that it will be located in the hosting root in a file with a telling name " smsservice.wsdl.php". In the second line, we tell the SOAP server which class to pull in order to process the envelope received from the client and return the envelope with the response. As you might have guessed, it is in this class that our only method will be described. sendSms. In the third line we start the server! Everything, our server is ready! With which I congratulate all of us!

Now we need to create a WSDL file. To do this, you can either simply copy its contents from the previous section, or take liberties and “template” it a bit:

"; ?> /" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http:// schemas.xmlsoap.org/wsdl/http/" name="SmsWsdl" xmlns="http://schemas.xmlsoap.org/wsdl/"> /"> /smsservice.php" />

At this stage, the resulting server should suit us completely, because. we can log the envelopes coming to it and then calmly analyze the incoming data. In order for us to receive anything on the server, we need a client. So let's get on with them!

6 SOAP client on the way

First of all, we need to create a file in which we will write the client. As usual, we will create it at the root of the host and call it " client.php”, and inside we write the following:

messageList = new MessageList(); $req->messageList->message = new Message(); $req->messageList->message->phone = "79871234567"; $req->messageList->message->text = "Test message 1"; $req->messageList->message->date = "2013-07-21T15:00:00.26"; $req->messageList->message->type = 15; $client = new SoapClient("http://($_SERVER["HTTP_HOST"])/smsservice.wsdl.php", array("soap_version" => SOAP_1_2)); var_dump($client->sendSms($req));

Let's describe our objects. When we wrote the WSDL, three entities were described in it for the envelope entering the server: Request, MessageList And message. Accordingly, the classes Request, MessageList And message are reflections of these entities in our PHP script.

After we have defined the objects, we need to create an object ( $req), which will be sent to the server. Then come the two most cherished lines for us! Our SOAP client! Believe it or not, but this is enough for our server to start pouring messages from the client, as well as for our server to successfully receive and process them! In the first of them, we create an instance of the SoapClient class and pass the address of the location of the WSDL file to its constructor, and explicitly indicate in the parameters that we will work using the SOAP protocol version 1.2. On the next line we call the method sendSms object $client and immediately display the result in the browser.
Let's run it and see what we finally got!

I received the following object from the server:

Object(stdClass) public "status" => boolean true

And this is wonderful, because. now we know for sure that our server is working and not just working, but also can return some values ​​to the client!

Now let's look at the log that we prudently keep on the server side! In the first part of it, we see the raw data that entered the server:

79871234567 Test message 1 2013-07-21T15:00:00.26 15

This is the envelope. Now you know what it looks like! But we are unlikely to be interested in constantly admiring it, so let's deserialize the object from the log file and see if everything is fine with us:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1 " (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public "type" => string "15" (length=2)

As you can see, the object was deserialized correctly, with which I would like to congratulate all of us! Next, something more interesting awaits us! Namely, we will send by the client to the server not one sms-message, but a whole pack (to be more precise, three whole)!

7 Sending Complex Objects

Let's think about how we can send a whole bunch of messages to the server in one package? Probably the easiest way would be to organize an array inside the messageList element! Let's do it:

// create an object to send to the server $req = new Request(); $req->messageList = new MessageList(); $msg1 = new Message(); $msg1->phone = "79871234567"; $msg1->text = "Test message 1"; $msg1->date = "2013-07-21T15:00:00.26"; $msg1->type = 15; $msg2 = new Message(); $msg2->phone = "79871234567"; $msg2->text = "Test message 2"; $msg2->date = "2014-08-22T16:01:10"; $msg2->type = 16; $msg3 = new Message(); $msg3->phone = "79871234567"; $msg3->text = "Test message 3"; $msg3->date = "2014-08-22T16:01:10"; $msg3->type = 17; $req->messageList->message = $msg1; $req->messageList->message = $msg2; $req->messageList->message = $msg3;

Our logs show that the following packet came from the client:

79871234567 Test message 1 2013-07-21T15:00:00.26 15 79871234567 Test message 2 2014-08-22T16:01:10 16 79871234567 Test message 3 2014-08-22T16:01:10 17

What nonsense, you say? And you will be right in a sense, because. just as we learned that which object left the client, it came to our server in the form of an envelope in exactly the same form. True, sms messages were not serialized in XML in the way we needed - they had to be wrapped in elements message, not in Struct. Now let's see in what form such an object comes to the method sendSms:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "Struct" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public " type" => string "15" (length=2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length= 37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone " => string "79871234567" (length=11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length= 19) public "type" => string "17" (length=2)

What does this knowledge give us? Only that the path we have chosen is not correct and we have not received an answer to the question - “How can we get the correct data structure on the server?”. But I suggest not to despair and try to cast our array to the type an object:

$req->messageList->message = (object)$req->messageList->message;

In this case, we will receive another envelope:

79871234567 Test message 1 2013-07-21T15:00:00.26 15 79871234567 Test message 2 2014-08-22T16:01:10 16 79871234567 Test message 3 2014-08-22T16:01:10 17

Came into the method sendSms the object has the following structure:

Object(stdClass) public "messageList" => object(stdClass) public "message" => object(stdClass) public "BOGUS" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public " type" => string "15" (length=2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length= 37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone " => string "79871234567" (length=11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length= 19) public "type" => string "17" (length=2)

As for me, then “from a change in the places of the terms, the sum does not change” (c). What BOGUS, what Struct We haven't reached our goal yet! And to achieve it, we need to make sure that instead of these incomprehensible names, our native message. But how to achieve this, the author does not yet know. Therefore, the only thing we can do is get rid of the extra container. In other words, we will now make sure that instead of message became BOGUS! To do this, change the object as follows:

// create an object to send to the server $req = new Request(); $msg1 = new Message(); $msg1->phone = "79871234567"; $msg1->text = "Test message 1"; $msg1->date = "2013-07-21T15:00:00.26"; $msg1->type = 15; $msg2 = new Message(); $msg2->phone = "79871234567"; $msg2->text = "Test message 2"; $msg2->date = "2014-08-22T16:01:10"; $msg2->type = 16; $msg3 = new Message(); $msg3->phone = "79871234567"; $msg3->text = "Test message 3"; $msg3->date = "2014-08-22T16:01:10"; $msg3->type = 17; $req->messageList = $msg1; $req->messageList = $msg2; $req->messageList = $msg3; $req->messageList = (object)$req->messageList;

What if we get lucky and the correct name comes up from the scheme? To do this, let's look at the envelope that arrived:

79871234567 Test message 1 2013-07-21T15:00:00.26 15 79871234567 Test message 2 2014-08-22T16:01:10 16 79871234567 Test message 3 2014-08-22T16:01:10 17

Yes, the miracle did not happen! BOGUS- we won't win! Came in sendSms the object in this case will look like this:

Object(stdClass) public "messageList" => object(stdClass) public "BOGUS" => array (size=3) 0 => object(stdClass) public "phone" => string "79871234567" (length=11) public " text" => string "Test message 1" (length=37) public "date" => string "2013-07-21T15:00:00.26" (length=22) public "type" => string "15" (length =2) 1 => object(stdClass) public "phone" => string "79871234567" (length=11) public "text" => string "Test message 2" (length=37) public "date" => string " 2014-08-22T16:01:10" (length=19) public "type" => string "16" (length=2) 2 => object(stdClass) public "phone" => string "79871234567" (length= 11) public "text" => string "Test message 3" (length=37) public "date" => string "2014-08-22T16:01:10" (length=19) public "type" => string " 17" (length=2)

As they say - "Almost"! On this (slightly sad) note, I propose to quietly round off and draw some conclusions for ourselves.

8 Conclusion

Finally we got here! Let's decide what you can do now:

  • you can write the necessary WSDL file for your web service;
  • you can write your own client without any problems that can communicate with the server using the SOAP protocol;
  • you can write your own server that communicates with the outside world via SOAP;
  • you can send arrays of the same type of objects to the server from your client (with some restrictions).

Also, we made some discoveries for ourselves during our little research:

  • the native class SoapClient does not know how to correctly serialize data structures of the same type in XML;
  • when serializing an array to XML, it creates an extra element named Struct;
  • when serializing an object to XML, it creates an extra element named BOGUS;
  • BOGUS lesser evil than Struct due to the fact that the envelope is more compact (no extra namespaces are added to the XML header of the envelope);
  • unfortunately, the SoapServer class doesn't automatically validate the envelope data with our XML schema (maybe other servers don't either).

Views