Subversion Repositories Applications.framework

Rev

Rev 494 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
252 jpm 1
<?php
2
// declare(encoding='UTF-8');
3
/**
4
* Classe client permettant d'interroger des services web REST.
5
*
473 jpm 6
* @category		php 5.2
7
* @package		Framework
252 jpm 8
* @author		Jean-Pascal MILCENT <jpm@tela-botanica.org>
9
* @copyright	Copyright (c) 2010, Tela Botanica (accueil@tela-botanica.org)
473 jpm 10
* @license		CeCILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt>
11
* @license		GNU-GPL <http://www.gnu.org/licenses/gpl.html>
12
* @since		0.2
252 jpm 13
*/
287 jpm 14
class RestClient {
252 jpm 15
	const HTTP_URL_REQUETE_SEPARATEUR = '&';
349 jpm 16
	const HTTP_URL_REQUETE_CLE_VALEUR_SEPARATEUR = '=';
256 jpm 17
	private $http_methodes = array('GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS', 'CONNECT', 'TRACE');
252 jpm 18
	protected $parametres = null;
256 jpm 19
	private $url = null;
20
	private $reponse_entetes = null;
496 eric 21
        private $tracer_ip_source = null;
22
        private $requete_headers = array();
23
 
24
        public function __construct() {
25
        //verifie si paramètre tracer_ip_source est sur TRUE dans config.ini
26
                    if (Config::existe('tracer_ip_source')) {
27
                        //lire la config
28
                        $valeur = Config::get('tracer_ip_source');
29
                        //si oui, active X-Forwarded-For
30
                        $this->setTracerIPSource($valeur);
31
            }
32
            //pas de else, si pas dans la config $tracer_ip_source sera NULL/FALSE
33
         }
473 jpm 34
	//+------------------------------------------------------------------------------------------------------+
252 jpm 35
	// ACCESSEURS
473 jpm 36
 
353 jpm 37
	public function getReponseEntetes() {
256 jpm 38
		return $this->reponse_entetes;
39
	}
473 jpm 40
 
252 jpm 41
	public function getParametre($cle) {
42
		$valeur = (isset($this->parametres[$cle])) ? $this->parametres[$cle] : null;
43
		return $valeur;
44
	}
473 jpm 45
 
252 jpm 46
	public function ajouterParametre($cle, $valeur) {
47
		$this->parametres[$cle] = $valeur;
48
	}
473 jpm 49
 
252 jpm 50
	public function supprimerParametre($cle) {
51
		unset($this->parametres[$cle]);
52
	}
473 jpm 53
 
252 jpm 54
	public function nettoyerParametres() {
55
		$this->parametres = null;
56
	}
496 eric 57
 
58
        public function setTracerIPSource($valeur) {
59
                $this->tracer_ip_source = $valeur;
60
        }
61
 
473 jpm 62
	//+------------------------------------------------------------------------------------------------------+
252 jpm 63
	// MÉTHODES
473 jpm 64
 
252 jpm 65
	public function consulter($url) {
66
		$retour = $this->envoyerRequete($url, 'GET');
67
		return $retour;
68
	}
473 jpm 69
 
252 jpm 70
	public function ajouter($url, Array $donnees) {
71
		$retour = $this->envoyerRequete($url, 'PUT', $donnees);
72
		return $retour;
73
	}
473 jpm 74
 
252 jpm 75
	public function modifier($url, Array $donnees) {
76
		$retour = $this->envoyerRequete($url, 'POST', $donnees);
77
		return $retour;
78
	}
473 jpm 79
 
252 jpm 80
	public function supprimer($url) {
81
		$retour = $this->envoyerRequete($url, 'DELETE');
82
		return $retour;
83
	}
473 jpm 84
 
85
	public function envoyerRequete($url, $mode, Array $donnees = array()) {
86
		$contenu = false;
87
		if (FALSE && function_exists('curl_init') && $mode == 'GET') {
88
			// nous n'activons le wrapper que pour GET pour l'instant
89
			// car l'utilisation de curl pour les autres modes pourrait
90
			// vraisemblablement induire des comportements différents. (test-suite needed)
91
			$contenu = $this->envoyerCurlRequete($url, $mode, $donnees);
92
		} else {
496 eric 93
                        $contenu = $this->envoyerStreamRequete($url, $mode, $donnees);
473 jpm 94
		}
95
		return $contenu;
96
	}
97
 
98
	private function envoyerStreamRequete($url, $mode, Array $donnees = array()) {
496 eric 99
                $this->url = $url;
428 raphael 100
		$contenu = false;
101
		if (! in_array($mode, $this->http_methodes)) {
102
			$e = "Le mode de requête '$mode' n'est pas accepté!";
103
			trigger_error($e, E_USER_WARNING);
104
		} else {
496 eric 105
                    if ($mode == 'GET') {
428 raphael 106
				$this->traiterUrlParametres();
107
			}
496 eric 108
                    $content = http_build_query($donnees, null, self::HTTP_URL_REQUETE_SEPARATEUR);
109
                    $this->requete_headers['http'] = array();
110
                    $this->requete_headers['http']['method'] = $mode;
111
                    $this->requete_headers['http']['header'] = "Content-type: application/x-www-form-urlencoded\r\n";
112
                    if ($this->tracer_ip_source) {
113
                        // ajoute  X-Forwarded-For au header
114
                        $this->requete_headers['http']['header'] .= "X-Forwarded-For: {$_SERVER['REMOTE_ADDR']}, {$_SERVER['SERVER_ADDR']}\r\n";
115
                    }
116
                    $this->requete_headers['http']['header'] .= "User-Agent: {$_SERVER['HTTP_USER_AGENT']} ApiTela/";
117
                    $this->requete_headers['http']['header'] .= Framework::getInfoAppli('nom');
118
                    $this->requete_headers['http']['header'] .= "\r\n";
119
                    $this->requete_headers['http']['content'] = $content;
120
			$contexte = stream_context_create($this->requete_headers);
121
			$flux = @fopen($this->url, 'r', false, $contexte);
473 jpm 122
			if (!$flux) {
123
				$entetesFmt = print_r($this->analyserEntete(), true);
124
				$e = "Echec requête '$mode' : {$this->url}\n".
125
					"Paramètres requête : $content \n".
126
					"Entêtes réponse : $entetesFmt \n";
127
				trigger_error($e, E_USER_WARNING);
128
			} else {
129
				// Informations sur les en-têtes et métadonnées du flux
130
				$this->reponse_entetes = stream_get_meta_data($flux);
428 raphael 131
 
473 jpm 132
				// Contenu actuel de $url
133
				$contenu = stream_get_contents($flux);
134
 
135
				fclose($flux);
136
			}
137
			$this->traiterEntete();
138
		}
139
		$this->reinitialiser();
140
		return $contenu;
141
	}
142
 
143
	private function envoyerCurlRequete($url, $mode, Array $donnees = array()) {
144
		$this->url = $url;
145
		$contenu = false;
146
		if (! in_array($mode, $this->http_methodes)) {
147
			$e = "Le mode de requête '$mode' n'est pas accepté!";
148
			trigger_error($e, E_USER_WARNING);
149
		} else {
150
			if ($mode == 'GET') {
151
				$this->traiterUrlParametres();
152
			}
153
 
428 raphael 154
			$ch = curl_init($this->url);
155
			curl_setopt($ch, CURLOPT_HEADER, TRUE);
156
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
438 raphael 157
			curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
473 jpm 158
			if ($mode == 'POST') {
159
				curl_setopt($ch, CURLOPT_POST, TRUE);
160
			} elseif ($mode == 'PUT') {
161
				curl_setopt($ch, CURLOPT_PUT, TRUE);
162
			} elseif($mode == 'DELETE') {
163
				curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
164
			}
428 raphael 165
 
473 jpm 166
			$content = '';
167
			if ($donnees) {
168
				$content = http_build_query($donnees, null, self::HTTP_URL_REQUETE_SEPARATEUR);
169
				curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
170
			}
428 raphael 171
 
172
			$flux = curl_exec($ch);
173
			curl_close($ch);
174
 
175
			if (!$flux) {
176
				$this->reponse_entetes = $http_response_header;
473 jpm 177
				$entetesFmt = print_r($this->analyserEntete(), true);
178
				$msgTpl = "Echec requête (CURL) '%s' : %s\nParamètres requête : %s \nEntêtes réponse : %s \n";
179
				$msg = sprintf($msgTpl, strtoupper($mode), $this->url, $content, $entetesFmt);
180
				trigger_error($msg, E_USER_WARNING);
428 raphael 181
			} else {
438 raphael 182
				// attention, CURLOPT_FOLLOWLOCATION amène le stream à contenir plusieurs section d'header HTTP successives
183
				$t = explode("\r\n\r\n", $flux);
184
				$contenu = array_splice($t, -1);
185
				$dernier_entete = array_splice($t, -1);
186
				$contenu = $contenu[0];
187
				$dernier_entete = $dernier_entete[0];
188
 
428 raphael 189
				// XXX: mimic stream_get_meta_data() (ce qui n'est pas très propre, le code appelant ferait mieux de se mettre à jour)
438 raphael 190
				$this->reponse_entetes = array('wrapper_data' => explode("\r\n", $dernier_entete));
428 raphael 191
			}
192
			$this->traiterEntete();
193
		}
194
		$this->reinitialiser();
195
		return $contenu;
196
	}
197
 
257 jpm 198
	private function traiterUrlParametres() {
252 jpm 199
		$parametres = array();
200
		if (count($this->parametres) > 0) {
201
			foreach ($this->parametres as $cle => $valeur) {
350 jpm 202
				$cle = rawurlencode($cle);
203
				$valeur = rawurlencode($valeur);
349 jpm 204
				$parametres[] = $cle.self::HTTP_URL_REQUETE_CLE_VALEUR_SEPARATEUR.$valeur;
252 jpm 205
			}
206
			$url_parametres = implode(self::HTTP_URL_REQUETE_SEPARATEUR, $parametres);
257 jpm 207
			$this->url = $this->url.'?'.$url_parametres;
252 jpm 208
		}
209
	}
473 jpm 210
 
256 jpm 211
	private function traiterEntete() {
212
		$infos = $this->analyserEntete();
252 jpm 213
		$this->traiterEnteteDebogage($infos);
214
	}
473 jpm 215
 
256 jpm 216
	private function analyserEntete() {
473 jpm 217
		$entetes = $this->reponse_entetes;
256 jpm 218
		$infos = array('date' => null, 'uri' => $this->url, 'debogages' => null);
473 jpm 219
 
252 jpm 220
		if (isset($entetes['wrapper_data'])) {
221
			$entetes = $entetes['wrapper_data'];
222
		}
223
		foreach ($entetes as $entete) {
224
			if (preg_match('/^X_REST_DEBOGAGE_MESSAGES: (.+)$/', $entete, $match)) {
225
				$infos['debogages'] = json_decode($match[1]);
226
			}
227
			if (preg_match('/^Date: .+ ([012][0-9]:[012345][0-9]:[012345][0-9]) .*$/', $entete, $match)) {
228
				$infos['date'] = $match[1];
229
			}
230
		}
231
		return $infos;
232
	}
473 jpm 233
 
234
	private function traiterEnteteDebogage($entetes) {
252 jpm 235
		if (isset($entetes['debogages'])) {
236
			$date = $entetes['date'];
237
			$uri = $entetes['uri'];
238
			$debogages = $entetes['debogages'];
239
			foreach ($debogages as $debogage) {
240
				$e = "DEBOGAGE : $date - $uri :\n$debogage";
241
				trigger_error($e, E_USER_NOTICE);
242
			}
243
		}
244
	}
473 jpm 245
 
252 jpm 246
	private function reinitialiser() {
247
		$this->nettoyerParametres();
248
	}
249
}