1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SugarAPI\SDK; |
4
|
|
|
|
5
|
|
|
use SugarAPI\SDK\Exception\AuthenticationException; |
6
|
|
|
use SugarAPI\SDK\Exception\SDKException; |
7
|
|
|
|
8
|
|
|
class SugarAPI { |
9
|
|
|
|
10
|
|
|
const API_URL = '/rest/v10/'; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Default Settings for SugarAPI Object. |
14
|
|
|
* Includes Default Instance, and Default Authentication Ooptions |
15
|
|
|
* Example: |
16
|
|
|
* array( |
17
|
|
|
* 'instance' => 'localhost', |
18
|
|
|
* 'auth' => array( |
19
|
|
|
* 'username' => 'admin', |
20
|
|
|
* 'password' => 'password', |
21
|
|
|
* 'client_id' => 'custom_client', |
22
|
|
|
* 'client_secret' => 's3cr3t', |
23
|
|
|
* 'platform' => 'custom_app' |
24
|
|
|
* ) |
25
|
|
|
* ); |
26
|
|
|
* @var array |
27
|
|
|
*/ |
28
|
|
|
protected static $_DEFAULTS = array(); |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* The configured instance |
32
|
|
|
* @var |
33
|
|
|
*/ |
34
|
|
|
protected $instance; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* The configured Rest v10 URL |
38
|
|
|
* @var |
39
|
|
|
*/ |
40
|
|
|
protected $url; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* The configured Authentication options |
44
|
|
|
* @var array |
45
|
|
|
*/ |
46
|
|
|
protected $authOptions = array( |
47
|
|
|
'username' => '', |
48
|
|
|
'password' => '', |
49
|
|
|
'client_id' => '', |
50
|
|
|
'client_secret' => '', |
51
|
|
|
'platform' => '' |
52
|
|
|
); |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* The authentication token, after successful login to SugarAPI |
56
|
|
|
* @var |
57
|
|
|
*/ |
58
|
|
|
protected $authToken; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* The time in which Auth Token expires, and needs to be refreshed |
62
|
|
|
* @var |
63
|
|
|
*/ |
64
|
|
|
protected $authExpiration; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* The list of registered EntryPoints |
68
|
|
|
* @var array |
69
|
|
|
*/ |
70
|
|
|
private $entryPoints = array(); |
71
|
|
|
|
72
|
|
|
public function __construct($instance = '', array $authOptions = array()){ |
73
|
|
|
$this->loadDefaults(); |
74
|
|
|
if (!empty($instance)){ |
75
|
|
|
$this->setInstance($instance); |
76
|
|
|
} |
77
|
|
|
if (!empty($authOptions)){ |
78
|
|
|
$this->setAuthOptions($authOptions); |
79
|
|
|
} |
80
|
|
|
$this->registerSDKEntryPoints(); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* Configure the static property $_DEFAULTS with settings from defaults.php |
85
|
|
|
*/ |
86
|
|
|
protected function loadDefaults(){ |
87
|
|
|
if (empty(static::$_DEFAULTS)) { |
88
|
|
|
include __DIR__ . DIRECTORY_SEPARATOR . 'defaults.php'; |
89
|
|
|
if (isset($defaults)) { |
|
|
|
|
90
|
|
|
static::$_DEFAULTS = $defaults; |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
if (isset(static::$_DEFAULTS['instance'])){ |
94
|
|
|
$this->setInstance(static::$_DEFAULTS['instance']); |
95
|
|
|
} |
96
|
|
|
if (isset(static::$_DEFAULTS['auth']) && is_array(static::$_DEFAULTS['auth'])){ |
97
|
|
|
$this->setAuthOptions(static::$_DEFAULTS['auth']); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* Configure the Authentication Options being used by SugarAPI Object |
103
|
|
|
* @param array $options |
104
|
|
|
*/ |
105
|
|
|
public function setAuthOptions(array $options){ |
106
|
|
|
foreach ($this->authOptions as $key => $value){ |
107
|
|
|
if (isset($options[$key])){ |
108
|
|
|
$this->authOptions[$key] = $options[$key]; |
109
|
|
|
} |
110
|
|
|
} |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Register the defined EntryPoints in SDK, located in src/EntryPoint/registry.php file |
115
|
|
|
* @throws SDKException |
116
|
|
|
*/ |
117
|
|
|
protected function registerSDKEntryPoints(){ |
118
|
|
|
require __DIR__.DIRECTORY_SEPARATOR.'EntryPoint'.DIRECTORY_SEPARATOR.'registry.php'; |
119
|
|
|
foreach ($entryPoints as $funcName => $className){ |
|
|
|
|
120
|
|
|
$className = "SugarAPI\\SDK\\EntryPoint\\".$className; |
121
|
|
|
$this->registerEntryPoint($funcName, $className); |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* Register an EntryPoint method on the SugarAPI object. |
127
|
|
|
* Allows for loading custom EntryPoints, so long as custom EntryPoints are autoloaded accordingly |
128
|
|
|
* @param $funcName - name of Method to be called on SugarAPI Object |
129
|
|
|
* @param $className - full name of EntryPoint Class that will be utilized |
130
|
|
|
* @throws SDKException |
131
|
|
|
*/ |
132
|
|
|
public function registerEntryPoint($funcName, $className){ |
133
|
|
|
if (isset($this->entryPoints[$funcName])){ |
134
|
|
|
throw new SDKException('SDK method already defined. Method '.$funcName.' references Class '.$className); |
135
|
|
|
} |
136
|
|
|
$this->entryPoints[$funcName] = $className; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Generates the EntryPoint objects based on the Method name that was called |
141
|
|
|
* @param $name |
142
|
|
|
* @param $params |
143
|
|
|
* @return mixed |
144
|
|
|
* @throws AuthenticationException |
145
|
|
|
* @throws SDKException |
146
|
|
|
*/ |
147
|
|
|
public function __call($name, $params){ |
148
|
|
|
if (array_key_exists($name, $this->entryPoints)){ |
149
|
|
|
$Class = $this->entryPoints[$name]; |
150
|
|
|
$EntryPoint = new $Class($this->url, $params); |
151
|
|
|
|
152
|
|
|
if ($EntryPoint->authRequired()){ |
153
|
|
|
if (isset($this->authToken)){ |
154
|
|
|
if ($this->authExpired()){ |
155
|
|
|
$this->refreshAuth(); |
156
|
|
|
} |
157
|
|
|
$EntryPoint->configureAuth($this->authToken->access_token); |
158
|
|
|
}else{ |
159
|
|
|
throw new AuthenticationException('Authentication is required for EntryPoint ['.$name.'].'); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
return $EntryPoint; |
163
|
|
|
}else{ |
164
|
|
|
throw new SDKException('Method '.$name.', is not a registered method of the SugarAPI SDK'); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Login to the configured SugarCRM instance, and stored the Auth Token |
170
|
|
|
* @throws AuthenticationException |
171
|
|
|
*/ |
172
|
|
|
public function login(){ |
173
|
|
|
if (empty($this->authOptions['username']) || empty($this->authOptions['password'])){ |
174
|
|
|
throw new AuthenticationException("Username or Password was not provided."); |
175
|
|
|
} |
176
|
|
|
$response = $this->oauth2Token()->data($this->authOptions)->execute()->getResponse(); |
|
|
|
|
177
|
|
|
if ($response->getStatus()=='200'){ |
178
|
|
|
$this->setAuthToken($response->getBody()); |
179
|
|
|
} else{ |
180
|
|
|
throw new AuthenticationException($response->getBody()); |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Set the current AuthToken and Auth Expiration properties |
186
|
|
|
* @param stdObject $token |
187
|
|
|
*/ |
188
|
|
|
protected function setAuthToken($token){ |
189
|
|
|
$this->authToken = $token; |
190
|
|
|
$this->authExpiration = time()+$token->expires_in; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Refresh Auth Token to further API use |
195
|
|
|
*/ |
196
|
|
|
public function refreshAuth(){ |
197
|
|
|
$refreshOptions = array( |
198
|
|
|
'client_id' => $this->authOptions->client_id, |
199
|
|
|
'client_secret' => $this->authOptions->client_secret, |
200
|
|
|
'refresh_token' => $this->authOptions->refresh_token |
201
|
|
|
); |
202
|
|
|
$response = $this->oauth2Refresh()->data($refreshOptions)->execute()->getResponse(); |
|
|
|
|
203
|
|
|
if ($response->getStatus()=='200'){ |
204
|
|
|
$this->setAuthToken($response->getBody()); |
205
|
|
|
} else{ |
206
|
|
|
throw new AuthenticationException($response->getBody()); |
207
|
|
|
} |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Check if current access token is expired |
212
|
|
|
* @return bool |
213
|
|
|
*/ |
214
|
|
|
public function authExpired(){ |
215
|
|
|
return time() >= $this->authExpiration; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Force Logout of SugarAPI Object |
220
|
|
|
*/ |
221
|
|
|
public function logout(){ |
222
|
|
|
if (!empty($this->authToken)){ |
223
|
|
|
$response = $this->oauth2Logout()->execute()->getResponse(); |
|
|
|
|
224
|
|
|
if ($response->getStatus()=='200'){ |
225
|
|
|
unset($this->authToken); |
226
|
|
|
unset($this->authExpiration); |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Configure the instance that the SugarAPI object will be communicating with |
233
|
|
|
* @param $instance |
234
|
|
|
*/ |
235
|
|
|
public function setInstance($instance){ |
236
|
|
|
if (strpos("http", $instance)===FALSE){ |
237
|
|
|
$instance = "http://".$instance; |
238
|
|
|
} |
239
|
|
|
if (strpos("rest/v10", $instance)!==FALSE){ |
240
|
|
|
$instance = str_replace("rest/v10", "", $instance); |
241
|
|
|
} |
242
|
|
|
$this->instance = $instance; |
243
|
|
|
$this->url = rtrim($this->instance, "/").self::API_URL; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Get the configured Rest v10 URL |
248
|
|
|
* @return mixed |
249
|
|
|
*/ |
250
|
|
|
public function getURL(){ |
251
|
|
|
return $this->url; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Get the Authentication Token |
256
|
|
|
* @return mixed |
257
|
|
|
*/ |
258
|
|
|
public function getToken(){ |
259
|
|
|
return $this->authToken; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Get the configured Authentication Options |
264
|
|
|
* @return array |
265
|
|
|
*/ |
266
|
|
|
public function getAuthOptions(){ |
267
|
|
|
return $this->authOptions; |
268
|
|
|
} |
269
|
|
|
} |
This check looks for calls to
isset(...)
orempty()
on variables that are yet undefined. These calls will always produce the same result and can be removed.This is most likely caused by the renaming of a variable or the removal of a function/method parameter.