Commit | Line | Data |
---|---|---|
6a488035 | 1 | <?php |
6a488035 TO |
2 | |
3 | /** | |
ade83e4b CB |
4 | * |
5 | * This class allows to consume the API, either from within a module that knows civicrm already: | |
6 | * | |
7 | * @code | |
0cd417ef | 8 | * require_once('api/class.api.php'); |
ade83e4b CB |
9 | * $api = new civicrm_api3(); |
10 | * @endcode | |
11 | * | |
12 | * or from any code on the same server as civicrm | |
13 | * | |
14 | * @code | |
15 | * require_once('/your/civi/folder/api/class.api.php'); | |
16 | * // the path to civicrm.settings.php | |
17 | * $api = new civicrm_api3 (array('conf_path'=> '/your/path/to/your/civicrm/or/joomla/site)); | |
18 | * @endcode | |
19 | * | |
20 | * or to query a remote server via the rest api | |
21 | * | |
22 | * @code | |
0cd417ef KW |
23 | * $api = new civicrm_api3 (array ('server' => 'http://example.org', |
24 | * 'api_key'=>'theusersecretkey', | |
25 | * 'key'=>'thesitesecretkey')); | |
ade83e4b CB |
26 | * @endcode |
27 | * | |
28 | * No matter how initialised and if civicrm is local or remote, you use the class the same way. | |
29 | * | |
30 | * @code | |
31 | * $api->{entity}->{action}($params); | |
32 | * @endcode | |
33 | * | |
34 | * So, to get the individual contacts: | |
35 | * | |
36 | * @code | |
37 | * if ($api->Contact->Get(array('contact_type'=>'Individual','return'=>'sort_name,current_employer')) { | |
38 | * // each key of the result array is an attribute of the api | |
39 | * echo "\n contacts found " . $api->count; | |
40 | * foreach ($api->values as $c) { | |
41 | * echo "\n".$c->sort_name. " working for ". $c->current_employer; | |
42 | * } | |
43 | * // in theory, doesn't append | |
44 | * } else { | |
45 | * echo $api->errorMsg(); | |
46 | * } | |
47 | * @endcode | |
48 | * | |
49 | * Or, to create an event: | |
50 | * | |
51 | * @code | |
52 | * if ($api->Event->Create(array('title'=>'Test','event_type_id' => 1,'is_public' => 1,'start_date' => 19430429))) { | |
53 | * echo "created event id:". $api->id; | |
54 | * } else { | |
55 | * echo $api->errorMsg(); | |
56 | * } | |
57 | * @endcode | |
58 | * | |
59 | * To make it easier, the Actions can either take for input an | |
60 | * associative array $params, or simply an id. The following two lines | |
61 | * are equivalent. | |
62 | * | |
63 | * @code | |
64 | * $api->Activity->Get (42); | |
65 | * $api->Activity->Get (array('id'=>42)); | |
66 | * @endcode | |
67 | * | |
68 | * | |
69 | * You can also get the result like civicrm_api does, but as an object | |
70 | * instead of an array (eg $entity->attribute instead of | |
71 | * $entity['attribute']). | |
72 | * | |
73 | * @code | |
74 | * $result = $api->result; | |
75 | * // is the json encoded result | |
76 | * echo $api; | |
77 | * @endcode | |
6a488035 TO |
78 | */ |
79 | class civicrm_api3 { | |
ade83e4b CB |
80 | |
81 | /** | |
dc64d047 EM |
82 | * Class constructor. |
83 | * | |
decce855 | 84 | * @param array $config API configuration. |
ade83e4b | 85 | */ |
3bdca100 | 86 | public function __construct($config = NULL) { |
6a488035 TO |
87 | $this->local = TRUE; |
88 | $this->input = array(); | |
89 | $this->lastResult = array(); | |
90 | if (isset($config) && isset($config['server'])) { | |
91 | // we are calling a remote server via REST | |
92 | $this->local = FALSE; | |
93 | $this->uri = $config['server']; | |
94 | if (isset($config['path'])) { | |
95 | $this->uri .= "/" . $config['path']; | |
96 | } | |
ade83e4b CB |
97 | else { |
98 | $this->uri .= '/sites/all/modules/civicrm/extern/rest.php'; | |
99 | } | |
6a488035 TO |
100 | $this->uri .= '?json=1'; |
101 | if (isset($config['key'])) { | |
102 | $this->key = $config['key']; | |
103 | } | |
104 | else { | |
105 | die("\nFATAL:param['key] missing\n"); | |
106 | } | |
107 | if (isset($config['api_key'])) { | |
108 | $this->api_key = $config['api_key']; | |
109 | } | |
110 | else { | |
111 | die("\nFATAL:param['api_key] missing\n"); | |
112 | } | |
6a488035 TO |
113 | return; |
114 | } | |
115 | if (isset($config) && isset($config['conf_path'])) { | |
116 | define('CIVICRM_SETTINGS_PATH', $config['conf_path'] . '/civicrm.settings.php'); | |
117 | require_once CIVICRM_SETTINGS_PATH; | |
f9c7ce43 | 118 | require_once 'CRM/Core/ClassLoader.php'; |
6a488035 TO |
119 | require_once 'api/api.php'; |
120 | require_once "api/v3/utils.php"; | |
f9c7ce43 | 121 | CRM_Core_ClassLoader::singleton()->register(); |
6a488035 TO |
122 | $this->cfg = CRM_Core_Config::singleton(); |
123 | $this->init(); | |
124 | } | |
125 | else { | |
126 | $this->cfg = CRM_Core_Config::singleton(); | |
127 | } | |
128 | } | |
129 | ||
ade83e4b | 130 | /** |
dc64d047 EM |
131 | * Convert to string. |
132 | * | |
c490a46a | 133 | * @return string |
ade83e4b | 134 | */ |
6a488035 TO |
135 | public function __toString() { |
136 | return json_encode($this->lastResult); | |
137 | } | |
138 | ||
ade83e4b | 139 | /** |
dc64d047 EM |
140 | * Perform action. |
141 | * | |
645ee340 EM |
142 | * @param $action |
143 | * @param $params | |
dc64d047 | 144 | * |
645ee340 | 145 | * @return bool |
ade83e4b | 146 | */ |
6a488035 | 147 | public function __call($action, $params) { |
ade83e4b | 148 | // @TODO Check if it's a valid action. |
6a488035 TO |
149 | if (isset($params[0])) { |
150 | return $this->call($this->currentEntity, $action, $params[0]); | |
151 | } | |
152 | else { | |
153 | return $this->call($this->currentEntity, $action, $this->input); | |
154 | } | |
155 | } | |
156 | ||
ade83e4b | 157 | /** |
dc64d047 EM |
158 | * As of PHP 5.3.0. |
159 | * | |
645ee340 EM |
160 | * @param $name |
161 | * @param $arguments | |
ade83e4b | 162 | */ |
6a488035 TO |
163 | public static function __callStatic($name, $arguments) { |
164 | // Should we implement it ? | |
165 | echo "Calling static method '$name' " . implode(', ', $arguments) . "\n"; | |
166 | } | |
167 | ||
ade83e4b | 168 | /** |
dc64d047 EM |
169 | * Call via rest. |
170 | * | |
645ee340 EM |
171 | * @param $entity |
172 | * @param $action | |
173 | * @param array $params | |
dc64d047 | 174 | * |
645ee340 | 175 | * @return \stdClass |
ade83e4b | 176 | */ |
3bdca100 | 177 | public function remoteCall($entity, $action, $params = array()) { |
6a488035 TO |
178 | $fields = "key={$this->key}&api_key={$this->api_key}"; |
179 | $query = $this->uri . "&entity=$entity&action=$action"; | |
180 | foreach ($params as $k => $v) { | |
181 | $fields .= "&$k=" . urlencode($v); | |
182 | } | |
183 | if (function_exists('curl_init')) { | |
ade83e4b CB |
184 | // To facilitate debugging without leaking info, entity & action |
185 | // are GET, other data is POST. | |
6a488035 TO |
186 | $ch = curl_init(); |
187 | curl_setopt($ch, CURLOPT_URL, $query); | |
188 | curl_setopt($ch, CURLOPT_POST, count($params) + 2); | |
189 | curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); | |
190 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); | |
6a488035 | 191 | $result = curl_exec($ch); |
7a514fb1 CB |
192 | // CiviCRM expects to get back a CiviCRM error object. |
193 | if (curl_errno($ch)) { | |
3bdca100 | 194 | $res = new stdClass(); |
ade83e4b | 195 | $res->is_error = 1; |
7922c1d3 XD |
196 | $res->error_message = curl_error($ch); |
197 | $res->level = "cURL"; | |
ade83e4b CB |
198 | $res->error = array('cURL error' => curl_error($ch)); |
199 | return $res; | |
8ef12e64 | 200 | } |
6a488035 | 201 | curl_close($ch); |
6a488035 TO |
202 | } |
203 | else { | |
ade83e4b CB |
204 | // Should be discouraged, because the API credentials and data |
205 | // are submitted as GET data, increasing chance of exposure.. | |
6a488035 | 206 | $result = file_get_contents($query . '&' . $fields); |
6a488035 | 207 | } |
78a069a9 | 208 | if (!$res = json_decode($result)) { |
3bdca100 | 209 | $res = new stdClass(); |
78a069a9 CB |
210 | $res->is_error = 1; |
211 | $res->error_message = 'Unable to parse returned JSON'; | |
212 | $res->level = 'json_decode'; | |
213 | $res->error = array('Unable to parse returned JSON' => $result); | |
214 | $res->row_result = $result; | |
215 | } | |
216 | return $res; | |
6a488035 TO |
217 | } |
218 | ||
aa1b1481 | 219 | /** |
dc64d047 EM |
220 | * Call api function. |
221 | * | |
aa1b1481 EM |
222 | * @param $entity |
223 | * @param string $action | |
224 | * @param array $params | |
225 | * | |
226 | * @return bool | |
227 | */ | |
3bdca100 | 228 | public function call($entity, $action = 'Get', $params = array()) { |
6a488035 TO |
229 | if (is_int($params)) { |
230 | $params = array('id' => $params); | |
231 | } | |
232 | elseif (is_string($params)) { | |
233 | $params = json_decode($params); | |
234 | } | |
235 | ||
236 | if (!isset($params['version'])) { | |
6a488035 TO |
237 | $params['version'] = 3; |
238 | } | |
239 | if (!isset($params['sequential'])) { | |
240 | $params['sequential'] = 1; | |
241 | } | |
242 | ||
243 | if (!$this->local) { | |
244 | $this->lastResult = $this->remoteCall($entity, $action, $params); | |
245 | } | |
246 | else { | |
ade83e4b | 247 | // Converts a multi-dimentional array into an object. |
6a488035 TO |
248 | $this->lastResult = json_decode(json_encode(civicrm_api($entity, $action, $params))); |
249 | } | |
ade83e4b | 250 | // Reset the input to be ready for a new call. |
6a488035 TO |
251 | $this->input = array(); |
252 | if (property_exists($this->lastResult, 'is_error')) { | |
253 | return !$this->lastResult->is_error; | |
254 | } | |
ade83e4b | 255 | // getsingle doesn't have is_error. |
6a488035 TO |
256 | return TRUE; |
257 | } | |
258 | ||
ade83e4b CB |
259 | /** |
260 | * Helper method for long running programs (eg bots). | |
261 | */ | |
3bdca100 | 262 | public function ping() { |
6a488035 TO |
263 | global $_DB_DATAOBJECT; |
264 | foreach ($_DB_DATAOBJECT['CONNECTIONS'] as & $c) { | |
265 | if (!$c->connection->ping()) { | |
266 | $c->connect($this->cfg->dsn); | |
267 | if (!$c->connection->ping()) { | |
268 | die("we couldn't connect"); | |
269 | } | |
270 | } | |
271 | } | |
272 | } | |
273 | ||
ade83e4b CB |
274 | /** |
275 | * Return the last error message. | |
c490a46a | 276 | * @return string |
ade83e4b | 277 | */ |
3bdca100 | 278 | public function errorMsg() { |
6a488035 TO |
279 | return $this->lastResult->error_message; |
280 | } | |
281 | ||
ade83e4b | 282 | /** |
1747ab99 | 283 | * Initialize. |
ade83e4b | 284 | */ |
3bdca100 | 285 | public function init() { |
6a488035 TO |
286 | CRM_Core_DAO::init($this->cfg->dsn); |
287 | } | |
288 | ||
ade83e4b | 289 | /** |
1747ab99 EM |
290 | * Get attribute. |
291 | * | |
645ee340 EM |
292 | * @param $name |
293 | * @param null $value | |
1747ab99 | 294 | * |
c490a46a | 295 | * @return $this |
6a488035 | 296 | */ |
6a488035 TO |
297 | public function attr($name, $value = NULL) { |
298 | if ($value === NULL) { | |
299 | if (property_exists($this->lastResult, $name)) { | |
300 | return $this->lastResult->$name; | |
301 | } | |
302 | } | |
303 | else { | |
304 | $this->input[$name] = $value; | |
305 | } | |
306 | return $this; | |
307 | } | |
308 | ||
ade83e4b | 309 | /** |
dc64d047 EM |
310 | * Is this an error. |
311 | * | |
c490a46a | 312 | * @return bool |
ade83e4b | 313 | */ |
6a488035 TO |
314 | public function is_error() { |
315 | return (property_exists($this->lastResult, 'is_error') && $this->lastResult->is_error); | |
316 | } | |
317 | ||
ade83e4b | 318 | /** |
1747ab99 EM |
319 | * Check if var is set. |
320 | * | |
100fef9d | 321 | * @param string $name |
dc64d047 | 322 | * |
c490a46a | 323 | * @return bool |
ade83e4b | 324 | */ |
6a488035 TO |
325 | public function is_set($name) { |
326 | return (isset($this->lastResult->$name)); | |
327 | } | |
328 | ||
ade83e4b | 329 | /** |
1747ab99 EM |
330 | * Get object. |
331 | * | |
332 | * @param string $name | |
dc64d047 | 333 | * |
c490a46a | 334 | * @return $this |
ade83e4b | 335 | */ |
6a488035 | 336 | public function __get($name) { |
ade83e4b | 337 | // @TODO Test if valid entity. |
6a488035 | 338 | if (strtolower($name) !== $name) { |
ade83e4b CB |
339 | // Cheap and dumb test to differentiate call to |
340 | // $api->Entity->Action & value retrieval. | |
6a488035 TO |
341 | $this->currentEntity = $name; |
342 | return $this; | |
343 | } | |
6a488035 | 344 | if ($name === 'result') { |
6a488035 TO |
345 | return $this->lastResult; |
346 | } | |
347 | if ($name === 'values') { | |
348 | return $this->lastResult->values; | |
349 | } | |
6a488035 | 350 | if (property_exists($this->lastResult, $name)) { |
6a488035 TO |
351 | return $this->lastResult->$name; |
352 | } | |
6a488035 TO |
353 | $this->currentEntity = $name; |
354 | return $this; | |
355 | } | |
356 | ||
ade83e4b CB |
357 | /** |
358 | * Or use $api->value. | |
c490a46a | 359 | * @return array |
ade83e4b | 360 | */ |
6a488035 TO |
361 | public function values() { |
362 | if (is_array($this->lastResult)) { | |
363 | return $this->lastResult['values']; | |
364 | } | |
ade83e4b CB |
365 | else { |
366 | return $this->lastResult->values; | |
367 | } | |
6a488035 TO |
368 | } |
369 | ||
ade83e4b CB |
370 | /** |
371 | * Or use $api->result. | |
c490a46a | 372 | * @return array |
ade83e4b | 373 | */ |
6a488035 TO |
374 | public function result() { |
375 | return $this->lastResult; | |
376 | } | |
96025800 | 377 | |
6a488035 | 378 | } |