/[openfoncier]/trunk/services/REST/services.php
ViewVC logotype

Diff of /trunk/services/REST/services.php

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 559 by mlimic, Tue Oct 30 17:05:34 2012 UTC revision 728 by mlimic, Fri Nov 16 16:59:46 2012 UTC
# Line 1  Line 1 
1  <?php  <?php
2    /**
3  /*   * Ce fichier permet de déclarer la classe Services, classe de base pour
4   * The base class for all of the services provided through the REST interface.   * toutes les ressources exposées à travers l'interface REST.
  *  
  * It creates an instance of utils which establish DB connection. It also  
  * contains methods for checking the correctness of the format of the  
  * incomming messages.  
  * It contains the methods post, put, get, delete that exhibit upon a REST  
  * call the default behaviour - "request can't be processed since the service  
  * is not available.  
  *  
  *  
  * @author: Mirna Limic <[email protected]>  
  * @uses ./restler The RESTLER framework. When using PUT and POST http methods  
  * the function receiving the incomming data MUST contain a parameter called  
  * request_data. The RESTLER framework stores the incoming JSON string converted  
  * into an array in the request_data parameter.  
  *  
  * Date: 15/10/2012  
  * Follow-up:  
  * Bugs: Unknown  
5   *   *
6     * @package openfoncier
7     * @version SVN : $Id$
8   */   */
9    
10      class Services {  /**
11                     * Cette classe instancie la classe utils pour établir une connexion à la base
12                     * de données et accéder aux méthodes principales de cette classe. Elle contient
13                  /*   * également les méthodes permettant de vérifier l'intégrité REST des requêtes
14                   * Constructor   * reçues. On trouve la définition du comportement par défaut pour les méthodes
15                   *   * POST, PUT, GET, DELETE. Les méthodes de retour/réponse à la requête REST sont
16                   * Initializes the contents attribute to an empty array, the   * aussi définies ici.
17                   * mdtry_groups attribute to null, and the metier_manager attribute   */
18                   * to null.  class Services {
19                   */  
20          protected function __construct() {      /**
21                          /* constant 'REST_REQUEST' indicates that the processing was       * Constructeur
22                           * initiated via a REST request. It is used inside of om_dbform       *
23                           * class to avoid premature ending of script execution due to       * Initialise les attributs de la classe.
24                           * a die() issued by a DB error or another source.       */
25                           */      protected function __construct() {
26                          if (!defined('REST_REQUEST')) {  
27                                  define('REST_REQUEST', true);          /**
28                          }           * Définition de la constante 'REST_REQUEST' qui est un marqueur
29                          /* contents is populated by derived classes. The contents array           * permettant d'indiquer que nous sommes dans le contexte d'une requête
30                           * contains the parts of the message received via a REST request in           * REST. Par exemple, ce marqueur permet de ne pas afficher les erreurs
31                           * JSON format. It is an associative array. It is used in verifying           * lors des traitements dans om_dbform.class.php.
32                           * the completeness of the format of the request (see functions           */
33                           * requestValid and dataComplete).          if (!defined('REST_REQUEST')) {
34                           */              define('REST_REQUEST', true);
35              $this->contents = array();          }
36                          /* metier_manager points to one of the xxxManager classes that do  
37                           * the processing of the data. It should be instatiated by one of          /**
38                           * the derived classes.           * Cet attribut contient les données JSON reçues dans la requête REST.
39                           */           * C'est un tableau associatif. Il est utilisé lors de la vérification
40                          $this->metier_manager = null;           * de la validité du format de la requête REST (voir la méthode
41                          /* mdtry_grps is used in checking the completeness (validity) of           * requestValid).
42                           * a request's format. It can be a 2D array, or a 1D array or a mix.           */
43                           * Each element of mdtry_groups, called a group, is treated as an          $this->contents = array();
44                           * array. All strings found in a group have to be present in the  
45                           * received JSON data. No two strings coming from different groups          /**
46                           * can be found in the JSON data. I.e. the groups are disjoint.           * @var resource Cet attribut est l'intance du metier_manager qui permet
47                           * mdtry_grps are to be populated by a derived class.           * d'effectuer le traitement sur les données. L'instanciation est faite
48                           */           * dans une des classes enfants en fonctione de la ressource.
49                          $this->mdtry_grps = null;           */
50            $this->metier_manager = null;
51    
52            /**
53             * @todo XXX Traduire ce commentaire
54             *
55             * mdtry_grps is used in checking the completeness (validity) of
56             * a request's format. It can be a 2D array, or a 1D array or a mix.
57             * Each element of mdtry_groups, called a group, is treated as an
58             * array. All strings found in a group have to be present in the
59             * received JSON data. No two strings coming from different groups
60             * can be found in the JSON data. I.e. the groups are disjoint.
61             * mdtry_grps are to be populated by a derived class.
62             */
63            $this->mdtry_grps = null;
64    
65        }
66    
67        /**
68         * Destructeur
69         *
70         * Détruit les variables.
71         */
72        protected function __destruct() {
73    
74            // Si un metiermanager est défini alors on le détruit
75            if ($this->metier_manager) {
76                unset($this->metier_manager);
77            }
78    
79        }
80    
81        /**
82         * Cette méthode vérifie si le format des données JSON reçues dans la
83         * requête REST est valide.
84         *
85         * @param mixed $data Le tableau contenant les données reçues
86         * @param mixed $optional Une liste des éléments pour lequel l'attribut
87         * contents est autorisé à avoir une valeur vide
88         * @return bool Renvoi true si le format des données est valide sinon false
89         */
90        protected function requestValid($data, $optional = null) {
91    
92            // Si le tableau est vide alors on retourne false
93            if (count($data) == 0) {
94                return false;
95            }
96    
97            // Remplissage de l'attribut contents avec les données reçues
98            foreach (array_keys($this->contents) as $elem) {
99                if (isset($data[$elem])) {
100                    $this->contents[$elem] = $data[$elem];
101                }
102            }
103    
104            // Vérification que toutes les données nécessaires sont présentes à
105            // moins qu'elle soit optionnelle
106            foreach ($this->contents as $key => $value) {
107                // Si la valeur est vide
108                if (empty($value)) {
109                    // Si cette clé est optionnelle alors on passe à l'itération
110                    // suivante
111                    if ($optional && in_array($key, $optional)) {
112                        continue;
113                    }
114                    // Si cette clé n'est pas optionnelle alors on retourne false
115                    return false;
116                }
117            }
118    
119            // On retourne true
120            return true;
121    
122        }
123    
124        /**
125         * @todo XXX Traduire et commenter cette méthode
126         *
127         * Return the index of the mandatory group into which the request belongs.
128         * Returns the index of the group that corresponds to the format
129         * of the JSON data received via REST interface.
130         * All strings found in a mandatory group have to be present
131         * in the received JSON data. No two strings coming from
132         * different groups can be found in the JSON data. I.e. the
133         * groups are disjoint.
134         * Mandatory groups are useful when one http method (PUT, ...) can be
135         * used to process several different requests.
136         *
137         * @param mixed $data the array containing the data received via REST
138         * @return int index of the group starting with index 0 or -1 if no
139         * group could be found, or conflicting data found.
140         */
141        protected function requestMdtrGroup(&$data) {
142            // case of mandatory groups, i.e. multiple requests possible to the same source
143            if (!$this->mdtry_grps) {
144                return -1;
145          }          }
146                            $lengroups = count($this->mdtry_grps);
147                            $group_idx = -1;
148                  /*          for ($i = 0; $i < $lengroups; $i++) {
149                   * Destructor              $group = $this->mdtry_grps[$i];
150                   *              if (!is_array($group)) {
151                   * Destroys the xxxManager instance.                  $group = array($group);
152                   */              }
153          protected function __destruct() {              // position of the element, $elem, of a $group that is currently being considered
154                          if ($this->metier_manager) {              $pos = 0;
155                                  unset($this->metier_manager);              foreach($group as $elem) {
156                          }                  if (isset($data[$elem]) && !empty($data[$elem])) {
157                        if ($group_idx != -1 && $group_idx != $i) { // The keys from disjoint groups found in data
158                            return -1;
159                        }
160                        if ($group_idx < 0 && $pos > 0) {
161                            // not all keys in a group found inside data
162                            return -1;
163                        }
164                        $group_idx = $i;
165                    } else { // Not all strings in a group found insidee of data, error
166                        if ($group_idx == $i) {
167                            return -1;
168                        }
169                    }
170                    $pos++;
171                }
172                
173          }          }
174            return $group_idx;
175        }
176    
177        /**
178         * Cette méthode permet de définir le traitement du POST sur une requête
179         * REST. Elle doit être surchargée par la ressource si nécessaire. Le
180         * comportement par défaut est de retourner une erreur 400.
181         *
182         * @param mixed $request_data Les données JSON reçues (voir @uses)
183         *
184         * @uses ./restler La librairie restler, lors de l'utilisation des méthodes
185         * PUT et POST, la méthode qui reçoit les données entrantes DOIT contenir
186         * un paramètre appellé request_data. En effet la librairie stocke la chaine
187         * JSON reçue convertie en un tableau dans le paramètre request_data.
188         */
189        public function post($request_data) {
190            return $this->sendHttpCode(400, "La méthode POST n'est pas disponible sur cette ressource.");
191        }
192    
193        /**
194         * Cette méthode permet de définir le traitement du GET sur une requête
195         * REST. Elle doit être surchargée par la ressource si nécessaire. Le
196         * comportement par défaut est de retourner une erreur 400.
197         *
198         * @param string $id L'identifiant de la ressource
199         */
200        public function get($id) {
201            return $this->sendHttpCode(400, "La méthode GET n'est pas disponible sur cette ressource.");
202        }
203    
204        /**
205         * Cette méthode permet de définir le traitement du PUT sur une requête
206         * REST. Elle doit être surchargée par la ressource si nécessaire. Le
207         * comportement par défaut est de retourner une erreur 400.
208         *
209         * @param mixed $request_data Les données JSON reçues (voir @uses)
210         * @param string $id L'identifiant de la ressource
211         *
212         * @uses ./restler La librairie restler, lors de l'utilisation des méthodes
213         * PUT et POST, la méthode qui reçoit les données entrantes DOIT contenir
214         * un paramètre appellé request_data. En effet la librairie stocke la chaine
215         * JSON reçue convertie en un tableau dans le paramètre request_data.
216         */
217        public function put($request_data, $id) {
218            return $this->sendHttpCode(400, "La méthode PUT n'est pas disponible sur cette ressource.");
219        }
220    
221        /**
222         * Cette méthode permet de définir le traitement du DELETE sur une requête
223         * REST. Elle doit être surchargée par la ressource si nécessaire. Le
224         * comportement par défaut est de retourner une erreur 400.
225         *
226         * @param string $id L'identifiant de la ressource
227         */
228        public function delete($id) {
229            return $this->sendHttpCode(400, "La méthode DELETE n'est pas disponible sur cette ressource.");
230        }
231    
232        /**
233         * Cette méthode envoi une réponse en fonction du résultat du traitement
234         * de la requête.
235         *
236         * @param string $result Le résultat du traitement
237         * @param string $KO_msg Le message additionnel à envoyer en cas d'erreur
238         * @param string $OK_msg Le message additionnel à envoyer en cas de succès
239         *
240         * @todo XXX Vérifier la logique de traitement de cette méthode
241         */
242        protected function sendReply($result, $msg) {
243    
244            // Si le résultat de la requête n'est pas correct
245            if ($result != 'OK') {
246    
247                // Si il y a eut un problème avec les données, alors on retourne un
248                // code 400
249                if ($result == 'BAD_DATA') {
250                    return $this->sendHttpCode(400, $msg);
251                }
252    
253                // Si il y a eut un problème dans le traitement, alors on retourne
254                // un code 500 avec le message additionnel
255                return $this->sendHttpCode(500, $msg);
256    
                   
                 /*  
                  * Verifies that $this->contents contains non-empty data.  
                  * @param mixed $optional An array of strings for which $this->contents  
                  * is allowed to have an empty value  
                  * @return bool returns true if contents has no disallowed empty values,  
                  * false otherwise.  
                  */  
                 private function dataComplete($optional = null) {  
                         foreach ($this->contents as $key => $value) {  
                                 if (empty($value)) {  
                                         // check if the key is optional  
                                         if ($optional && in_array($key, $optional)) {  
                                                 continue;  
                                         }  
                                         return false;  
                                 }  
                         }  
                         return true;  
                 }  
   
   
                 /*  
                  * Verifies if the format of the JSON data received via REST  
                  * is valid.  
                  * @param mixed $data the array containing the data received via REST  
                  * @param mixed $optional An array of strings for which $this->contents  
                  * is allowed to have an empty value  
                  * @return bool returns true if contents has no disallowed empty values,  
                  * false otherwise.  
                  */  
                 protected function requestValid($data, $optional = null) {  
                         if (count($data) == 0) {  
                             return false;  
                         }  
                         // populate the contents array, where data could be coming from  
                         // a REST request, for example  
                         foreach (array_keys($this->contents) as $elem) {  
                             if (isset($data[$elem])) {  
                                 $this->contents[$elem] = $data[$elem];  
                             }      
                         }  
                         // check all data is present, unless it's presence is optional  
                         return $this->dataComplete($optional);  
             }  
   
                   
                 /*  
                  * Return the index of the mandatory group into which the request belongs.  
                  *  
                  * Returns the index of the group that corresponds to the format  
                  * of the JSON data received via REST interface.  
                  * All strings found in a mandatory group have to be present  
                  * in the received JSON data. No two strings coming from  
                  * different groups can be found in the JSON data. I.e. the  
                  * groups are disjoint.  
                  * Mandatory groups are useful when one http method (PUT, ...) can be  
                  * used to process several different requests.  
                  * @param mixed $data the array containing the data received via REST  
                  * @return int index of the group starting with index 0 or -1 if no  
                  * group could be found, or conflicting data found.  
                  */  
                 protected function requestMdtrGroup(&$data) {  
                         // case of mandatory groups, i.e. multiple requests possible to the same source  
                         if (!$this->mdtry_grps) {  
                                 return -1;  
                         }  
                         $lengroups = count($this->mdtry_grps);  
                         $group_idx = -1;  
                         for ($i = 0; $i < $lengroups; $i++) {  
                                 $group = $this->mdtry_grps[$i];  
                                 if (!is_array($group)) {  
                                         $group = array($group);  
                                 }  
                                 // position of the element, $elem, of a $group that is currently being considered  
                                 $pos = 0;  
                                 foreach($group as $elem) {  
                                         if (isset($data[$elem]) && !empty($data[$elem])) {  
                                                 if ($group_idx != -1 && $group_idx != $i) { // The keys from disjoint groups found in data  
                                                         return -1;  
                                                 }  
                                                 if ($group_idx < 0 && $pos > 0) {  
                                                         // not all keys in a group found inside data  
                                                         return -1;  
                                                 }  
                                                 $group_idx = $i;  
                                         } else { // Not all strings in a group found insidee of data, error  
                                                 if ($group_idx == $i) {  
                                                         return -1;  
                                                 }  
                                         }  
                                         $pos++;  
                                 }  
                                   
                         }  
                         return $group_idx;  
                 }  
                   
                   
                 /*  
                  * Send a reply depending on the result of processing of a request.  
                  *  
                  * @param string $result The result of the processing.  
                  * @param string $KO_msg String containing the error that dappened  
                  * during the processing of valid request data.  
                  */  
                 protected function sendReply($result, $KO_msg = null, $OK_msg = null) {  
                         // if there were any errors, return an error code  
                         if ($result != 'OK') {  
                                 // if there was a problem with the data itself, send the error info  
                                 if ($result != 'KO') {  
                                         return $this->sendHttpCode(400, $result);  
                                 }  
                                 // problem in processing the data  
                                 return $this->sendHttpCode(500, $KO_msg);  
                         }  
                           
                         return $this->sendHttpCode(200, $OK_msg);  
                 }  
                   
                   
                 /*  
                  * Sends the return http status code.  
                  * @param string|int $code The code to send  
                  * @param string $msg The additional error message that can be added  
                  * to the return ff it is not empty. It is added in parentheses.  
                  */  
                 protected function sendHttpCode($code, $msg = '') {  
                         return $this->HTTPSTATUS($code, $msg);  
                 }  
                   
                   
                 /*  
                  * Called when the GET http method is used. If not overloaded,  
                  * it is the default functionality, i.e. GET unsupported.  
                  * Note: the parameter MUST be called *request_data* due  
                  * to the way RESTLER framework does the JSON to parameter  
                  * mapping.  
                  * @param mixed $request_data The request data, and MUST have  
                  * that name.  
                  * @return mixed The array containing the response.  
                  */  
         protected function post($request_data) {  
             return $this->sendHttpCode(400, 'Unsupported.');  
257          }          }
                   
258    
259                  /*          // Si le résultat de la requête est correct, alors on retourne un code
260                   * Called when the GET http method is used. If not overloaded,          // 200 et le message additionnel
261                   * it is the default functionality, i.e. GET unsupported.          return $this->sendHttpCode(200, $msg);
262                   * @param string $id The identifier of a dossier  
263                   * @return mixed The array containing the response.      }
264                   */  
265          protected function get($id) {      /**
266                          return $this->sendHttpCode(400, 'Unsupported.');       * Cette méthode permet de retourner la réponse à la requête REST.
267         *
268         * @param int|string $http_code Le code HTTP à envoyer
269         * @param string $message Le message additionnel à envoyer
270         * @return mixed En-tête HTTP et tableau résultat de la requête REST
271         *
272         * @todo XXX Modifier le tableau de retour pour qu'il ressemble au tableau
273         * de retour d'erreur fourni par restler lors d'un 404 par exemple
274         */
275        protected function sendHttpCode($http_code, $message = '') {
276    
277            // Définition du protocole HTTP
278            $http_protocol = "HTTP/1.0";
279            if (isset($_SERVER['SERVER_PROTOCOL'])
280                && stripos($_SERVER['SERVER_PROTOCOL'],"HTTP") >= 0) {
281                $http_protocol = $_SERVER['SERVER_PROTOCOL'];
282          }          }
283    
284                            // Définition des codes HTTP
285                  /*          $http = array(
286                   * Called when the PUT http method is used. If not overloaded,              200 => '200 OK',
287                   * it is the default functionality, i.e. PUT unsupported.              201 => '201 Created',
288                   * @param mixed $request_data The request data, and MUST have              204 => '204 No Content',
289                   * that name.              400 => '400 Bad Request',
290                   * @param string $id The identifier of a dossier              401 => '401 Unauthorized',
291                   * @return mixed The array containing the response.              403 => '403 Forbidden',
292                   */                          404 => '404 Not Found',
293          protected function put($request_data, $id) {              409 => '409 Conflict',
294              return $this->sendHttpCode(400, 'Unsupported.');              500 => '500 Internal Server Error',
295            );
296    
297            // Gestion du paramètre $http_code - les types int et string peuvent
298            // être reçus
299            if (!is_numeric($http_code)) {
300                $http_code = intval($http_code);
301          }          }
302    
303                            // Envoi de l'en-tête HTTP
304                  /*          header($http_protocol." ".$http[$http_code]);
305                   * Called when the DELETE http method is used. If not overloaded,  
306                   * it is the default functionality, i.e. DELETE unsupported.          // Retour du tableau résultat
307                   * @param string $id The identifier of a dossier          return array(
308                   * @return mixed The array containing the response.              'http_code' => $http_code,
309                   */              'http_code_message' => $http[$http_code],
310                'message' => $message,
311          protected function delete($id) {          );
312              return $this->sendHttpCode(400, 'Unsupported.');  
         }    
   
   
                 /*  
                  * Returns the response to a REST request as an array.  
                  * @param int|string $num The code to send in the response.  
                  * @param string msg The additional string to send together  
                  * with the error code. If present it is added to the  
                  * return inside parentheses.  
                  * @return mixed The array containing the response.  
                  */  
                 private function HTTPStatus($num, $msg) {  
                     $code = $num;  
                     if ( !is_numeric($num) ) {  
                         $code = intval($num);  
                     }  
                     $http_protocol = "HTTP/1.0";  
                     if(isset($_SERVER['SERVER_PROTOCOL']) && stripos($_SERVER['SERVER_PROTOCOL'],"HTTP") >= 0){  
                         $http_protocol = $_SERVER['SERVER_PROTOCOL'];  
                     }  
                     $http = array(  
                         200 => ' 200 OK',  
                         201 => ' 201 Created',  
                         204 => ' 204 No Content',  
                         400 => ' 400 Bad Request',  
                         401 => ' 401 Unauthorized',  
                         403 => ' 403 Forbidden',  
                         404 => ' 404 Not Found',  
                         409 => ' 409 Conflict',  
                         500 => ' 500 Internal Server Error',  
                     );  
                           
                         // set the header info  
                         //header($http[$code]);  
                         header($http_protocol . " ". $http[$code]);  
                           
                         // create the response, add the msg in parentheses if  
                         // it was supplied  
                         //$error_msg = $http[$code];  
                         $error_msg = null;  
                         if ($msg && !empty($msg)) {  
                                 //$error_msg .= ' ('.$msg.')';  
                                 $error_msg = $msg;  
                         }  
                         print '  $error_msg:'.$error_msg."\r\n";  
                         if ($error_msg) {  
                                 return array(  
                             //'code' => $code,  
                             //'error' => $error_msg,  
                                         $error_msg  
                                 );  
                         }  
                         return null;  
                 }  
           
313      }      }
314    
315    }
316    
317  ?>  ?>

Legend:
Removed from v.559  
changed lines
  Added in v.728

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26