PHP: una classe client universale che utilizza cURL per interrogare Web service attraverso XML

Webservice e cURL I Web service sono strumenti che permettono di scambiare messaggi fra client e server in una rete distribuita, indipendentemente dal linguaggio di programmazione utilizzato. I più diffusi linguaggi per la programmazione web mettono a disposizioni classi e funzioni per la loro implementazione che avviene utilizzando il protocollo SOAP (Simple Object Access Protocol).

Normalmente l’esposizione delle funzioni server avviene attraverso un documento XML che si chiama WSDL (Web Services Description Language). Secondo quanto descritto nel WSDL associato al servizio è possibile interrogare il server attraverso richieste incapsulate in un documento XML e fatte viaggiare attraverso il protocollo HTTP mediante una richiesta di tipo POST.

Quello che ci interessa ora è di poter inviare una richiesta ad un qualsiasi web service SOAP  tramite un documento XML preformattato con i valori richiesti dal server. Possiamo utilizzare la libreria cURL che oggi è inclusa nel pacchetto PHP e ci fornisce le funzioni necessarie ad inviare tramite http una richiesta di tipo POST.

Le proprietà pubbliche sono:

$charsetIl tipo di charset, default: utf-8
$connecttimeoutOpzione cURL Connection time out, default: 10
$contentTypeHeader Content-type default: text/xml
$debugAbilita cURL debug, default: false
$md5_passAbilita MD5 per la password, default: false
$postOpzione cURL Post, default: true
$returntransferOpzione cURL Return transfer, default: true
$ssl_verifyhostOpzione cURL SSL Verify Host, default: true
$ssl_verifypeerOpzione cURL SSL Verify Peer, default: true
$timeoutOpzione cURL Time out, default: 10

e i metodi pubblici sono:

__construct($url)Costruttore, input la url per connettersi al Web service
printExtraHeaders()Stampa gli headers aggiuntivi
resetExtraHeaders()Rimuove tutti gli headers aggiuntivi
sendXML($post_string)Invia al web service, input la stringa XML
setCredentials($user, $pass)Imposta la username e la password (opzionale), input username e password
setExtraHeader($extraHeader)Aggiunge un header, input: stringa con header aggiuntivo
setExtraHeaders($extraHeaders)Aggiunge una serie di header, input: header aggintivi in array

Ecco il codice della classe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
<?php
/**
 * SendXMLToWebService
 * This class is for transmitting an http / POST request to Web service
 * through a XML document
 * 
 * @author Mario Spada <spadamar@spadamar.com> 
 * @copyright Copyright (c) 2015 Mario Spada
 * @license http://opensource.org/licenses/GPL-2.0 GNU Public License
 * @package SendXMLToWebService
 * @version 0.1.0 2015/11/22
 */
 
class SendXMLToWebService {
 
   	/**
	 * cURL option Connection time out
	 * @var int
	 */
	public $connecttimeout = 10;
   	/**
	 * cURL option Time out
	 * @var int
	 */	
	public $timeout = 10;
   	/**
	 * cURL option Return transfer
	 * @var boolean
	 */	
	public $returntransfer = true;
   	/**
	 * cURL option SSL Verify Peer
	 * @var boolean
	 */		
	public $ssl_verifypeer = false;
   	/**
	 * cURL option SSL Verify Host
	 * @var boolean
	 */		
	public $ssl_verifyhost = false;
   	/**
	 * cURL option Post
	 * @var boolean
	 */	
	public $post = true;
   	/**
	 * Header Charset
	 * @var string
	 */	
	public $charset = 'utf-8';
   	/**
	 * Header Content-type
	 * @var string
	 */	
	public $contentType = 'text/xml';
   	/**
	 * Enable MD5 encryption for password
	 * @var boolean
	 */	
	public $md5_pass = false;
   	/**
	 * Enable cURL debug
	 * @var boolean
	 */	
	public $debug = true;
   	/**
	 * Set url (private)
	 * @var string
	 */	
	private $_url;
   	/**
	 * Set username (private)
	 * @var string
	 */	
	private $_user = '';
   	/**
	 * Set password (private)
	 * @var string
	 */	
	private $_pass = '';
   	/**
	 * Extra headers (private)
	 * @var array
	 */	
	private $_extraHeaders = array();
 
