We write SOAP client-server application in PHP

Hello!
It so happened that recently I began to develop web services. But today the topic is not about me, but about how we can write our XML Web Service based on the SOAP 1.2 protocol.

I hope that after reading the topic you can yourself:
  • write your own server implementation of the web application;
  • write your own client implementation of a web application;
  • Write your own web service description (WSDL)
  • send by the client arrays of the same type of data to the server.

As you might have guessed, all the magic will be done using PHP and the SoapClient and SoapServer built-in 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 announced above, we will write a service for sending SMS messages, and more precisely, we will receive messages from different sources via the SOAP protocol. After which, we will consider in what form they come to the server. The process of queuing messages for further sending to the provider, unfortunately, is beyond the scope of this post for many reasons.


1.2 What data will be changed?


Well, 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 subject, I propose not to be smart for a long time and immediately answer the main questions for myself:

  • What minimum data should be sent to the server in order to send an SMS message to the subscriber?
  • What minimum data should be sent from the server to satisfy the client's needs?

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

  • mobile number as well
  • SMS message text.

In principle, these two characteristics are enough for sending, but I immediately think of the case when an SMS with a congratulation on your birthday arrives at 3 in the morning, or 4! At this moment, I will be very grateful to everyone for not being forgotten about me! Therefore, we will also send to the server and

  • The date the SMS was sent.

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

  • Type of message.

This parameter is not required, but it can be very useful for us if you quickly need to tell the boss about how many of our customers we “pleased” with our news, and also draw some nice statistics on this.

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

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

  • Telephone number,
  • SMS message text
  • time of sending an SMS message to a subscriber,
  • type of message.



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

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



On this we have finished the description of the problem statement! And finally, let's get down to the most interesting part - we’ll figure out what a strange beast this SOAP is!

2 What is SOAP with?


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

And I will begin 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 clarify 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 the REST approach, the number of methods and the complexity of the protocol are strictly limited, which is why the number of individual resources can be large. " That is, as applied 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 which procedure we will call to process the incoming data we transmit along with the data, while with the REST approach on our site there are many inputs (links), each of which accepts and processes only certain data. If one of the readers knows how even easier to explain the difference in these approaches, then 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 transport, which is very good on the one hand, because immediately, all the power of the technology stack based on this markup language comes into our arsenal, namely XML-Schema - a language for describing the structure of an XML document (thanks to Wikipedia!), which allows automatic validation of client data coming to the server.

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 level 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 will give a hint - SOAP! ... Any guesses didn’t appear? ... Have you definitely read the Wikipedia article? ... In general, I won’t bother you further. Therefore, I will immediately turn to the answer: “SOAP (from the English Simple Object Access Protocol - a simple protocol for accessing objects; up to specification 1.2) ". The most noteworthy in this line is in italics! I don’t know what conclusions you made from all of this, but I see the following - since this protocol can by no means be called “simple” (and apparently even w3 agrees with this), then from version 1.2 it generally ceased to be decrypted somehow! And it began to be called SOAP, just SOAP and the point.

Oh well, please excuse me, it skidded a little to the side. As I wrote earlier, XML is used as a transport, and packages that run between the client and 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 structure of an HTML page. It has a main section - Envelop , which includes the Header and Body sections , orFault. В Body передаются данные и он является обязательным разделом конверта, в то время как Header является опциональным. В Header может передаваться авторизация, либо какие-либо иные данные, которые на прямую не относятся к входным данным процедур веб-сервиса. Про Fault особо рассказывать нечего, кроме того, что он приходит в клиент с сервера в случае возникновения каких-либо ошибок.


На этом мой обзорный рассказ про протокол SOAP заканчивается (более детально сами конверты и их структуру мы рассмотрим когда наши клиент и сервер наконец-то научатся запускать их друг в друга) и начинается новый – про компаньона SOAP под названием WSDL(Web Services Description Language). Yes, yes, this is the very thing that scares away most of us from the very attempt 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 language for describing and accessing web services, based on the Wikipedia XML language (c). If from this definition you don’t understand the whole sacred meaning of this technology, then I will try to describe it in my own words!

WSDL is designed so that our clients can communicate normally with the server. To do this, the following information is described in a file with the extension * .wsdl:
  • What namespaces were used,
  • What data schemes were used,
  • What types of messages does the web service expect from customers,
  • What data belongs to which web service procedures,
  • What procedures does the web service contain?
  • How should a client call web service procedures,
  • What address should the client call be sent to.

As you can see, this file is the entire web service. Having indicated 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’s enough to know the location address of its WSDL file! Soon we will find out that SOAP is not so terrible as it is painted (c) by Russian proverbs.

3 Introduction to XML Schema


Now we know a lot of things about what SOAP is, what is inside it and we have an overview of what technology stack surrounds it. Since, first of all, SOAP is a way 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 look a bit at how automatic data validation through XML schemas occurs.

The main task of the scheme is to describe the data structure that we are going to process. All data in XML schemas is divided into simple (scalar) and coplex (structures) types. Simple types include such types as:
  • line,
  • number,
  • boolean value
  • date.

Something very simple that has no extensions inside. Their antipode are 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 the following properties: author , title , price , ISBN number , etc. And these properties, in turn, can be both simple types and complex. 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:

71239876543Тестовое сообщение2013-07-20T12:00:0012

Our complex type diagram will look like this:


This entry is read as follows: we have a variable “ message ” of type “ Message ” and there is a complex type called “ Message ”, which consists of a sequential set of elements “ phone ” of type string , “ text ” of type string , “ date ” of type dateTime , " 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 value of the elements " element " and " complexType»You have become more or less clear, so we will not focus on them anymore and immediately switch to the composer element“ sequence ”. When we use the “ sequence ” composer element , we report that the elements included in it should always be located in the sequence indicated in the diagram, and all of them are mandatory. But do not despair! There are two more composer elements in XML schemas: choice and all . The composer " choice " reports that there must be some 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 sms messages can be transmitted from one to infinity in a packet. Therefore, I propose to understand how such data is declared in the XML schema. The overall package structure may look like this:

71239876543Тестовое сообщение 12013-07-20T12:00:001271239876543Тестовое сообщение N2013-07-20T12:00:0012

The schema for this complex type will look like this:


The first block contains the familiar declaration of the complex type “ Message ”. If you noticed, then in each simple type included in the " Message ", new qualifying attributes " minOccurs " and " maxOccurs " were added . As it is not difficult to guess from the name, the first ( minOccurs ) reports that in this sequence there should be at least one element of the type “ phone ”, “ text ”, “ date ” and “ type ”, while the next ( maxOccurs)) the attribute declares to us that there are a maximum of one such element in our sequence. As a result, when we write our schemes for any data, we are given the widest choice for setting them up!

The second block of the scheme declares a messageList element of type MessageList . It can be seen that the " MessageList " is a complex type that includes at least one " message " element , but the maximum number of such elements is not limited!

On this we will assume that LikBez has been completed according to the schemes and then another exciting adventure is waiting for us - we will write our own WSDL!

4 We write your 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 propose not to hack.

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

application/wsdl+xml

But in practice, I usually sent the “ text / xml ” HTTP 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 pretty impressive description, so don’t be scared, because most of the text is required water and once written, you can constantly copy it from one web service to another!

Since WSDL is XML, in the very first line you need to write about it directly. The root element of a file should always be called “ definitions ”:


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


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

After that, we announce that our SmsService web service has an entry point (“port”) called “ SmsServicePort ”. It is at this entry point that all requests from clients to the server will be sent. And we indicate in the element " address " a link to a file handler that will receive requests.

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


To do this, it lists what operations and in what form will be called. Those. for the SmsServicePort port , a binding is defined under the name SmsServiceBinding , which has the rpc call type and uses HTTP as the transport (transport) protocol. 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 announced, you must specify 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:


To do this, we indicate that our binding is of the type “ SmsServicePortType ” and in the “ portType ” element with the same type 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 message (from server to client) will be called “ sendSmsResponse ”. Like all names in 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 " message " elements with the names " sendSmsRequest " and " sendSmsResponse ", respectively. In them we indicate that an envelope should be received at the input, the structure of which corresponds to the " Request " data type . After that, an envelope containing the data type “ Response ” is returned from the server .

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


You can congratulate us! Our first WSDL was written! And we are one step closer to achieving our goal.
Next, we will deal with what PHP provides us 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 SoapServer built-in class. In order for all further actions to happen as well as mine, you need to tweak your PHP a little. To be more precise, you need to make sure that you have the extension "php-soap". How to put it on your web server is best read on the official PHP website (see the list of references).

After everything has been installed and configured, we will need to create a file “ smsservice.php ” in the root folder of your hosting with the following contents:

setClass("SoapSmsGateWay");
//Запускаем сервер
$server->handle();

What is above the line with the ini_set function, I hope there is no need to explain. Because it determines what HTTP headers we will send from the server to the client and the environment is configured. In the line with “ini_set” we disable the caching of the WSDL file so that our changes in it immediately take effect on the client.

