Hello!
It so happened that recently I became involved in the development of web services. But today the topic is not about me, but about how we write our XML Web Service based on the SOAP 1.2 protocol.
I hope that after reading the topic you can do it yourself:
- write your own server implementation of a web application;
- write your own client implementation of the web application;
- write your own web service description (WSDL);
- send the client arrays of the same type to the server.
As you might guess, all the magic will be created using PHP and the built-in classes SoapClient and SoapServer. As a rabbit, we will have a service for sending sms-messages.
1 Task setting
1.1 Borders
In the beginning, I propose to deal with the result that we achieve at the end of the topic. As announced above, we will write a service for sending sms-messages, and if more precisely, then we will receive messages from different sources via the SOAP protocol. After that, we consider in what form they come to the server. The very process of queuing messages for their further sending to the provider, unfortunately, is beyond the scope of this post for many reasons.

')
1.2 What data will we change?
Great, we have decided on the borders! 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 for a long time not to be wise and immediately answer the main questions for myself:
- What is the minimum data to be sent to the server to send an SMS message to the subscriber?
- What is the minimum data to send from the server to meet the needs of the client?
Something tells me that for this you need to send the following:
- mobile phone number as well
- text sms messages.
In principle, these two characteristics are enough to send, but I immediately get a chance, as an SMS with a birthday greeting message comes to you at 3 am, or 4! At this moment I will be very grateful to everyone for not having forgotten about me! Therefore, we will also send to the server and
The next thing I want to send to the server is
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 βpleasedβ with our news, as well as draw some nice statistics on this matter.
And yet, I forgot something! If you reflect a little more, it is worth noting that the client can send one sms-message or some of them to the server at once. 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 sms-messages we need the following data:
- Telephone number,
- SMS text,
- time of sending sms-message to the subscriber,
- message type

We answered the first question, now it is necessary to answer the second question. And perhaps, I will allow myself a little to bum. Therefore, from the server we will send only boolean data, the value of which has the following meaning:
- TRUE - the package successfully reached the server, passed authentication and was queued for sending to the sms provider
- FALSE - in all other cases