	/**
	 * @param string $url url to connect to Web service
	 */
	public function __construct($url) {
		$this->_url = $url;
		$this->_checkRequisites();
	}
	/**
	 * Send XML document to Web Service
	 * @param string $post_string string containing XML document
	 * @return string result string, usually in XML format
	 */
	public function sendXML($post_string) {
		$psLen = strlen($post_string);
		$headers = $this->_getHeaders($psLen);				
 
		$soap_do = curl_init(); 
		curl_setopt($soap_do, CURLOPT_URL,            $this->_url);   
		curl_setopt($soap_do, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); 
		curl_setopt($soap_do, CURLOPT_TIMEOUT,        $this->timeout); 
		curl_setopt($soap_do, CURLOPT_RETURNTRANSFER, $this->returntransfer);
		curl_setopt($soap_do, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer);  
		curl_setopt($soap_do, CURLOPT_SSL_VERIFYHOST, $this->ssl_verifyhost); 
		curl_setopt($soap_do, CURLOPT_POST,           $this->post); 
		curl_setopt($soap_do, CURLOPT_POSTFIELDS,    $post_string); 
		curl_setopt($soap_do, CURLOPT_HTTPHEADER,    $headers);
		if (!empty($this->_user)) {
			curl_setopt($soap_do, CURLOPT_USERPWD, $this->_user.":".$this->_pass);		
		}
		$result = curl_exec($soap_do);
		$err = curl_error($soap_do);
		curl_close ($soap_do);
		if ($err && $this->debug) 
			echo "ERROR: ".$err;
		return $result;
 
	}
	/**
	 * Set username and password
	 * @param string $user username
	 * @param string $pass password
	 */
	public function setCredentials($user, $pass) {
		$this->_user = $user;
		$this->_pass = md5($pass);
		if ($this->md5_pass) {
			$this->_pass = md5($this->_pass);
		}
	}
	/**
	 * Set an additional header
	 * @param string $extraHeader a valid header row
	 */
	public function setExtraHeader($extraHeader) {
		array_push($this->_extraHeaders,$extraHeader);
	}
	/**
	 * Set multiple additional header at once in array format
	 * @param array $extraHeaders an array with multiple header rows
	 */
	public function setExtraHeaders($extraHeaders) {
		$this->_extraHeaders = array_merge($this->_extraHeaders,$extraHeaders);
	}
	/**
	 * Remove all additional headers
	 */
	public function resetExtraHeaders() {
		$this->_extraHeaders = array();
	}
	/**
	 * Prints out all additional headers
	 */
	public function printExtraHeaders() {
		print_r($this->_extraHeaders);
	}
	/**
	 * Compose headers
	 * @param string $psLen length of the XML string 
	 */
	private function _getHeaders($psLen) {
		$headers = array();
		array_push($headers,'Content-Type: '.$this->contentType.'; charset='.$this->charset);
		if (count($this->_extraHeaders) > 0) {
			$headers = array_merge($headers,$this->_extraHeaders);
		}
		array_push($headers,'Content-Length: '.$psLen);
		return $headers;		
	}
	/**
	 * Check if cURL and allow_url_fopen are enabled, check url validity
	 */	
	private function _checkRequisites() {
		if (!function_exists('curl_version'))
			die("cURL is not enabled!");
		if(!ini_get('allow_url_fopen'))
			die("allow_url_fopen is not enabled!");
		if (!filter_var($this->_url, FILTER_VALIDATE_URL)) 
			die("URL is not valid!");
		return true;
	}
 
}
?>

La documentazione della classe è disponibile qui
Il pacchetto completo qui.

Per i test ho utilizzato alcuni web service gratuiti che sono disponibili a questo indirizzo: http://www.webservicex.net. Questo sito è molto interessante e pubblica dettagliatamente tutti i parametri per collegarsi ai Web service, includendo naturalmente l’XML campione per inviare i dati e l’XML che conterrà la risposta.

L’esempio di test della classe che ho scelto utilizza il web service GeoIPService per geolocalizzare un indirizzo IP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
require_once("SendXMLToWebService.class.php");
$xml = <<<STR
<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
  <soap12:Body>
    <GetGeoIP xmlns="http://www.webservicex.net/">
      <IPAddress>8.8.8.8</IPAddress>
    </GetGeoIP>
  </soap12:Body>
</soap12:Envelope>
STR;
$test = new SendXMLToWebService("http://www.webservicex.net/geoipservice.asmx");
$resXML = $test->sendXML($xml);
$dom = new DOMDocument;
$dom->loadXML($resXML);
$IP = $dom->getElementsByTagName('IP');
$ReturnCodeDetails = $dom->getElementsByTagName('ReturnCodeDetails');
$CountryName = $dom->getElementsByTagName('CountryName');
$CountryCode = $dom->getElementsByTagName('CountryCode');
//print_r($resXML);
echo $IP->item(0)->nodeName." = ".$IP->item(0)->nodeValue."<br>\n";
echo $ReturnCodeDetails->item(0)->nodeName." = ".$ReturnCodeDetails->item(0)->nodeValue."<br>\n";
echo $CountryName->item(0)->nodeName." = ".$CountryName->item(0)->nodeValue."<br>\n";
echo $CountryCode->item(0)->nodeName." = ".$CountryCode->item(0)->nodeValue."<br>\n";
?>

Questo è il risultato (utilizzando come IP un DNS di Google):

IP = 8.8.8.8
ReturnCodeDetails = Success
CountryName = United States
CountryCode = USA

Riferimenti: