Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
422 aurelien 1
<?php
2463 jpm 2
// declare(encoding='UTF-8');
3
/**
4
 * Classe principale de gestion des web-services.
5
 *
6
 * @internal   Mininum PHP version : 5.2
7
 * @category   CEL
8
 * @package    Services
9
 * @subpackage Index
10
 * @version    0.1
11
 * @author     Mathias CHOUET <mathias@tela-botanica.org>
12
 * @author     Jean-Pascal MILCENT <jpm@tela-botanica.org>
13
 * @author     Aurelien PERONNET <aurelien@tela-botanica.org>
14
 * @license    GPL v3 <http://www.gnu.org/licenses/gpl.txt>
15
 * @license    CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
16
 * @copyright  1999-2014 Tela Botanica <accueil@tela-botanica.org>
17
 */
422 aurelien 18
class JRest {
19
 
20
 	/** Parsed configuration file */
21
    private $config;
22
 
23
	/** The HTTP request method used. */
24
	private $method = 'GET';
25
 
26
	/** The HTTP request data sent (if any). */
27
	private $requestData = NULL;
28
 
29
	/** Array of strings to convert into the HTTP response. */
30
	private $output = array();
31
 
32
	/** Nom resource. */
33
	private $resource = NULL;
34
 
35
	/** Identifiant unique resource. */
36
	private $uid = NULL;
37
 
38
	/**
39
	 * Constructor. Parses the configuration file "JRest.ini", grabs any request data sent, records the HTTP
40
	 * request method used and parses the request URL to find out the requested resource
41
	 * @param str iniFile Configuration file to use
42
	 */
3473 killian 43
	public function __construct($iniFile = 'jrest.ini.php') {
422 aurelien 44
		$this->config = parse_ini_file($iniFile, TRUE);
45
		if (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER['QUERY_STRING'])) {
46
			if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) {
47
				$this->requestData = '';
48
				$httpContent = fopen('php://input', 'r');
49
				while ($data = fread($httpContent, 1024)) {
50
					$this->requestData .= $data;
51
				}
52
				fclose($httpContent);
53
			}
54
			if (strlen($_SERVER['QUERY_STRING']) == 0) {
55
				$len = strlen($_SERVER['REQUEST_URI']);
56
			} else {
57
				$len = -(strlen($_SERVER['QUERY_STRING']) + 1);
58
			}
2463 jpm 59
 
539 jpm 60
			$urlString = '';
61
			if  (substr_count($_SERVER['REQUEST_URI'], $this->config['settings']['baseURL']) > 0) {
62
				$urlString = substr($_SERVER['REQUEST_URI'], strlen($this->config['settings']['baseURL']), $len);
63
			} else if (substr_count($_SERVER['REQUEST_URI'], $this->config['settings']['baseAlternativeURL']) > 0) {
64
				$urlString = substr($_SERVER['REQUEST_URI'], strlen($this->config['settings']['baseAlternativeURL']), $len);
65
			}
422 aurelien 66
 
67
			$urlParts = explode('/', $urlString);
68
 
69
			if (isset($urlParts[0])) $this->resource = $urlParts[0];
70
			if (count($urlParts) > 1 && $urlParts[1] != '') {
71
				array_shift($urlParts);
72
				foreach ($urlParts as $uid) {
73
					if ($uid != '') {
74
						$this->uid[] = urldecode($uid);
75
					}
76
				}
77
			}
78
 
79
			$this->method = $_SERVER['REQUEST_METHOD'];
80
		} else {
81
			trigger_error('I require the server variables REQUEST_URI, REQUEST_METHOD and QUERY_STRING to work.', E_USER_ERROR);
82
		}
83
	}
84
 
85
	/**
86
	 * Execute the request.
87
	 */
88
	function exec() {
89
		switch ($this->method) {
90
			case 'GET':
91
				$this->get();
92
				break;
93
			case 'POST':
94
				$this->post();
95
				break;
96
			case 'DELETE':
97
				$this->delete();
98
				break;
99
			case 'PUT':
100
				$this->add();
101
				break;
102
		}
103
	}
104
 
105
	/**
106
	 * Execute a GET request. A GET request fetches a list of resource when no resource name is given, a list of element
107
	 * when a resource name is given, or a resource element when a resource and resource unique identifier are given. It does not change the
108
	 * database contents.
109
	 */
110
	private function get() {
111
		if ($this->resource) {
112
			$resource_file = 'services/'.ucfirst($this->resource).'.php';
113
			$resource_class = ucfirst($this->resource);
114
			if (file_exists($resource_file))  {
115
				include_once $resource_file;
116
				if (class_exists($resource_class)) {
117
					$service = new $resource_class($this->config);
118
					if ($this->uid) { // get a resource element
119
						if (method_exists($service, 'getElement')) {
120
							$service->getElement($this->uid);
121
						}
122
					} elseif (method_exists($service, 'getRessource')) { // get all elements of a ressource
123
						$service->getRessource();
124
					}
125
				}
126
			}
127
		} else { // get resources
128
			// include set.jrest.php, instanticiation et appel
129
		}
130
	}
131
 
132
	private function post() {
133
	   	$pairs = array();
134
		// Récupération des paramètres passés dans le contenu de la requête HTTP (= POST)
135
	   	if ($this->requestData) {
136
			$pairs = $this->parseRequestData();
137
		}
138
 
139
		// Ajout des informations concernant l'upload de fichier passées dans la variable $_FILE
140
		if(isset($_FILES)) {
141
			foreach ($_FILES as $v) {
142
				$pairs[$v['name']] = $v;
143
			}
144
 
145
			// Ne pas effacer cette ligne ! Elle est indispensable pour les services du Carnet en ligne
146
			// qui n'utilisent que le tableau pairs dans les posts
1920 aurelien 147
			$pairs = array_merge($_POST, $pairs);
422 aurelien 148
		}
149
 
150
		// gestion du contenu du post
151
		if(isset($_POST))
152
		{
153
			// Safari ne sait pas envoyer des DELETE avec gwt...
154
			// Nous utilisons le parametre "action" passé dans le POST qui doit contenir DELETE pour lancer la supression
1015 aurelien 155
			if (isset($pairs['action']) && $pairs['action'] == 'DELETE') {
422 aurelien 156
				$this->delete();
157
				return;
158
			}
159
 
160
			if (count($pairs) != 0) {
161
				if ($this->uid) { // get a resource element
162
					$resource_file = 'services/'.ucfirst($this->resource).'.php';
163
					$resource_class = ucfirst($this->resource);
164
					if (file_exists($resource_file)) {
165
						include_once $resource_file;
166
						if (class_exists($resource_class)) {
167
							$service = new $resource_class($this->config);
168
							if (method_exists($service,'updateElement')) { // Update element
1628 raphael 169
								$ret_value = false;
170
								try {
171
									// TODO : a voir le retour ...
172
									$ret_value = $service->updateElement($this->uid, $pairs);
173
									if($ret_value) $this->created();
174
									else $this->badRequest();
175
								} catch (Exception $e) {
176
									$this->badRequest($e);
422 aurelien 177
								}
178
							}
179
						}
180
					}
181
				} else { // get all elements of a ressource
182
					$this->add($pairs);
183
				}
184
			} else {
185
				$this->lengthRequired();
186
			}
187
		}
188
	}
189
 
190
	private function delete() {
191
		$resource_file = 'services/'.ucfirst($this->resource).'.php';
192
		$resource_class = ucfirst($this->resource);
193
		if (file_exists($resource_file)) {
194
			include_once $resource_file;
195
			if (class_exists($resource_class)) {
196
				$service = new $resource_class($this->config);
197
				if ($this->uid) { // get a resource element
198
		 			if (method_exists($service, 'deleteElement')) { // Delete element
199
						if ($service->deleteElement($this->uid)) {
200
							$this->noContent();
201
						}
202
	 				}
203
				}
204
			}
205
		}
206
	}
207
 
208
	private function add($pairs = null) {
209
		if (is_null($pairs)) {
210
			$pairs = array();
211
			// Récupération des paramètres passés dans le contenu de la requête HTTP (= POST)
212
			// FIXME : vérifier que l'on récupère bien les données passées par PUT
213
		   	if ($this->requestData) {
214
				$pairs = $this->parseRequestData();
215
			}
216
		}
217
 
218
		if (count($pairs) != 0) {
219
			$resource_file = 'services/'.ucfirst($this->resource).'.php';
220
			$resource_class = ucfirst($this->resource);
221
			if (file_exists($resource_file)) {
222
				include_once $resource_file;
223
				if (class_exists($resource_class)) {
224
					$service = new $resource_class($this->config);
225
					if (method_exists($service,'createElement')) { // Create a new element
1628 raphael 226
						$ret_value = false;
227
						try {
228
							$ret_value = $service->createElement($pairs);
229
							if($ret_value) $this->created();
230
							else $this->badRequest();
231
						} catch (Exception $e) {
232
							$this->badRequest($e);
422 aurelien 233
						}
234
					}
235
				}
236
			}
237
		} else {
238
			$this->lengthRequired();
239
		}
240
	}
241
 
242
	/**
243
	 * Parse the HTTP request data.
244
	 * @return str[] Array of name value pairs
245
	 */
246
	private function parseRequestData() {
247
		$values = array();
248
		$pairs = explode('&', $this->requestData);
249
		foreach ($pairs as $pair) {
250
			$parts = explode('=', $pair);
251
			if (isset($parts[0]) && isset($parts[1])) {
252
				$parts[1] = rtrim(urldecode($parts[1]));
253
				$values[$parts[0]] = $parts[1];
254
			}
255
		}
256
		return $values;
257
	}
258
 
259
	/**
260
	 * Send a HTTP 201 response header.
261
	 */
262
	private function created($url = FALSE) {
263
		header('HTTP/1.0 201 Created');
264
		if ($url) {
265
			header('Location: '.$url);
266
		}
267
	}
268
 
269
	/**
270
	 * Send a HTTP 204 response header.
271
	 */
272
	private function noContent() {
273
		header('HTTP/1.0 204 No Content');
274
	}
275
 
276
	/**
277
	 * Send a HTTP 400 response header.
278
	 */
1628 raphael 279
	private function badRequest(Exception $e = NULL) {
422 aurelien 280
		header('HTTP/1.0 400 Bad Request');
1628 raphael 281
		if($e) echo $e->getMessage();
422 aurelien 282
	}
283
 
284
	/**
285
	 * Send a HTTP 401 response header.
286
	 */
287
	private function unauthorized($realm = 'JRest') {
288
		if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
289
			header('WWW-Authenticate: Basic realm="'.$realm.'"');
290
		}
291
		header('HTTP/1.0 401 Unauthorized');
292
	}
293
 
294
	/**
295
	 * Send a HTTP 404 response header.
296
	 */
297
	private function notFound() {
298
		header('HTTP/1.0 404 Not Found');
299
	}
300
 
301
	/**
302
	 * Send a HTTP 405 response header.
303
	 */
304
	private function methodNotAllowed($allowed = 'GET, HEAD') {
305
		header('HTTP/1.0 405 Method Not Allowed');
306
		header('Allow: '.$allowed);
307
	}
308
 
309
	/**
310
	 * Send a HTTP 406 response header.
311
	 */
312
	private function notAcceptable() {
313
		header('HTTP/1.0 406 Not Acceptable');
314
		echo join(', ', array_keys($this->config['renderers']));
315
	}
316
 
317
	/**
318
	 * Send a HTTP 411 response header.
319
	 */
320
	private function lengthRequired() {
321
		header('HTTP/1.0 411 Length Required');
322
	}
323
 
324
	/**
325
	 * Send a HTTP 500 response header.
326
	 */
327
	private function internalServerError() {
328
		header('HTTP/1.0 500 Internal Server Error');
329
	}
330
}
331
?>