With this we have finished the description of the problem statement! And finally, let's get down to the most interesting - we will understand what a strange beast this SOAP is!
2 What is SOAP?
In general, I initially 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 book about this protocol.
And I will begin my narration with the fact that this data exchange protocol refers to a subset of protocols based on the so-called RPC paradigm (Remote Procedure Call, remote procedure call) which is opposed to REST (Representational State Transfer, transfer of a representative state). For more information about this can be found 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 the use of a small number of network resources with a large number of methods and a complex protocol. With the REST approach, the number of methods and the complexity of the protocol are strictly limited, due to which the number of individual resources can be large. β That is, in relation to us, this means that on the site in the case of the RPC approach there will always be one input (link) to the service and what procedure to call to process incoming data we transfer 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 from the reader knows how even easier to explain the difference in these approaches, be sure to write in the comments!
The next thing we need to learn about SOAP is that this protocol uses the same XML as a transport, which on the one hand is very good, because Immediately, our arsenal includes all the power of a 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 for automatic validation of data received by the server from clients.
And so, now we know that SOAP is a protocol used to implement remote procedure calls and uses XML as a transport! If you read the article on Wikipedia, then from there you can also learn that it can be used on top of 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 best about all this? If there are no guesses, then I will give a hint - SOAP! ... Vseravno no guesses? ... You just read the article on Wikipedia? ... In general, I will not torment you further. Therefore, I will immediately turn to the answer: "SOAP (from the English. Simple Object Access Protocol is a simple
protocol for accessing objects;
up to specification 1.2 )." The most remarkable in this line is italicized! I donβt know what conclusions you made from all this, but I can see the following - since this protocol cannot be called βsimpleβ (and apparently even agree with w3), then it has stopped somehow deciphering from version 1.2! And it became known as SOAP, just SOAP and the point.
Well, okay, please excuse me, skidded a little to the side. As I wrote earlier, XML is used as a transport, and the packages that run between the client and the server are called SOAP envelopes. If we consider the generalized structure of the envelope, then you will find it very familiar, because resembles the structure of an HTML page. It has a main section -
Envelop , which includes sections
Header and
Body , or
Fault . Data is transferred to the
Body and it is a required section of the envelope, while the
Header is optional. In the
Header , authorization can be transferred, or any other data that does not directly relate to the input data of the web service procedures. There is nothing special to tell about
Fault , 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'll take a closer look at the envelopes themselves and their structure when our client and server finally learn how to run them into each other) and a new one begins - about the SOAP companion called
WSDL (Web Services Description Language) . Yes, yes, this is the very thing that scares most of us from trying to take and implement our API on this protocol. As a result, we usually invent our bike with JSON as a transport. So what is WSDL? WSDL is a web services description and access language based on XML (c) Wikipedia. If this definition does not make you understand the whole sacred meaning of this technology, then I will try to describe it in my own words!
WSDL is designed to enable our clients to communicate normally with the server. For this, the following information is described in the file with the extension "* .wsdl":
- What namespaces were used
- What data schemes were used?
- What types of messages does the web service expect from clients,
- What data belongs to which web service procedures,
- What procedures does a web service contain?
- How should the client call the web service procedures,
- To what address should the customer calls be sent.
As you can see, this file is the whole 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 nothing about where the web service is located. Enough to know the address of the location of its WSDL file! Soon we will learn that SOAP is not as terrible as it is painted (c) by Russian proverbs.
3 Introduction to XML Schema
Now we know a lot about what SOAP is, what is inside it and we have a general overview of what technology stack surrounds it. Since, first of all, SOAP is a method of interaction between a client and a server, and XML markup language is used as a transport for it, in this section we will understand a little about how automatic data validation takes place using XML schemas.
The main task of the scheme 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 (structure) types. Simple types include such types as:
- line,
- number,
- boolean value
- date.
Something very simple, which has no extensions inside. Their antithesis is complex complex types. The simplest example of a complex type that comes to everyoneβs head 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 suggest not to go far and write an XML schema for our sms-message! Below is the xml description of the sms message:
<message> <phone>71239876543</phone> <text> </text> <date>2013-07-20T12:00:00</date> <type>12</type> </message>
The scheme of our complex type will be as follows:
<element name="message" type="Message" /> <complexType name="Message"> <sequence> <element name="phone" type="string" /> <element name="text" type="string" /> <element name="date" type="dateTime" /> <element name="type" type="decimal" /> </sequence> </complexType>
This record is read as follows: we have a
message variable of the
Message type and there is a complex type named
Message , which consists of a sequential set of
phone elements of the
string type,
text of the
string type,
date of the
dateTime type , "
Type " of type
decimal . These types are simple and are already defined in the schema description. Congratulations! We just wrote our first XML schema!
I think that the meaning of the elements β
element β and β
complexType β has become more or less clear to you, therefore we will not focus on them anymore and switch immediately to the element-composer β
sequence β. When we use the β
sequence β element-composer, we inform that the elements included in it must always be located in the sequence indicated in the diagram, and all of them are mandatory. But do not despair! In XML schemas, there are two more composer elements: β
choice β and β
all β. The β
choice β composer announces that there must be one of the elements listed in it, and the β
all β composer any combination of the elements listed.
As you remember, in the first section of the topic we agreed that the package can be transmitted from one to infinity sms-messages. Therefore, I propose to understand how such data is declared in the XML schema. The overall package structure may look like this:
<messageList> <message> <phone>71239876543</phone> <text> 1</text> <date>2013-07-20T12:00:00</date> <type>12</type> </message> <message> <phone>71239876543</phone> <text> N</text> <date>2013-07-20T12:00:00</date> <type>12</type> </message> </messageList>
The scheme for this complex type will look like this:
<complexType name="Message"> <sequence> <element name="phone" type="string" minOccurs="1" maxOccurs="1" /> <element name="text" type="string" minOccurs="1" maxOccurs="1" /> <element name="date" type="dateTime" minOccurs="1" maxOccurs="1" /> <element name="type" type="decimal" minOccurs="1" maxOccurs="1" /> </sequence> </complexType> <element name="messageList" type="MessageList" /> <complexType name="MessageList"> <sequence> <element minOccurs="1" maxOccurs="unbounded" name="message" type="Message"/> </sequence> </complexType>
In the first block is a familiar declaration of the complex type β
Message β. If you have noticed, in each simple type included in the β
Message β, new specifying attributes β
minOccurs β and β
maxOccurs β have been added. As it is not difficult to guess from the name, the first (
minOccurs ) reports that in this sequence there must be at least one element of the type β
phone β, β
text β, β
date β and β
type β, while the next one (
maxOccurs ) the attribute declares to us that there are maximum of such elements in our sequence one by one. As a result, when we write our schemes for any data, we are given the widest choice in how to set them up!
The second block of the scheme declares an element β
messageList β of the type β
MessageList β. It can be seen that
MessageList is a complex type that includes at least one
message element, but the maximum number of such elements is unlimited!
At this point, we will assume that the LikBez scheme has been completed and then another equally exciting adventure awaits us - we will write our own WSDL!
4 Writing our WSDL
Do you remember that WSDL is our web service? Hope to remember! As we write it, our small web service will float on it. Therefore, I suggest not to tinker.
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 for files with the extension "* .wsdl" MIME type equal to the following line:
application/wsdl+xml
But in practice, I usually sent the HTTP "
text / xml " header via PHP:
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 donβt be intimidated because Most of the text is mandatory water and once you write it you can constantly copy from one web service to another!
Since WSDL is XML, in the very first line it is necessary to write about it directly. The root element of the file should always be called β
definitions β:
<?xml version="1.0" encoding="utf-8"?> <definitions> </definitions>
Usually, WSDL consists of 4-5 basic blocks. The very first block - the definition of a web service or in other words - the entry point.
<?xml version="1.0" encoding="utf-8"?> <definitions> <!β --> <service name="SmsService"> <port name="SmsServicePort" binding="tns:SmsServiceBinding"> <soap:address location="http://localhost:80/smsservice.php" /> </port> </service> </definitions>
It says here that we have a service called β
SmsService β. In principle, all the names in the WSDL file can be changed by you to whatever you want, because they play absolutely no role.
After that, we announce that there is an entry point (βportβ) in our web service β
SmsService β, which is called β
SmsServicePort β. All requests from clients to the server will be sent to this entry point. And we indicate in the
address element a link to the handler file that will receive requests.
After we have defined a web service and specified an entry point for it - you need to attach supported procedures to it:
<?xml version="1.0" encoding="utf-8"?> <definitions> <!β - --> <binding name="SmsServiceBinding" type="tns:SmsServicePortType"> <soap:binding style=βrpcβ transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="sendSms"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <!β --> <service name="SmsService"> <port name="SmsServicePort" binding="tns:SmsServiceBinding"> <soap:address location="http://localhost:80/smsservice.php" /> </port> </service> </definitions>
To do this, which operations are listed and in what form they will be called. Those. A binding has been defined for the β
SmsServicePort β port under the name β
SmsServiceBinding β, which has the β
rpc β call type and uses HTTP as the transfer protocol (transport). Thus, we have indicated here that we will make an RPC call over HTTP. After that we describe what 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 indicated that standard SOAP envelopes will be used.
After that we need to bind the procedure to the messages:
<?xml version="1.0" encoding="utf-8"?> <definitions> <!β --> <portType name="SmsServicePortType"> <operation name="sendSms"> <input message="tns:sendSmsRequest" /> <output message="tns:sendSmsResponse" /> </operation> </portType> <!β - --> <binding name="SmsServiceBinding" type="tns:SmsServicePortType"> <soap:binding style=βrpcβ transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="sendSms"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <!β --> <service name="SmsService"> <port name="SmsServicePort" binding="tns:SmsServiceBinding"> <soap:address location="http://localhost:80/smsservice.php" /> </port> </service> </definitions>
To do this, we indicate that our binding (βbindingβ) is of the type β
SmsServicePortType β and in the β
portType β element with the same type of name we indicate the binding of procedures to messages. And so, the incoming message (from client to server) will be called β
sendSmsRequest β, and the outgoing (from server to client) β
sendSmsResponse β. Like all WSDL names, the names of incoming and outgoing messages are arbitrary.
Now we need to describe the messages themselves, i.e. incoming and outgoing:
<?xml version="1.0" encoding="utf-8"?> <definitions> <message name="sendSmsRequest"> <part name="Request" element="tns:Request" /> </message> <message name="sendSmsResponse"> <part name="Response" element="tns:Response" /> </message> <portType name="SmsServicePortType"> <operation name="sendSms"> <input message="tns:sendSmsRequest" /> <output message="tns:sendSmsResponse" /> </operation> </portType> <binding name="SmsServiceBinding" type="tns:SmsServicePortType"> <soap:binding style=βrpcβ transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="sendSms"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <service name="SmsService"> <port name="SmsServicePort" binding="tns:SmsServiceBinding"> <soap:address location="http://localhost:80/smsservice.php" /> </port> </service> </definitions>
To do this, we add β
message β elements with the names β
sendSmsRequest β and β
sendSmsResponse β, respectively. In them we indicate that the envelope should come in, the structure of which corresponds to the data type β
Request β. After that, an envelope containing the data type β
Response β is returned from the server.
Now we need to do a little - add a description of these types to our WSDL file! And what do you think, how are incoming and outgoing data described in WSDL? I think that you have already understood everything for a long time and said to yourself that with the help of XML schemas! And you will be absolutely right!
<?xml version="1.0" encoding="utf-8"?> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://localhost/" 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/"> <types> <xs:schema xmlns:tns="http://schemas.xmlsoap.org/wsdl/" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://localhost/"> <complexType name="Message"> <sequence> <element name="phone" type="string" minOccurs="1" maxOccurs="1" /> <element name="text" type="string" minOccurs="1" maxOccurs="1" /> <element name="date" type="dateTime" minOccurs="1" maxOccurs="1" /> <element name="type" type="decimal" minOccurs="1" maxOccurs="1" /> </sequence> </complexType> <complexType name="MessageList"> <sequence> <element minOccurs="1" maxOccurs="unbounded" name="message" type="Message"/> </sequence> </complexType> <element name="Request"> <element name="messageList" type="MessageList" /> </element> <element name="Response"> <complexType> <sequence> <element name="status" type="boolean" /> </sequence> </complexType> </element> </xs:schema> </types> <message name="sendSmsRequest"> <part name="Request" element="tns:Request" /> </message> <message name="sendSmsResponse"> <part name="Response" element="tns:Response" /> </message> <portType name="SmsServicePortType"> <operation name="sendSms"> <input message="tns:sendSmsRequest" /> <output message="tns:sendSmsResponse" /> </operation> </portType> <binding name="SmsServiceBinding" type="tns:SmsServicePortType"> <soap:binding style=βrpcβ transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="sendSms"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <service name="SmsService"> <port name="SmsServicePort" binding="tns:SmsServiceBinding"> <soap:address location="http://localhost:80/smsservice.php" /> </port> </service> </definitions>
You can congratulate us! Our first WSDL was written! And we are one step closer to achieving our goal.
Next, we will deal with the fact that we provide PHP to develop its own distributed applications.
5 Our first SOAP server
Earlier, I wrote that we will use the built-in class SoapServer to create a SOAP server in PHP. In order for all further actions to occur as well as for me, you need to tweak your PHP a little. To be even more precise, you need to make sure that you have installed the extension "php-soap". How to put it on your web server is best read on the official PHP site (see list of references).
After everything has been installed and configured, we will need to create in the root folder of your hosting file β
smsservice.php β with the following content:
<?php header("Content-Type: text/xml; charset=utf-8"); header('Cache-Control: no-store, no-cache'); header('Expires: '.date('r')); set_include_path(get_include_path() .PATH_SEPARATOR.'classes' .PATH_SEPARATOR.'objects'); const CONF_NAME = "config.ini"; function __autoload($class_name){ include $class_name.'.class.php'; } ini_set("soap.wsdl_cache_enabled", "0");
What is above the line with the function "ini_set", I hope that it is not necessary to explain. Since there it is determined which HTTP headers we will send from the server to the client and the environment is configured. 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 takes up only three lines! In the first line, we create a new instance of the SoapServer object and pass the address of our WSDL description of the web service to the constructor. Now we know that it will be located in the root of the hosting in the file with the saying 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 answer. As you might have guessed, it is in this class that our only method,
sendSms, will be described. In the third line, we run the server! Everything, our server is ready! With what I congratulate all of us!
Now we need to create a WSDL file. To do this, you can either just copy its contents from the previous section, or take liberties and βtemplateβ it a bit:
<?php header("Content-Type: text/xml; charset=utf-8"); echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>"; ?> <definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://<?=$_SERVER['HTTP_HOST']?>/" 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/"> <types> <xs:schema elementFormDefault="qualified" xmlns:tns="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://<?=$_SERVER['HTTP_HOST']?>/"> <xs:complexType name="Message"> <xs:sequence> <xs:element name="phone" type="xs:string" minOccurs="1" maxOccurs="1" /> <xs:element name="text" type="xs:string" minOccurs="1" maxOccurs="1" /> <xs:element name="date" type="xs:dateTime" minOccurs="1" maxOccurs="1" /> <xs:element name="type" type="xs:decimal" minOccurs="1" maxOccurs="1" /> </xs:sequence> </xs:complexType> <xs:complexType name="MessageList"> <xs:sequence> <xs:element name="message" type="Message" minOccurs="1" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> <xs:element name="Request"> <xs:complexType> <xs:sequence> <xs:element name="messageList" type="MessageList" /> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Response"> <xs:complexType> <xs:sequence> <xs:element name="status" type="xs:boolean" /> </xs:sequence> </xs:complexType> </xs:element> </xs:schema> </types> <!-- sendSms --> <message name="sendSmsRequest"> <part name="Request" element="tns:Request" /> </message> <message name="sendSmsResponse"> <part name="Response" element="tns:Response" /> </message> <!-- --> <portType name="SmsServicePortType"> <operation name="sendSms"> <input message="tns:sendSmsRequest" /> <output message="tns:sendSmsResponse" /> </operation> </portType> <!-- - --> <binding name="SmsServiceBinding" type="tns:SmsServicePortType"> <soap:binding transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="sendSms"> <soap:operation soapAction="" /> <input> <soap:body use="literal" /> </input> <output> <soap:body use="literal" /> </output> </operation> </binding> <!-- --> <service name="SmsService"> <port name="SmsServicePort" binding="tns:SmsServiceBinding"> <soap:address location="http://<?=$_SERVER['HTTP_HOST']?>/smsservice.php" /> </port> </service> </definitions>
At this stage, the resulting server should arrange us completely, because we can log the envelopes sent to it and then calmly analyze the incoming data. In order for us to receive something on the server, we need a client. So let's get them engaged!
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 name it β
client.php β, and inside we will write the following:
<?php header("Content-Type: text/html; charset=utf-8"); header('Cache-Control: no-store, no-cache'); header('Expires: '.date('r')); set_include_path(get_include_path() .PATH_SEPARATOR.'classes' .PATH_SEPARATOR.'objects'); function __autoload($class_name){ include $class_name.'.class.php'; } ini_set('display_errors', 1); error_reporting(E_ALL & ~E_NOTICE);
We describe our objects. When we wrote WSDL in it for the envelope entering the server, three entities were described:
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 we will send to the server. Then come the two most cherished lines for us! Our SOAP client! Believe it or not, this is enough to start sending messages from the client to our server, as well as our server to successfully receive and process them! In the first of these, we create an instance of the SoapClient class and pass to its constructor the address of the location of the WSDL file, and in the parameters we explicitly indicate that we will work using the SOAP protocol version 1.2. In the next line, we call the
$ client object's
sendSms method and immediately display the result in the browser.
Let's run and see what we finally did!
The following object returned to me from the server:
object(stdClass)[5] public 'status' => boolean true
And this is great, 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, which we provide for on the server side! In the first part of it, we see the raw data that arrived at the server:
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://sms-service/"> <env:Body> <ns1:Request> <ns1:messageList> <message> <phone>79871234567</phone> <text> 1</text> <date>2013-07-21T15:00:00.26</date> <type>15</type> </message> </ns1:messageList> </ns1:Request> </env:Body> </env:Envelope>
This is the envelope. Now you know what it looks like! But it is unlikely that it will be interesting to admire him constantly, so let's desseralize an object from a log file and see if we all are well:
object(stdClass)[4] public 'messageList' => object(stdClass)[5] public 'message' => object(stdClass)[6] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 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 is deserialized correctly, with which I want to congratulate all of us! Then we are waiting for something more interesting! Namely - we will send the client to the server not one sms-message, but a whole pack (to be exact, then three as many)!
7 We send complex objects
Let's think about how to send a whole pack of messages to the server in one package? Probably the easiest way is to organize an array inside the messageList element! Let's do it:
Our logs list the following package from the client:
<?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:enc="http://www.w3.org/2003/05/soap-encoding" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://sms-service/"> <env:Body> <ns1:Request> <ns1:messageList> <message> <SOAP-ENC:Struct> <phone>79871234567</phone> <text> 1</text> <date>2013-07-21T15:00:00.26</date> <type>15</type> </SOAP-ENC:Struct> <SOAP-ENC:Struct> <phone>79871234567</phone> <text> 2</text> <date>2014-08-22T16:01:10</date> <type>16</type> </SOAP-ENC:Struct> <SOAP-ENC:Struct> <phone>79871234567</phone> <text> 3</text> <date>2014-08-22T16:01:10</date> <type>17</type> </SOAP-ENC:Struct> </message> </ns1:messageList> </ns1:Request> </env:Body> </env:Envelope>
What nonsense, you say? And you will be right in a sense, because we just learned that what object was gone from the client, then absolutely in the same form it came to us to the server as an envelope. True, sms messages were serialized in XML not in the way we needed - they should have been wrapped in message elements , not in Struct . Now let's see how this object comes to the sendSms method : object(stdClass)[6] public 'messageList' => object(stdClass)[7] public 'message' => object(stdClass)[8] public 'Struct' => array (size=3) 0 => object(stdClass)[9] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 1' (length=37) public 'date' => string '2013-07-21T15:00:00.26' (length=22) public 'type' => string '15' (length=2) 1 => object(stdClass)[10] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 2' (length=37) public 'date' => string '2014-08-22T16:01:10' (length=19) public 'type' => string '16' (length=2) 2 => object(stdClass)[11] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 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 an object type : $req->messageList->message = (object)$req->messageList->message;
In this case, another envelope will come to us: <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://sms-service/"> <env:Body> <ns1:Request> <ns1:messageList> <message> <BOGUS> <phone>79871234567</phone> <text> 1</text> <date>2013-07-21T15:00:00.26</date> <type>15</type> </BOGUS> <BOGUS> <phone>79871234567</phone> <text> 2</text> <date>2014-08-22T16:01:10</date> <type>16</type> </BOGUS> <BOGUS> <phone>79871234567</phone> <text> 3</text> <date>2014-08-22T16:01:10</date> <type>17</type> </BOGUS> </message> </ns1:messageList> </ns1:Request> </env:Body> </env:Envelope>
The object that came to the sendSms method has the following structure: object(stdClass)[7] public 'messageList' => object(stdClass)[8] public 'message' => object(stdClass)[9] public 'BOGUS' => array (size=3) 0 => object(stdClass)[10] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 1' (length=37) public 'date' => string '2013-07-21T15:00:00.26' (length=22) public 'type' => string '15' (length=2) 1 => object(stdClass)[11] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 2' (length=37) public 'date' => string '2014-08-22T16:01:10' (length=19) public 'type' => string '16' (length=2) 2 => object(stdClass)[12] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 3' (length=37) public 'date' => string '2014-08-22T16:01:10' (length=19) public 'type' => string '17' (length=2)
As for me, βfrom changing places of the terms - the amount does not changeβ (c). That BOGUS , that Struct - the goal we have not yet achieved! And to achieve it, we need to make sure that instead of these incomprehensible names, our native message is displayed . But how to achieve this, the author is not yet known. Therefore, the only thing we can do is to get rid of the excess container. In other words, we will now make it so that instead of the message , BOGUS becomes ! To do this, change the object as follows:
Suddenly we are lucky and the correct name is pulled from the scheme? To do this, look at the incoming envelope: <?xml version="1.0" encoding="UTF-8"?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://sms-service/"> <env:Body> <ns1:Request> <ns1:messageList> <BOGUS> <phone>79871234567</phone> <text> 1</text> <date>2013-07-21T15:00:00.26</date> <type>15</type> </BOGUS> <BOGUS> <phone>79871234567</phone> <text> 2</text> <date>2014-08-22T16:01:10</date> <type>16</type> </BOGUS> <BOGUS> <phone>79871234567</phone> <text> 3</text> <date>2014-08-22T16:01:10</date> <type>17</type> </BOGUS> </ns1:messageList> </ns1:Request> </env:Body> </env:Envelope>
Yes, the miracle did not happen! BOGUS - do not win! The object that came to sendSms will look like this: object(stdClass)[6] public 'messageList' => object(stdClass)[7] public 'BOGUS' => array (size=3) 0 => object(stdClass)[8] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 1' (length=37) public 'date' => string '2013-07-21T15:00:00.26' (length=22) public 'type' => string '15' (length=2) 1 => object(stdClass)[9] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 2' (length=37) public 'date' => string '2014-08-22T16:01:10' (length=19) public 'type' => string '16' (length=2) 2 => object(stdClass)[10] public 'phone' => string '79871234567' (length=11) public 'text' => string ' 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 suggest quietly rounding out and draw some conclusions for yourself.8 Conclusion
We finally got here! Let's define what you can do now:- you can write the necessary WSDL file for your web service;
- without any problems, you can write your own client capable of communicating with the server via the SOAP protocol;
- You can write your own server communicating with the outside world on SOAP;
- You can send arrays of similar objects to the server from your client (with some restrictions).
Also, we made some discoveries for ourselves in the course of our little research:- The native class SoapClient cannot correctly serialize data structures of the same type into XML;
- when serializing an array in XML, it creates an extra element called Struct ;
- when you serialize an object in XML, it creates an extra element called BOGUS ;
- BOGUS is a lesser evil than Struct due to the fact that the envelope is more compact (no extra namespaces are added in the XML envelope header);
- Unfortunately, the SoapServer class does not automatically validate the envelope data with our XML schema (perhaps other servers do not do this).
9 References
PS SOAP , SoapServer SoapClient. , SOAP PHP, , :)
PPS Mikaz
ArrayObject SoapVar .
:
$req = new Request(); $req->messageList = new \ArrayObject(); $msg1 = new Message(); $msg1->phone = '79871234567'; $msg1->text = ' 1'; $msg1->date = '2013-07-21T15:00:00.26'; $msg1->type = 15; $soap_msg1 = new \SoapVar($msg1, SOAP_ENC_OBJECT, null, null, 'Message'); $msg2 = new Message(); $msg2->phone = '79871234567'; $msg2->text = ' 2'; $msg2->date = '2014-08-22T16:01:10'; $msg2->type = 16; $soap_msg2 = new \SoapVar($msg2, SOAP_ENC_OBJECT, null, null, 'Message'); $msg3 = new Message(); $msg3->phone = '79871234567'; $msg3->text = ' 3'; $msg3->date = '2014-08-22T16:01:10'; $msg3->type = 17; $soap_msg3 = new \SoapVar($msg3, SOAP_ENC_OBJECT, null, null, 'Message'); $req->messageList->append($soap_msg1); $req->messageList->append($soap_msg2); $req->messageList->append($soap_msg3);