APIDatabaseError class for SQL error, which send debug info to slim\log
[dolibarr-api.git] / index.php
1 <?php
2
3 /*** DOLIBARR ***/
4 require("dolibarr/master.inc.php");
5 require_once(DOL_DOCUMENT_ROOT."/adherents/class/adherent.class.php");
6 require_once(DOL_DOCUMENT_ROOT."/adherents/class/adherent_type.class.php");
7 require_once(DOL_DOCUMENT_ROOT."/societe/class/societe.class.php");
8 require_once(DOL_DOCUMENT_ROOT."/core/class/cpays.class.php");
9 /****************/
10
11
12 require 'Array2XML.php';
13 require 'Slim/Slim.php';
14 \Slim\Slim::registerAutoloader();
15 require 'mediatypehandler.php';
16
17 $app = new \Slim\Slim();
18
19 $app->config('debug', true);
20
21 $log = $app->getLog();
22 $log->setEnabled(true);
23 $log->setLevel(\Slim\Log::WARN);
24
25 // automatically negociate content-type and decode data and encode response
26 $app->add(new MediaTypeMiddleware());
27
28 $app->notFound(function () use ($app) {
29     echo marsh_response(array('errors' => array(new APIError('Path not found', 'dolibarr.api.InvalidPath', 1001))));
30 });
31
32 $return405=function() use ($app) {
33     $app->status(405);
34     echo 'Methode non supportée';
35 };
36
37 function add_allow() {
38     $methods=func_get_args();
39     if(count($methods) == 1 && is_array($methods[0])) {
40         $methods=$methods[0];
41     }
42     return function (\Slim\Route $route) use ($methods) {
43         $app = \Slim\Slim::getInstance();
44         $app->response()['Allow'] = implode(', ', $methods);
45     };
46 };
47
48 /**
49  * Exception class to wrap API errors
50  */
51 class APIException extends Exception implements JsonSerializable {
52     private $errors;
53
54     public function __construct($errors) {
55         $this->errors=$errors;
56     }
57
58     public function addError($err) {
59         $this->errors[]=$err;
60     }
61
62     public function getErrors() {
63         return $this->errors;
64     }
65
66     public function hasErrors() {
67         return (bool)count($this->errors);
68     }
69
70     public function __toString() {
71         return __CLASS__;
72     }
73
74     public function jsonSerialize() {
75         return array('errors' => $this->errors);
76     }
77 }
78
79 class APIError implements JsonSerializable, IteratorAggregate {
80     protected $error;
81     protected $error_code;
82     protected $message;
83
84     public function __construct($message, $error='cafai.api.GenericError', $errcode=1000) {
85         $this->error      = $error;
86         $this->error_code = $errcode;
87         $this->message    = $message;
88     }
89
90     public function jsonSerialize() {
91         return array('error' => $this->error, 
92                      'error_code' => $this->error_code,
93                      'message' => $this->message);
94     }
95
96     public function getIterator() {
97         return new ArrayIterator($this->jsonSerialize());
98
99     }
100 }
101
102 class APIObjectNotFoundError extends APIError {
103     const error='dolibarr.api.ObjectNotFound';
104     const error_code=1002;
105     public function __construct($message) {
106         parent::__construct($message, self::error, self::error_code);
107     }
108 }
109
110 class APIMissingParamError extends APIError {
111     const error_base='dolibarr.api.MissingParam';
112     const error_code=1010;
113     public function __construct($target, $message) {
114         $error=self::error_base.".$target";
115         parent::__construct($message, $error, self::error_code);
116     }
117 }
118
119 class APIInvalidParamError extends APIError {
120     const error_base='dolibarr.api.InvalidParam';
121     const error_code=1011;
122     public function __construct($target, $message) {
123         $error=self::error_base.".$target";
124         parent::__construct($message, $error, self::error_code);
125     }
126 }
127
128 class APIInternalError extends APIError {
129     const error='dolibarr.api.InternalError';
130     const error_code=1020;
131 }
132
133 class APIDatabaseError extends APIError {
134     const error='dolibarr.api.DatabaseError';
135     const error_code=1021;
136     public function __construct($message, $db, $error='', $code=0) {
137         global $log, $app;
138         $debug_mode=$app->config('debug');
139
140         $trace=(new Exception)->getTraceAsString();
141         $log->error("Database Error:\n".
142                     "\tclient: ".$app->request()->getIp()."\n".
143                     "\tpath: ".$app->request()->getPath()."\n".
144                     "\tdata: ".@json_encode($app->request()->getBody())."\n".
145                     "\terr msg: ".$message."\n".
146                     "\tinfo: error=\"".$error."\" code=".$code."\n".
147                     "\tsql error: ".$db->lasterrno.'; '.$db->lasterror."\n".
148                     "\tquery: ".$db->lastqueryerror."\n".
149                     "\ttrace:\n".preg_replace("/^#0 .*?\n/", '', $trace)."\n");
150
151         if($debug_mode)
152             parent::__construct($message, self::error, self::error_code);
153         else
154             /* Mask database errors on production... */
155             parent::__construct("", APIInternalError::error, APIInternalError::error_code);
156     }
157
158 }
159
160
161 function halt_error($status, $rest_error) {
162     $app = \Slim\Slim::getInstance();
163     $app->halt($status, marsh_response(array('errors' => array(
164         $rest_error
165     ))));
166 }
167
168 /**
169  * Check keys of an array for a constraint ($cb)
170  * returns the key that fail
171  */
172 function check_keys($data, $keys, $cb) {
173     $ret=array();
174     foreach($keys as &$v) {
175         if(!$cb($data, $v)) {
176             $ret[]=$v;
177         }
178     }
179     return $ret;
180 }
181
182 function buildPath($path) {
183     return \Slim\Slim::getInstance()->request()->getScriptName().$path;
184 }
185
186 function check_unmodified_since($unix_timestamp, $error_on_missing=true) {
187     global $app;
188     $h=$app->request()->headers('IF_UNMODIFIED_SINCE');
189     if($h === null) {
190         halt_error(403, new APIError('You must supply the If-Unmodified-Since header '.
191                                      'in order to call this method'));
192     }
193     $unmodified_since = strtotime($h);
194     return $unix_timestamp == $unmodified_since;
195 }
196
197 require 'adherents.php';
198 require 'misc.php';
199
200 $app->run();