πŸ“œ ⬆️ ⬇️

Writing SOAP client-server application in PHP

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:

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:


Something tells me that for this you need to send the following:


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:




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:




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":

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:

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> <!--   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 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> <!--   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 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 /** * smsservice.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"); //   WSDL-   //  SOAP- $server = new SoapServer("http://{$_SERVER['HTTP_HOST']}/smsservice.wsdl.php"); //   $server->setClass("SoapSmsGateWay"); //  $server->handle(); 

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 /** * smsservice.wsdl.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 /** * /client.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); //   class Message{ public $phone; public $text; public $date; public $type; } class MessageList{ public $message; } class Request{ public $messageList; } //       $req = new Request(); $req->messageList = new MessageList(); $req->messageList->message = new Message(); $req->messageList->message->phone = '79871234567'; $req->messageList->message->text = '  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)); 

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:

 //       $req = new Request(); $req->messageList = new MessageList(); $msg1 = new Message(); $msg1->phone = '79871234567'; $msg1->text = '  1'; $msg1->date = '2013-07-21T15:00:00.26'; $msg1->type = 15; $msg2 = new Message(); $msg2->phone = '79871234567'; $msg2->text = '  2'; $msg2->date = '2014-08-22T16:01:10'; $msg2->type = 16; $msg3 = new Message(); $msg3->phone = '79871234567'; $msg3->text = '  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 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:

 //       $req = new Request(); $msg1 = new Message(); $msg1->phone = '79871234567'; $msg1->text = '  1'; $msg1->date = '2013-07-21T15:00:00.26'; $msg1->type = 15; $msg2 = new Message(); $msg2->phone = '79871234567'; $msg2->text = '  2'; $msg2->date = '2014-08-22T16:01:10'; $msg2->type = 16; $msg3 = new Message(); $msg3->phone = '79871234567'; $msg3->text = '  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; 

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:

Also, we made some discoveries for ourselves in the course of our little research:


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); 

Source: https://habr.com/ru/post/187390/


All Articles