Now we come to the server! As you can see, the entire SOAP server takes 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 a file with the speaking 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 sendSms method will be described . In the third line, we start the server! That's it, 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 allow yourself liberties and “template” it a little:

";
?>

At this stage, the resulting server should suit us completely, because we can log the envelopes arriving to it and then calmly analyze the incoming data. In order for us to be able to receive something on the server, we need a client. So let’s do it!

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 in the root of the host and call it “ client.php ”, and inside we will write the following:

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, three entities were described for the envelope entering the server: Request , MessageList and Message . Accordingly, the Request , MessageList, and Message classes are reflections of these entities in our PHP script.

After we defined the objects, we need to create the object ( $ req), which we will send to the server. Then come the two most treasured 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 to ensure that our server successfully receives and processes them! In the first of them, we create an instance of the SoapClient class and pass the location address of the WSDL file to its constructor, 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 sendSms method of the $ client object and immediately print the result in the browser.
Let's run and see what we finally got!

The following object returned to me from the server:

object(stdClass)[5]
  public 'status' => boolean true

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

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

79871234567Тестовое сообщение 12013-07-21T15:00:00.2615

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

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 was deserialized correctly, with which I want to congratulate all of us! Then something more interesting awaits us! Namely - we will send the client to the server not one SMS message, but a whole bunch (to be more precise, then three)!

7 We send complex objects


Let's think about how we can transfer a whole bunch of messages to the server in one package? Probably the easiest way would be to organize the 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 say that the following package came from the client:

79871234567Тестовое сообщение 12013-07-21T15:00:00.261579871234567Тестовое сообщение 22014-08-22T16:01:101679871234567Тестовое сообщение 32014-08-22T16:01:1017

What nonsense, you say? And you will be right in a sense, because since we just learned that what object left the client, then in the exact same form it came to our server in the form of an envelope. True, sms messages were not serialized in XML as we needed - they should have been wrapped in message elements , and not in Struct . Now let's see in what form such an object comes in 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? Just that the path we have chosen is not correct and we have not received an answer to the question - “How do 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:

79871234567Тестовое сообщение 12013-07-21T15:00:00.261579871234567Тестовое сообщение 22014-08-22T16:01:101679871234567Тестовое сообщение 32014-08-22T16:01:1017

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, then “from a change in the places of the terms - the amount does not change” (c). What BOGUS , what Struct - the goal we have not yet reached! And to achieve it, we need to make sure that instead of these obscure names, our native message is displayed . But how to achieve this, the author does not yet know. Therefore, the only thing we can do is to get rid of the excess container. In other words, we have to do now, so that instead of the message became BOGUS ! To do this, modify 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 will be pulled out of the scheme? To do this, look at the envelope that arrived:

79871234567Тестовое сообщение 12013-07-21T15:00:00.261579871234567Тестовое сообщение 22014-08-22T16:01:101679871234567Тестовое сообщение 32014-08-22T16:01:1017

Yes, a miracle did not happen! BOGUS - we won’t win! The object that came to sendSms in this case 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 propose to quietly round out and draw some conclusions for myself.

8 Conclusion


Finally we got here! Let's decide what you can do now:
  • you can write the WSDL file necessary for your web service;
  • without any problems you can write your own client capable of communicating with the server via SOAP;
  • You can write your own server communicating with the outside world via SOAP;
  • You can send arrays of objects of the same type to the server from your client (with some restrictions).

Also, we made some discoveries for ourselves in the course of our small study:
  • нативный класс SoapClient не умеет правильно сериализовывать однотипные структуры данных в XML;
  • при сериализации массива в XML он создает лишний элемент с именем Struct;
  • при сериализации объекта в XML он создает лишний элемент с именем BOGUS;
  • BOGUS меньшее зло чем Struct из-за того, что конверт получается компактнее (не добавляются лишние namespace'ы в XML заголовке конверта);
  • к сожалению, класс SoapServer автоматически не валидирует данные конверта нашей XML-схемой (возможно, и другие сервера этого не делают).


9 Список литературы




PS The author of the article wanted to cover the authorization of packages using the capabilities built into SOAP, but he was not able to do this using the SoapServer and SoapClient classes. Therefore, if you have positive experience using the built-in authorization in SOAP through PHP, then please write about this in the comments to the article, or in PM :)


PPS add-on from Mikaz
Instead of an array, you need to use ArrayObject in conjunction with SoapVar .

The working code might look like this:
$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);


Also popular now: