Computer lessons

We are writing a SOAP client-server application in PHP. Is Microsoft out of the game?

(PHP 5 >= 5.0.1)

SoapClient::SoapClient — SoapClient constructor

Parameters

wsdl

URI of the WSDL file or NULL if working in non-WSDL mode.

During development, WSDL caching may be disabled by the use of the soap.wsdl_cache_ttl php.ini setting otherwise changes made to the WSDL file will have no effect until soap.wsdl_cache_ttl is expired.

options

An array of options. If working in WSDL mode, this parameter is optional. If working in non-WSDL mode, the location and uri options must be set, where location is the URL of the SOAP server to send the request to, and uri is the target namespace of the SOAP service.

The style and use options only work in non-WSDL mode. In WSDL mode, they come from the WSDL file.

The soap_version option specifies whether to use SOAP 1.1 (default), or SOAP 1.2 client.

For HTTP authentication, the login and password options can be used to supply credentials. For making an HTTP connection through a proxy server, the options proxy_host, proxy_port, proxy_login and proxy_password are also available. For HTTPS client certificate authentication use local_cert and passphrase options. An authentication may be supplied in the authentication option. The authentication method may be either SOAP_AUTHENTICATION_BASIC(default) or SOAP_AUTHENTICATION_DIGEST.

The compression option allows to use compression of HTTP SOAP requests and responses.

The encoding option defines internal character encoding. This option does not change the encoding of SOAP requests (it is always utf-8), but converts strings into it.

The trace option enables tracing of request so faults can be backtraced. This defaults to FALSE

The classmap option can be used to map some WSDL types to PHP classes. This option must be an array with WSDL types as keys and names of PHP classes as values.

Setting the boolean trace option enables use of the methods SoapClient->__getLastRequest , SoapClient->__getLastRequestHeaders , SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders .

The exceptions option is a boolean value defining whether soap errors throw exceptions of type SoapFault .

The connection_timeout option defines a timeout in seconds for the connection to the SOAP service. This option does not define a timeout for services with slow responses. To limit the time to wait for calls to finish the default_socket_timeout setting is available.

The typemap option is an array of type mappings. Type mapping is an array with keys type_name, type_ns(namespace URI), from_xml(callback accepting one string parameter) and to_xml(callback accepting one object parameter).

The cache_wsdl option is one of WSDL_CACHE_NONE, WSDL_CACHE_DISK, WSDL_CACHE_MEMORY or WSDL_CACHE_BOTH.

The user_agent option specifies string to use in User-Agent header.

The stream_context option is a for context .

The features option is a bitmask of SOAP_SINGLE_ELEMENT_ARRAYS, SOAP_USE_XSI_ARRAY_TYPE, SOAP_WAIT_ONE_WAY_CALLS.

The keep_alive option is a boolean value defining whether to send the Connection: Keep-Alive header or Connection: close .

Changelog

Version Description
5.4.0 New keep_alive option.

Examples

Example #1 SoapClient::SoapClient() example

$client = new SoapClient("some.wsdl");

$client = new SoapClient ("some.wsdl" , array("soap_version" => SOAP_1_2 ));

$client = new SoapClient ("some.wsdl" , array("login" => "some_name" ,
"password" => "some_password" ));

$client = new SoapClient ("some.wsdl" , array("proxy_host" => "localhost" ,
"proxy_port" => 8080 ));

$client = new SoapClient ("some.wsdl" , array("proxy_host" => "localhost" ,
"proxy_port" => 8080,
"proxy_login" => "some_name" ,
"proxy_password" => "some_password" ));

$client = new SoapClient ("some.wsdl" , array("local_cert" => "cert_key.pem" ));

$client = new SoapClient (null , array("location" =>
"uri" => "http://test-uri/" ));

$client = new SoapClient (null , array("location" => "http://localhost/soap.php" ,
"uri" => "http://test-uri/" ,
"style" => SOAP_DOCUMENT ,
"use" => SOAP_LITERAL ));

$client = new SoapClient("some.wsdl" ,
array("compression" => SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP ));

$server = new SoapClient ("some.wsdl" , array("encoding" => "ISO-8859-1" ));

class MyBook(
public $title ;
public $author ;
}

$server = new SoapClient ("books.wsdl" , array("classmap" => array("book" => "MyBook" )));

?>

Chris Gunawardena

To monitor SOAP calls in and out of a unix server:

Sudo tcpdump -nn -vv -A -s 0 -i eth0 dst or src host xxx.xxx.xxx.xxx and port 80

And always use "cache_wsdl" => WSDL_CACHE_NONE

svenr at selfhtml dot org

The "classmap" option actually is a mapping from the "ComplexType" used within the SOAP to your PHP Classes.

Do not confuse the XML tag names returned for your request with these ComplexTypes. SOAP allows them to be different.

I had something like this:
...


FooBar

TagName is not the key you want to put in your classmap, you have to know the name of the ComplexType this TagName refers to. This info is contained inside the WSDL resource.

paulovitorbal at gmail dot com

When using certificates, in the parameter "local_cert", use the file contents, not the name of the file.

New soapclient("http://localhost/index.php?wsdl ", array("local_cert"=>file_get_contents("./key.pem"),"passphrase"=>"password"));

In PHP, you can have private properties defined in the classes you use in your classmap. So if you create a private property $foo in you class, and the SOAP element has a child element , the contents of $foo will be set to the contents of . This way you can control acces to the properties in your classes.

(Note: This does not work in Facebook's HPHP).

willem dot stuursma at hyves dot nl

If you want to use classes in a different namespace in your classmap, just use the backslash in the target class name.

Example:
$classmap = array("result" => "MyNamespace\\Result" );
?>

You need to specify the backslash twice because it is the escape character in strings.

simonlang at gmx dot ch

Example for a soap client with HTTP authentication over a proxy:

new SoapClient(
"service.wsdl" ,
array(
// Stuff for development.
"trace" => 1 ,
"exceptions" => true ,
"cache_wsdl" => WSDL_CACHE_NONE ,
"features" => SOAP_SINGLE_ELEMENT_ARRAYS ,

// Auth credentials for the SOAP request.
"login" => "username" ,
"password" => "password" ,

// Proxy url.
"proxy_host" => "example.com" , // Do not add the schema here (http or https). It won't work.
"proxy_port" => 44300,

// Auth credentials for the proxy.
"proxy_login" => NULL ,
"proxy_password" => NULL ,
);
?>
Providing an URL to a WSDL file on the remote server (which as well is protected with HTTP authentication) didn't work. I downloaded the WSDL and stored it on the local server.

Asaf Meller

A full working php .net soap configuration:
notes
1. web.config on .net server must work with basichttp binding.
2. parameters to soap functions must be passed as:
array("parm1_name"=>"parm1_value",
"parm2_name"=>"parm2_value"...)

header("Content-Type: text/plain");

Try (
$options = array(
"soap_version" => SOAP_1_1 ,
"exceptions" => true ,
"trace" => 1 ,
"cache_wsdl" => WSDL_CACHE_NONE
);
$client = new SoapClient ( "http://www.example.com/end_point.wsdl", $options);

) catch (Exception $e ) (
echo "

Exception Error!

" ;
echo $e -> getMessage();
}

Echo "running HelloWorld:" ;

Try (
$response = $client -> HelloWorld();

}
catch (Exception $e)
{
echo "Caught exception: " , $e -> getMessage (), "\n" ;
}

Print_r($response);
?>
good luck!
Asaf.

faebu at faebu dot ch

I"m experiencing the same problems when trying to load a WDSL fiel which is protected by basic http authentication, since the parameters login and password are just used for the request but not when reading the wdsl file. I just use the following workaround by downloading the xml file to a non-protected location on my server. Please notice that this doesn"t support any kind of caching.

class SoapAuthClient extends SoapClient (
/**
* Since the PHP SOAP package does not support basic authentication
* this class downloads the WDSL file using the cURL package and
* creates a local copy of the wdsl on your server.
*Make sure you provide the following additional parameter in the
* $optionsArray:
* wdsl_local_copy => true
*/

Private $cache_dir = "/home/example/htdocs/cache/" ;
private $cache_url = "http://www.example.com/cache/";

Function SoapAuthClient ($wdsl, $options) (
if (isset($options [ "wdsl_local_copy" ]) &&
$options [ "wdsl_local_copy" ] == true &&
isset($options [ "login" ]) &&
isset($options [ "password" ])) (

$file = md5(uniqid()). ".xml" ;

If (($fp = fopen ($this -> cache_dir . $file , "w" )) == false ) (
throw new Exception ( "Could not create local WDSL file (". $this -> cache_dir . $file. ")" );
}

$ch = curl_init();
$credit = ($options["login"]. ":" . $options["password" ]);
curl_setopt($ch, CURLOPT_URL, $wdsl);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt ($ch, CURLOPT_USERPWD, $credit);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt ($ch, CURLOPT_FILE, $fp);
if (($xml = curl_exec ($ch )) === false ) (
//curl_close($ch);
fclose($fp);
unlink ($this -> cache_dir . $file );

Throw new Exception(curl_error($ch));
}

Curl_close($ch);
fclose($fp);
$wdsl = $this -> cache_url . $file ;
}

Unset($options [ "wdsl_local_copy" ]);
unset($options [ "wdsl_force_local_copy" ]);

Echo $wdsl ;
parent :: __construct ($wdsl, $options);

Unlink ($this -> cache_dir . $file );
}
}
?>

tatupheba at gmail dot com

Hello folks!

A hint for developers:

When programming some soap server set the "soap.wsdl_cache_enabled" directive in php.ini file to 0:

Soap.wsdl_cache_enabled=0

Otherwise it will give a bunch of strange errors saying that your wsdl is incorrect or is missing.

Doing that will save you from a lot of useless pain.

titan at phpdevshell dot org

It should be noted that if you receive a return error: "Object reference not set to an instance of an object.". This could be due to something as simple as passing the incorrect parameters. When you look at this XML:



string
dateTime
dateTime

Your code should look something like this:

try (
$options = array(
"soap_version" => SOAP_1_2 ,
"exceptions" => true ,
"trace" => 1 ,
"cache_wsdl" => WSDL_CACHE_NONE
);
$client = new SoapClient ( "http://example.com/doc.asmx?WSDL", $options);
// Note where "Get" and "request" tags are in the XML
$results = $client -> Get (array("request" =>array("CustomerId" => "1234" )));
) catch (Exception $e ) (
echo "

Exception Error!

" ;
echo $e -> getMessage();
}

$results = $client -> Get (array("request" =>array("CustomerId" => "842115" )));
?>

If your WSDL file contains a parameter with a base64Binary type, you should not use base64_encode() when passing along your soap vars. When doing the request, the SOAP library automatically base64 encodes your data, so otherwise you"ll be encoding it twice.

WSDL snipplet:

$string = "data_you_want_to_send___like_xml_in_soap";
$soap_data = array(
"foo" => "bar" ,
//"content" => base64_encode($string) // don"t do this
"content" => $string //do this
);
$response = $client -> Send ($soap_data );
?>

marcovtwout at hotmail dot com

Being new to SOAP, I was searching for a while to find out why my message was getting a response in soapUI, but not with my php code. The specific service I was addressing gives a HTTP 202 Accepted on success (no response), but returns a SOAP message on errors.

Situation:
Using an (authenticated) client connection, and a WDSL file, SOAP calls with type "One-Way" don"t give a response header, even though a response is expected.

Solution:
When calling the client constructor, set SOAP_WAIT_ONE_WAY_CALLS in the $options["features"].

tim at tdinternet dot com

PHP 5.2.11 seems to not be very picky about the correctness of a WSDL.

Other SOAP clients were complaining about schema and namespace issues, while PHP"s SoapClient worked completely fine. Fixing those issues for the other clients however broke PHP"s SoapClient to the point where objects being passed to the SOAP method were becoming empty arrays on the server side.

Lesson learned: some elements were being prefixed with xsd: and others were not -- make absolutely sure your WSDL is correct and consistent (I"m using a tweaked WSDL_Gen.php).

james dot ellis at gmail dot com

I was having troubles getting responses from a Coldfusion SOAP server, with no obvious issues in the SoapClient used.

Eventually I found that the server was only accepting SOAP 1.1 requests and not 1.2. Not sure if this is a system wide Coldfusion setting but if you hit the same wall, try setting the SoapClient option "soap_version" to the constant SOAP_1_1 (which is the default but mine was defaulting to 1.2 as the client was being reused for another service )

ajcartmell at fonant dot com

There seems to be a problem with specifying empty strings for proxy_host and proxy_port options in recent versions of PHP (from a version later than 5.2.9, and equal to or earlier than 5.2.11).

Supplying empty string values ​​for proxy_host and proxy_port causes "host not found" type errors: supplying NULL or FALSE works fine.

bhargav dot khatana at gmail dot com

It took me longer than a week to figure out how to implement WSSE (Web Service Security) headers in native PHP SOAP. There are no much resource available on this, so thought to add this here for community benefit.

Step1: Create two classes to create a structure for WSSE headers

class clsWSSEAuth(
private $Username ;
private $Password ;
function __construct ($username, $password) (
$this -> Username = $username ;
$this -> Password = $password ;
}
}

Class clsWSSEToken (
private $UsernameToken ;
function __construct ($innerVal)(
$this -> UsernameToken = $innerVal ;
}
}
?>
Step2: Create Soap Variables for UserName and Password

$username = 1111 ;
$password = 1111 ;

//Check with your provider which security name-space they are using.
$strWSSENS = "http://schemas.xmlsoap.org/ws/2002/07/secext";

$objSoapVarUser = new SoapVar($username, XSD_STRING, NULL, $strWSSENS, NULL, $strWSSENS);
$objSoapVarPass = new SoapVar($password, XSD_STRING, NULL, $strWSSENS, NULL, $strWSSENS);
?>
Step3: Create Object for Auth Class and pass in soap var

$objWSSEAuth = new clsWSSEAuth ($objSoapVarUser , $objSoapVarPass );
?>
Step4: Create SoapVar out of object of Auth class

$objSoapVarWSSEAuth= new SoapVar ($objWSSEAuth , SOAP_ENC_OBJECT , NULL , $strWSSENS , "UsernameToken" , $strWSSENS );
?>
Step5: Create object for Token Class

$objWSSEToken = new clsWSSEToken ($objSoapVarWSSEAuth );
?>
Step6: Create SoapVar out of object of Token class

$objSoapVarWSSEToken= new SoapVar ($objWSSEToken, SOAP_ENC_OBJECT, NULL, $strWSSENS, "UsernameToken", $strWSSENS);
?>
Step7: Create SoapVar for "Security" node

$objSoapVarHeaderVal=new SoapVar ($objSoapVarWSSEToken, SOAP_ENC_OBJECT, NULL, $strWSSENS, "Security", $strWSSENS);
?>
Step8: Create header object out of security soapvar

$objSoapVarWSSEHeader= new SoapHeader ($strWSSENS , "Security" , $objSoapVarHeaderVal , true , "http://abce.com");

//Third parameter here makes "mustUnderstand=1
//Forth parameter generates "actor="http://abce.com ""
?>
Step9: Create object of Soap Client

$objClient = new SoapClient ($WSDL, $arrOptions);
?>
Step10: Set headers for soapclient object

$objClient -> __setSoapHeaders (array($objSoapVarWSSEHeader ));
?>
Step 11: Final call to method

$objResponse = $objClient -> __soapCall ($strMethod , $requestPayloadString );
?>

peter at webacoustics dot com

I found that WSDL fetching fails when using basic authentication in the soapclient. So I implemented the following workaround using wget. I realize wget may not be an option for some environments, in that case cURL would be the next simplest thing.

$wsdl = get_wsdl ( "https://example.com/soap/service?wsdl");
$this -> client = new SoapClient ($wsdl , array("user" => "someuser" , "password" => "somepassword" ));

Private function get_wsdl ($url) (
global $g ;
$url = escapeshellarg($url);
$cache_file = "/tmp/soap.wsdl." . md5($url);

//only fetch a new wsdl every hour
if(! file_exists ($cache_file ) || filectime ($cache_file )< time () - 3600 ) {
$agent = escapeshellarg("--user-agent=($g["useragent"])");
mwexec("wget ​​--quiet --timeout=5 ( $agent ) --no-check-certificate --output-document=( $cache_file ) ( $url ) " );
if(! file_exists ($cache_file )) (
throw new Exception("Couldn't load WSDL at ( $url )" );
}
}
return
$cache_file ;
}
?>

meltir at meltir dot com

To those fighting with NTLM authenticated proxy servers, here"s a solution I"m using atm:

/**
* A child of SoapClient with support for ntlm proxy authentication
*
* @author Meltir
*
*/
class NTLM_SoapClient extends SoapClient (

Public function __construct ($wsdl, $options = array()) (
if (empty($options [ "proxy_login" ]) || empty($options [ "proxy_password" ])) throw new Exception ( "Login and password required for NTLM authentication!");
$this -> proxy_login = $options [ "proxy_login" ];
$this -> proxy_password = $options [ "proxy_password" ];
$this -> proxy_host = (empty($options [ "proxy_host" ]) ? "localhost": $options[ "proxy_host"]);
$this-> proxy_port= (empty($options[ "proxy_port"]) ? 8080 : $options[ "proxy_port"]);
parent:: __construct($wsdl, $options);
}

/**
* Call a url using curl with ntlm auth
*
* @param string $url
* @param string $data
* @return string
* @throws SoapFault on curl connection error
*/
protected functioncallCurl($url, $data) {
$handle= curl_init();
curl_setopt($handle, CURLOPT_HEADER, false);
curl_setopt($handle, CURLOPT_URL, $url);
curl_setopt($handle, CURLOPT_FAILONERROR, true);
curl_setopt($handle, CURLOPT_HTTPHEADER,Array("PHP SOAP-NTLM Client"));
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this-> proxy_login. ":" . $this-> proxy_password);
curl_setopt($handle, CURLOPT_PROXY, $this-> proxy_host. ":" . $this-> proxy_port);
curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
$response= curl_exec($handle);
if (empty(
$response)) {
throw new
SoapFault("CURL error: ". curl_error($handle), curl_errno($handle));
}
curl_close($handle);
return
$response;
}

Public function __doRequest($request, $location, $action, $version, $one_way= 0 ) {
return
$this-> callCurl($location, $request);
}

}
?>

Requires curl and could be extended, but it works for my simple needs.

eric dot caron at gmail dot com

Thought pointed out by jan at bestbytes, and discussed in bug #27777, one common source of the "Parsing WSDL: Couldn"t find " error is from trying to access a WSDL that is protected by HTTP authentication. Passing the login/password in the 2nd parameter doesn"t always work; so if you encounter this error message and are trying to access a protected WSDL file, try passing the username and password in with the first parameter.

Anonymous

I had to struggle with a rather strange behavior when trying to consume standard Parlay X web services with no success. However, I found a remedy to my problem.

The problem which I faced was about an erroneous invalid HTTP basic authentication request sent to the web service. Although I was sending the right credentials, I was getting an authentication error. It turns out that PHP was sending HTTP requests to another endpoint which is not exposed directly through the web service and that end point does not require authentication.

My remedy for that issue was by using this simple lines in the example of using sendSms Paraly-X method.

First, creating a soap client without any HTTP authentication options:

$client= newSoapClient($wsdl_url);
?>

The above request will cache the wsdl in the /tmp directory. Immediately after this construction we create another soap client, this time with HTTP authentication options:

try (
$client= newSoapClient($wsdl_url, array("login"=> "griffin",
"password"=> "password"));
) catch (
Exception $e) {
printf("Error:sendSms: %s\n", $e-> __toString());
return
false;
}
?>

Now it should work without any problem. Without the second call, PHP will call sendSms without credentials resulting in a failed attempt due to missing authentication information. I found this procedure to be the simplest.

This process should be done every time the cached wsdl expires, or simply a one can increase the time-to-live for the cached wsdl from php.ini

jon dot gilbert at net-entwicklung dot de

Note that creating a soap client for an invalid URL (you do test what happens, when a service is not available, right?) usually throws an exception which can be caught with try..catch. However, if xdebug is active you will get a fatal error, which obviously cannot be caught.

sloloem at gmail dot com

I had an issue discussing the use of classmap that took me quite a while to figure out. I was assuming the WSDL type the docs were referring to was the name of the element being returned in the SOAP, so like,

And I was wondering why mapping
"classmap"=>array("node"=>"MyNode")

Did nothing.
That"s because in my WSDL I defined node as:

The classmap I needed was:
"classmap"=>array("nodeType"=>"MyNode")

I was able to find the type names using SoapClient->__getTypes()
Later, I realized where I could look inside the WSDL for the typename I needed.

I dunno if I missed something painfully obvious but maybe this can clear up some of the docs.

Jon

We"ve had some problems using SoapClient connecting to an external server via Microsoft ISA (presently v.2006 but this may apply to other versions too). We supply the proxy_host, proxy_port, proxy_login and proxy_password but the ISA server reports the login in its logs as "anonymous".

Our sysadmin believes this is because PHP is not supplying NTLN information (Windows security protocol) in the correct format (and whether it should work with proprietary proxies is of course another debate). We"d tried "username", "DOMAIN\username" to no effect. The solution is to add an exception in the ISA server for the target hostname/IP; null can then be supplied for proxy_login and proxy_password and the connection should then work as expected.

On a slightly related note, if you are having problems make sure the port number is supplied as an integer. Some versions of PHP will not use the proxy with SoapClient if the port number is supplied as a string.

jan at bestbytes dot de

You CAN get a wsdl, if basic authentication is required:

$login = "bert";
$password= "berts password";

$client= newSoapClient(
"http://". urlencode($login) . ":" . urlencode($password) . "@www.server.com/path/to/wsdl",
array(
"login"=> $login,
"password"=> $password
)
);

?>

info at nicksilvestro dot net

For anyone having trouble with ArrayOf_xsd_string and getting an error similar to "No deserializer defined for array type (http://www.w3.org/2001/XMLSchema)string"
Try using the "features" param, set to SOAP_USE_XSI_ARRAY_TYPE - this makes sure the correct deserializer is used.

Eg,
$client= newSoapClient("some.wsdl", array("features"=> SOAP_USE_XSI_ARRAY_TYPE));
?>

sniper

i was looking for a good example and couldnt find one,
finally found it somewhere(forgot where) i think this is
the best example to make a soap request with multiple params

$params->AWSAccessKeyId = AMAZON_API_KEY;
$params->Request->SearchIndex = "Books";
$params->Request->Keywords = "php5 oop";

$amazon = new SoapClient("http://webservices.amazon.com
/AWSECommerceService/AWSECommerceService.wsdl");
$result = $amazon->itemSearch($params);

alex on reutone comma com

To connect PHP SOAP to MS SOAP (CRM/EXCHANGE/...) I have created some classes using the explanation below and in other places.
www.reutone.com/heb/articles.php?instance_id=62&actions=show&id=521

naugtur at gmail dot com

SoapFault exception: looks like we got no XML document in has been already mentioned to occur when your server outputs something before ... > tag.

For all those having problems with that, andno access to the server code:
This is how to make a proxy that would clean responsesforYou

php
/**
* Simple class taken from a note by James Ellis
*/
classProxy_ClientextendsSoapClient{
protected
$cacheDocument= "" ;
public function
__construct($wsdl, $options) {
parent:: __construct($wsdl, $options);
}

/**
* SetCacheDocument() sets the previously cached document contents
*/
public functionSetCacheDocument($document) {
$this-> cacheDocument= $document;
}

/**
* __doRequest() overrides the standard SoapClient to handle a local request
*/
public function__doRequest() {
return
$this-> cacheDocument;
}
}

//put this code in your function or wherever You have all required variables set

$client= newSoapClient($wsdl_url, $settings_array);
$void= $client-> $method($params); //call this to get response from server

$response_string= $client-> __getLastResponse();

//this part removes stuff
$start= strpos($response_string, ");
$end= strrpos($response_string, ">" );
$response_string= substr($response_string, $start, $end- $start+ 1 );

//get your proxy prepared
$proxy= newProxy_Client($wsdl_url, $settings_array);
//and fill it with the server's response
$proxy-> SetCacheDocument($response_string);

$and_finally_the_result_is= $proxy-> $method($params);

print_r($and_finally_the_result_is); //this allows you to see what"s there

?>

$method is the method"s name eg. $method="getVersion";
$params - typical params for a soap method

Tips for Users:

    If you know the WSDL file, you can setup a quick link to the client forms using
    http://www.?template=/clientform.html&fn=soapform
    &SoapTemplate=none&SoapWSDL=Your_WSDL_File
    or
    http://www..html?SoapWSDL=Your_WSDL_File

    The server caches WSDL files in normal operations to improve performance. If you make any changes to a WSDL file, select the No checkbox.

Tips for Developers:

    Use<documentation> whenever possible in your WSDL file to provide instructions. It will be displayed in the client form.

    Use enumeration type if an element has fix number of values. They will be displayed as dropdown boxes.

Key Features:

    Support both 1999 and 2001 XML schema. The tool uses the schema defined in the WSDL file for constructing SOAP requests.

    Support array and array of structures. Only single dimensional arrays are supported. Sorry, no sparse arrays.

    Capable of serializing complex data types and array of complex data types, even multi-level embedded structs.

    Handling ID/HREF in both SOAP messages and schema definitions.

    Support both SOAP section 5/7 and document/literal encodings..

Technical Details-- Dynamic Binding of SOAP Services

A binding is a contract between the client logic and server logic. There are two types of bindings in SOAP: Object binding (or SOAP binding) and parameter binding. Most of the SOAP toolkits perform static object bindings by generating client side proxy objects. The problem is that, unlike the traditional programming module where objects/interfaces are stable, web services are subject to change at any moment without notice, because they are often owned/controlled by a third party. Another problem occurs when the number of web services to be accessed increases, the generated source code could quickly become a maintenance nightmare. Finally, when the web services to be accessed are unknown, which is more often then likely, early binding becomes impossible, or at least difficult. Generating a proxy object for a service to be built in the future is an interesting research project.

The generic SOAP client demonstrates dynamic bindings (or run-time bindings) of SOAP services and parameters. An object is generated at execution time when WSDL file is specified, and parameter values ​​are associated with a SOAP message just before delivery. The late binding (or delayed binding) technique could greatly reduces maintenance cost, because a single client can be used to access many web services.

Here at LeaseWeb, we work a lot with SOAP web-services to integrate our internal applications with each other. Especially during development and testing of our applications as we need the ability to practice with SOAP API’s.

$ curl -sS http://leaseweb.github.io/php-soap-client/installer | php

This will download the phar file to the current working directory and make it executable so you can use start using it right away by invoking:

$ ./soap_client

To install the latest master version you can get the source code directly from GitHub, package your own .phar file and install it - using GNU Make.
In order to be able to create the .phar file you need to have composer installed. To read more about composer refer to their excellent documentation.

# Install php soap client $ git clone https://github.com/LeaseWeb/php-soap-client.git $ cd php-soap-client $ composer.phar install $ make $ sudo make install

If you are getting a Failed to compile phar exception while running make you need to set phar.readonly = Off in your php.ini . On a development machine this is fine to do but please be aware of the security risks when setting phar.readonly to Off .

The above make install command will install the soap_client application to /usr/local/bin and make it executable so you can easily call it like this:

$ soap_client php-soap-client version 2.1.3 Usage: command Options: ... Available commands: call Call the remote service with the `method` specified and output the response to stdout. help Displays help for a command list Lists commands list-methods Get a list of available methods to call on the remote. request Generate an xml formatted SOAP request for the given method and output to stdout. wsdl Get the WSDL of a soap service.

From this point onwards we assume you have installed the soap_client.phar on your system in /usr/local/bin/soap_client and that the directory /urs/local/bin is in your $PATH .

Lets say we would like to see what methods are available on the remote service http://www.webservicex.net/ConvertTemperature.asmx. We could issue the following command:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" list-methods

Which will output the following:

ConvertTemp

If you run the above command with the -vvv option you will get more verbose output.
In this case the only available method is ConvertTemp . Let’s see how a SOAP XML request looks like for this method:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" request ConvertTemp 0

If you want to make a SOAP request to the ConvertTemp method on the remote service use the call sub command:

$ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" call --editor ConvertTemp

Notice the --editor option after the call sub command. If you use the --editor flag soap_client will open up the editor specified in your environment variable $EDITOR so you are able to modify the request XML before sending it.

If you issue the same request multiple times, you could save a soap request as a local XML file and pass it to /dev/stdin of the soap_client call command:

# Get the request xml and store it locally $ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" request ConvertTemp > my_sample_request.xml # Now edit my_sample_request.xml # Now you can call the ConvertTemp method with this pre-prepared request $ soap_client --endpoint="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" call ConvertTemp< my_sample_request.xml

Since you will be repeating soap_client commands frequently in a short time while exploring a remote web service you can save yourself some time by setting an environment variable SOAPCLIENT_ENDPOINT that contains the URL to the WSDL. When this environment variable is set you can omit the --endpoint command line option. Let’s do this now and call the ConvertTemp method:

$ export SOAPCLIENT_ENDPOINT="http://www.webservicex.net/ConvertTemperature.asmx?WSDL" $ soap_client call ConvertTemp< my_sample_request.xml

I wanted to know how much 107.6 degrees Fahrenheit is in Celsius, so my my_sample_request.xml contains:

$ cat my_sample_request.xml 107.6 degreeFahrenheit degreeCelsius

$ soap_client call ConvertTemp< my_sample_request.xml stdClass Object ( => 42)

The answer is 42.

If you rather see the responses in XML format you can use the --xml command line option:

$ soap_client call --xml ConvertTemp< my_sample_request.xml 42

This tutorial should give you enough information to get started with exploring, testing and/or developing SOAP API's.
In a future blog post, I will continue the topic of the php soap client. We are currently working on packing the .phar archive for the web.

Lyrical part.

Imagine that you have implemented or are implementing a certain system that should be accessible from the outside. Those. there is a certain server with which you need to communicate. For example a web server.

This server can perform many actions, work with the database, perform some third-party requests to other servers, do some calculations, etc. live and possibly develop according to the scenario known to him (i.e. according to the developers’ scenario). It is not interesting for a person to communicate with such a server, because he may not be able/want to provide beautiful pages with pictures and other user-friendly content. It is written and works to work and provide data when asked to it, without worrying that it is human readable, the client will deal with it himself.

Other systems, accessing this server, can already dispose of the data received from this server at their own discretion - process, accumulate, issue to their clients, etc.

Well, one of the options for communicating with such servers is SOAP. SOAP xml message exchange protocol.

Practical part.

A web service (this is the name of what the server provides and what clients use) makes it possible to communicate with the server with clearly structured messages. The fact is that the web service does not accept any data. The web service will respond with an error to any message that does not comply with the rules. The error, by the way, will also be in xml form with a clear structure (which is not true about the text of the message).

WSDL (Web Services Description Language). The rules by which messages are composed for the web service are also described using xml and also have a clear structure. Those. If a web service provides the ability to call a method, it must allow clients to know what parameters are used for this method. If the web service expects a string for Method1 as a parameter and the string should be named Param1, then these rules will be specified in the web service description.

Not only simple types, but also objects and collections of objects can be passed as parameters. The description of an object comes down to a description of each component of the object. If an object consists of several fields, then each field is described, its type, name (what are the possible values). Fields can also be of a complex type, and so on until the description of the types ends with simple ones - string, boolean, number, date... However, some specific types may turn out to be simple, it is important that clients can understand what values ​​they may contain.

For clients, it is enough to know the url of the web service; the wsdl will always be nearby, from which you can get an idea of ​​the methods and their parameters that this web service provides.

What are the advantages of all these bells and whistles:

  • In most systems, the description of methods and types occurs automatically. Those. the programmer on the server just needs to say that this method can be called through a web service, and the wsdl description will be generated automatically.
  • The description, which has a clear structure, is readable by any soap client. Those. whatever the web service, the client will understand what data the web service receives. Using this description, the client can build its own internal structure of object classes, the so-called. binding" and. As a result, the programmer using the web service has to write something like (pseudocode):

    NewUser:=TSoapUser.Create("Vasya","Pupkin","admin"); soap.AddUser(NewUser);

  • Automatic validation.

    • xml validation. xml must be well-formed. Invalid xml - immediately an error to the client, let him sort it out.
    • schema-validation. xml must have a certain structure. xml does not match the schema - immediately an error to the client, let him sort it out.
    • Data verification is carried out by the soap server so that data types and restrictions match the description.
  • Authorization and authentication can be implemented using a separate method. natively. or using http authorization.
  • Web services can work both via the soap protocol and via http, that is, through get requests. That is, if the parameters are simple data (without structure), then you can simply call the usual get www.site.com/users.asmx/GetUser?Name=Vasia or post. However, this is not everywhere and not always.
  • ... see on Wikipedia

There are also a lot of disadvantages:

  • Unreasonably large message size. Well, here the very nature of xml is such that the format is redundant, the more tags, the more useless information. Plus soap adds its redundancy. For intranet systems, the issue of traffic is less acute than for the internet, so soap for local networks is more in demand, in particular Sharepoint has a soap web service with which you can communicate with success (and some limitations).
  • Automatically changing the description of a web service can break all clients. Well, it’s like this for any system, if backward compatibility with old methods is not supported, everything will fall off...
  • Not a minus, but a drawback. All method calls must be atomic. For example, when working with a database, we can start a transaction, execute several queries, then rollback or commit. There are no transactions in soap. One request, one answer, the conversation is over.
  • Dealing with the description of what is on the server side (is everything described correctly?) and what is on the client (what was described to me here?) can be quite difficult. There were several times when I had to deal with the client side and convince the server programmer that his data was described incorrectly, but he couldn’t understand anything about it at all, because automatic generation and he shouldn’t, it’s a matter of software. And the error, naturally, was in the method code; the programmer simply did not see it.
  • Practice shows that web service developers are terribly far from the people who use these web services. In response to any request (valid from the outside), an incomprehensible error “Error 5. Everything is bad” may come. It all depends on the conscience of the developers :)
  • I'm sure I still don't remember something...

As an example, there is an open web service belavia:

  • http://86.57.245.235/TimeTable/Service.asmx - entry point, there is also a text description of methods for third-party developers.
  • http://86.57.245.235/TimeTable/Service.asmx?WSDL - wsdl description of methods and types of received and returned data.
  • http://86.57.245.235/TimeTable/Service.asmx?op=GetAirportsList - description of a specific method with an example of the type of xml request and xml response.

You can manually create and send a request like:

POST /TimeTable/Service.asmx HTTP/1.1 Host: 86.57.245.235 Content-Type: text/xml; charset=utf-8 Content-Length: length SOAPAction: "http://webservices.belavia.by/GetAirportsList" ru

the answer will come:

HTTP/1.1 200 OK Date: Mon, 30 Sep 2013 00:06:44 GMT Server: Microsoft-IIS/6.0 X-Powered-By: ASP.NET X-AspNet-Version: 4.0.30319 Cache-Control: private, max -age=0 Content-Type: text/xml; charset=utf-8 Content-Length: 2940

PS Previously, the Aeroflot web service was opened, but after 1C added soap support to 8ku, a bunch of 1C beta testers successfully installed it. Now something has changed there (I don’t know the address, you can look it up if you’re interested).
ZZY Disclaimer. He spoke at the everyday level. You can kick.

Hi all!
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 own XML Web Service based on the SOAP 1.2 protocol.

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

  • write your own server implementation of a web application;
  • write your own client implementation of a web application;
  • write your own web service description (WSDL);
  • send 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 built-in SoapClient and SoapServer classes. Our rabbit will be a service for sending SMS messages.

1 Problem statement

1.1 Boundaries

At 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 arrive on the server. The process of queuing messages for further delivery 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 boundaries! The next step that needs to be taken is to decide what data we will exchange between the server and the client. On this topic, I suggest not to split hairs for too long and immediately answer the main questions for yourself:

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

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

  • mobile phone number and
  • text of the SMS message.

In principle, these two characteristics are enough to send, but I immediately imagine the case of an SMS with birthday greetings coming to you at 3 o’clock in the morning, or 4! At this moment, I will be very grateful to everyone for not forgetting about me! Therefore, we will also send to the server and

  • date of sending the SMS message.

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

  • Message type.

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

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

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

  • Mobile phone number,
  • SMS message text,
  • time of sending the SMS message to the subscriber,
  • message type.

We have answered the first question, now we need to answer the second question. And perhaps I’ll allow myself to mess around a little. Therefore, from the server we will send only Boolean data, the meaning 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

This concludes the description of the problem statement! And finally, let's get down to the fun part - let's figure out what kind of strange beast this SOAP is!

2 What is SOAP?

In general, initially I did not plan to write anything about what SOAP is and wanted to limit myself to links to the w3.org 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). You can read more about this on 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 protocol complexity are strictly limited, which means 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 transmit along with the data, while with the REST approach in our The site has many inputs (links), each of which accepts and processes only certain data. If anyone reading knows how to explain the difference in these approaches even more simply, be sure to write in the comments!

The next thing we need to know about SOAP is that this protocol uses the same XML as a transport, which on the one hand is very good, because Our arsenal immediately includes the full power of a stack of technologies based on this markup language, namely XML-Schema - a language for describing the structure of an XML document (thanks 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 it uses XML as a transport! If you read the article on Wikipedia, you can also learn from there that it can be used over any application-level protocol, and not just in combination with HTTP (unfortunately, in this topic we will only consider SOAP over HTTP). And you know what I like most about all this? If there are no guesses, then I’ll give a hint - SOAP!... Still no guesses?... Are you sure you read the article on Wikipedia?... In general, I won’t torture you further. Therefore, I’ll go straight to the answer: “SOAP (from the English Simple Object Access Protocol - simple protocol access to objects; up to specification 1.2)". The most remarkable thing about this line is in italics! I don’t know what conclusions you drew from all this, but I see the following - since this protocol cannot in any way be called “simple” (and apparently even w3 agrees with this), then from version 1.2 it stopped being decrypted somehow! And it became known as SOAP, just SOAP, period.

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

This is where my review story about the SOAP protocol ends (we will look at the envelopes themselves and their structure in more detail when our client and server finally learn to run them at 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 away from even trying to implement our API on this protocol. As a result, we usually reinvent our wheel with JSON as transport. So what is WSDL? WSDL is a language for describing web services and accessing them, based on the XML language (c) Wikipedia. If this definition does not make clear to you the entire sacred meaning of this technology, then I will try to describe it in my own words!

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

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

As you can see, this file is the entire web service. By specifying the address of the WSDL file in the client, we will know everything about any web service! As a result, we do not need to know absolutely nothing about where the web service itself is located. All you need to know is the location of its WSDL file! We will soon find out that SOAP is not as scary as Russian proverbs make it out to be.

3 Introduction to XML-Schema

Now we know a lot about what SOAP is, what is inside it, and have an overview of the technology stack that 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 occurs using XML schemas.

The main task of the diagram 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(structures) types. Simple types include the following types:

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

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

I suggest not going far and writing an XML schema for our SMS message! Below is the xml description of the SMS message:

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

Our complex type diagram will look like this:

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

I think that the meaning of the elements " element" And " complexType"Everything has become more or less clear to you, so we won’t focus on them any more and let’s switch straight to the composer element" sequence" When we use the composer element " sequence“We inform you that the elements included in it must always be located in the sequence specified in the diagram, and all of them are mandatory. But don't despair! There are two more composer elements in XML schemas: " choice" And " all" Composer " choice" announces that there must be one of the elements listed in it, and the composer " all» – any combination of the listed elements.

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

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

The diagram for such a 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 " Message", new clarifying attributes have been added " minOccurs" And " maxOccurs" As you might guess from the name, the first ( minOccurs) indicates that this sequence must contain at least one element of type “ phone», « text», « date" And " type", while the next one ( maxOccurs) attribute declares to us that there is at most one such element in our sequence. As a result, when we write our own schemas for any data, we are given the widest choice in how to configure them!

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

4 Write your WSDL

Do you remember that WSDL is our web service? I hope you remember! As we write it, our little web service will run on it. Therefore, I suggest not to mess around.

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

Application/wsdl+xml

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

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

and everything worked great!

I want to warn you right away that our simple web service will have a rather impressive description, so don’t be alarmed, because... Most of the text is obligatory water and, having written it once, you can constantly copy it from one web service to another!

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

Typically, 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 by you to whatever you want, because they play absolutely no role.

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

Once we have defined the web service and specified the entry point for it, we need to bind supported procedures to it:

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

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

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

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

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

5 Our first SOAP server

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

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

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

I hope there is no need to explain what is above the line with the “ini_set” function. Because there it is determined which HTTP headers we will send from the server to the client and the environment is configured. In the line with “ini_set” we disable 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! On 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 its constructor. Now we know that it will be located in the root of the hosting in a file with the self-explanatory name “ smsservice.wsdl.php" In the second line, we tell the SOAP server which class needs to be pulled in order to process the envelope received from the client and return the envelope with the response. As you might have guessed, it is in this class that our only method will be described sendSms. On the third line we start the server! That's it, our server is ready! With which I congratulate us all!

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

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

At this stage, we should be completely satisfied with the resulting server, because We can log the envelopes coming to it and then calmly analyze the incoming data. In order for us to receive anything on the server, we need a client. So let's get to 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 = "Test message 1"; $req->messageList->message->date = "2013-07-21T15:00:00.26"; $req->messageList->message->type = 15; $client = new SoapClient("http://($_SERVER["HTTP_HOST"])/smsservice.wsdl.php", array("soap_version" => SOAP_1_2)); var_dump($client->sendSms($req));

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

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

The following object was returned to me from the server:

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

And this is great, because... Now we know for sure that our server is working and not only works, but can also return some values ​​to the client!

Now let's look at the log that we prudently keep on the server side! In its first part we see the raw data that arrived at the server:

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

This is the envelope. Now you know what it looks like! But it’s unlikely that we’ll be interested in looking at it all the time, so let’s deserialize the object from the log file and see if everything is fine:

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

As you can see, the object was deserialized correctly, for which I want to congratulate us all! Something more interesting awaits us next! Namely, we will send the client to the server not just one SMS message, but a whole pack (to be more precise, three)!

7 Sending complex objects

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

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

Our logs indicate that the following packet was received from the client:

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

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

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

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

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

In this case, we will receive another envelope:

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

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

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

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

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

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

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

Yes, a miracle did not happen! BOGUS– we won’t win! Came to sendSms the object in this case will look like this:

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

As they say – “Almost”! On this (slightly sad) note, I propose to slowly wrap things up and draw some conclusions for ourselves.

8 Conclusion

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

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

We also made some discoveries during our little research:

  • the native SoapClient class does not correctly serialize data structures of the same type in XML;
  • when serializing an array to XML it creates an extra element called Struct;
  • when serializing an object to XML it creates an extra element called BOGUS;
  • BOGUS less evil than Struct due to the fact that the envelope is more compact (extra namespaces are not added to the XML header of the envelope);
  • Unfortunately, the SoapServer class does not automatically validate the envelope data with our XML schema (perhaps other servers do not do this either).