1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* A Twitter supported and maintained Ads API SDK for PHP. |
4
|
|
|
* |
5
|
|
|
* @license MIT |
6
|
|
|
*/ |
7
|
|
|
namespace Hborras\TwitterAdsSDK; |
8
|
|
|
|
9
|
|
|
use Exception; |
10
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Account; |
11
|
|
|
use Hborras\TwitterAdsSDK; |
12
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Cursor; |
13
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\BadRequest; |
14
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\Forbidden; |
15
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\NotAuthorized; |
16
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\NotFound; |
17
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\RateLimit; |
18
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\ServerError; |
19
|
|
|
use Hborras\TwitterAdsSDK\TwitterAds\Errors\ServiceUnavailable; |
20
|
|
|
use Hborras\TwitterAdsSDK\Util\JsonDecoder; |
21
|
|
|
use GuzzleHttp; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* TwitterAds class for interacting with the Twitter API. |
25
|
|
|
* |
26
|
|
|
* @author Hector Borras <[email protected]> |
27
|
|
|
*/ |
28
|
|
|
class TwitterAds extends Config |
29
|
|
|
{ |
30
|
|
|
const API_VERSION = '1'; |
31
|
|
|
const API_REST_VERSION = '1.1'; |
32
|
|
|
const API_HOST = 'https://ads-api.twitter.com'; |
33
|
|
|
const API_HOST_SANDBOX = 'https://ads-api-sandbox.twitter.com'; |
34
|
|
|
const API_HOST_OAUTH = 'https://api.twitter.com'; |
35
|
|
|
const UPLOAD_HOST = 'https://upload.twitter.com'; |
36
|
|
|
const UPLOAD_PATH = 'media/upload.json'; |
37
|
|
|
const UPLOAD_CHUNK = 40960; // 1024 * 40 |
38
|
|
|
|
39
|
|
|
/** @var string Method used for the request */ |
40
|
|
|
private $method; |
41
|
|
|
/** @var string Resource used for the request */ |
42
|
|
|
private $resource; |
43
|
|
|
/** @var Response details about the result of the last request */ |
44
|
|
|
private $response; |
45
|
|
|
/** @var string|null Application bearer token */ |
46
|
|
|
private $bearer; |
47
|
|
|
/** @var Consumer Twitter application details */ |
48
|
|
|
private $consumer; |
49
|
|
|
/** @var Token|null User access token details */ |
50
|
|
|
private $token; |
51
|
|
|
/** @var HmacSha1 OAuth 1 signature type used by Twitter */ |
52
|
|
|
private $signatureMethod; |
53
|
|
|
/** @var bool Sandbox allows to make requests thought sandbox environment */ |
54
|
|
|
private $sandbox; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Constructor. |
58
|
|
|
* |
59
|
|
|
* @param string $consumerKey The Application Consumer Key |
60
|
|
|
* @param string $consumerSecret The Application Consumer Secret |
61
|
|
|
* @param string|null $oauthToken The Client Token (optional) |
62
|
|
|
* @param string|null $oauthTokenSecret The Client Token Secret (optional) |
63
|
|
|
* @param bool $sandbox The Sandbox environment (optional) |
64
|
|
|
*/ |
65
|
|
|
public function __construct($consumerKey, $consumerSecret, $oauthToken = null, $oauthTokenSecret = null, $sandbox = false) |
66
|
|
|
{ |
67
|
|
|
$this->resetLastResponse(); |
68
|
|
|
$this->signatureMethod = new HmacSha1(); |
69
|
|
|
$this->consumer = new Consumer($consumerKey, $consumerSecret); |
70
|
|
|
if (!empty($oauthToken) && !empty($oauthTokenSecret)) { |
71
|
|
|
$this->token = new Token($oauthToken, $oauthTokenSecret); |
72
|
|
|
} |
73
|
|
|
if (empty($oauthToken) && !empty($oauthTokenSecret)) { |
74
|
|
|
$this->bearer = $oauthTokenSecret; |
75
|
|
|
} |
76
|
|
|
$this->sandbox = $sandbox; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @param string $accountId |
81
|
|
|
* |
82
|
|
|
* @return Account|Cursor |
83
|
|
|
*/ |
84
|
|
|
public function getAccounts($accountId = '') |
85
|
|
|
{ |
86
|
|
|
$account = new Account($this); |
87
|
|
|
|
88
|
|
|
return $accountId ? $account->load($accountId) : $account->all(); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @param string $oauthToken |
93
|
|
|
* @param string $oauthTokenSecret |
94
|
|
|
*/ |
95
|
|
|
public function setOauthToken($oauthToken, $oauthTokenSecret) |
96
|
|
|
{ |
97
|
|
|
$this->token = new Token($oauthToken, $oauthTokenSecret); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @return string|null |
102
|
|
|
*/ |
103
|
|
|
public function getLastApiPath() |
104
|
|
|
{ |
105
|
|
|
return $this->response->getApiPath(); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @return int |
110
|
|
|
*/ |
111
|
|
|
public function getLastHttpCode() |
112
|
|
|
{ |
113
|
|
|
return $this->response->getHttpCode(); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* @return array |
118
|
|
|
*/ |
119
|
|
|
public function getLastXHeaders() |
120
|
|
|
{ |
121
|
|
|
return $this->response->getXHeaders(); |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* @return array|object|null |
126
|
|
|
*/ |
127
|
|
|
public function getLastBody() |
128
|
|
|
{ |
129
|
|
|
return $this->response->getBody(); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Resets the last response cache. |
134
|
|
|
*/ |
135
|
|
|
public function resetLastResponse() |
136
|
|
|
{ |
137
|
|
|
$this->response = new Response(); |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Make URLs for user browser navigation. |
142
|
|
|
* |
143
|
|
|
* @param string $path |
144
|
|
|
* @param array $parameters |
145
|
|
|
* |
146
|
|
|
* @return string |
147
|
|
|
*/ |
148
|
|
|
public function url($path, array $parameters) |
149
|
|
|
{ |
150
|
|
|
$this->resetLastResponse(); |
151
|
|
|
$this->response->setApiPath($path); |
152
|
|
|
$query = http_build_query($parameters); |
153
|
|
|
|
154
|
|
|
return sprintf('%s/%s?%s', self::API_HOST_OAUTH, $path, $query); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Make /oauth/* requests to the API. |
159
|
|
|
* |
160
|
|
|
* @param string $path |
161
|
|
|
* @param array $parameters |
162
|
|
|
* @return array |
163
|
|
|
* @throws Exception |
164
|
|
|
*/ |
165
|
|
|
public function oauth($path, array $parameters = []) |
166
|
|
|
{ |
167
|
|
|
$response = []; |
168
|
|
|
$this->resetLastResponse(); |
169
|
|
|
$this->response->setApiPath($path); |
170
|
|
|
$url = sprintf('%s/%s', self::API_HOST_OAUTH, $path); |
171
|
|
|
$result = $this->oAuthRequest($url, 'POST', $parameters); |
172
|
|
|
|
173
|
|
|
if ($this->getLastHttpCode() != 200) { |
174
|
|
|
throw new TwitterAdsException($result, 500, null, $result); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
parse_str($result, $response); |
178
|
|
|
$this->response->setBody($response); |
179
|
|
|
|
180
|
|
|
return $response; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
/** |
184
|
|
|
* Make /oauth2/* requests to the API. |
185
|
|
|
* |
186
|
|
|
* @param string $path |
187
|
|
|
* @param array $parameters |
188
|
|
|
* |
189
|
|
|
* @return array|object |
190
|
|
|
*/ |
191
|
|
|
public function oauth2($path, array $parameters = []) |
192
|
|
|
{ |
193
|
|
|
$method = 'POST'; |
194
|
|
|
$this->resetLastResponse(); |
195
|
|
|
$this->response->setApiPath($path); |
196
|
|
|
$url = sprintf('%s/%s', self::API_HOST_OAUTH, $path); |
197
|
|
|
$request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters); |
198
|
|
|
$authorization = 'Authorization: Basic '.$this->encodeAppAuthorization($this->consumer); |
199
|
|
|
$result = $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters); |
200
|
|
|
$response = JsonDecoder::decode($result, $this->decodeJsonAsArray); |
201
|
|
|
$this->response->setBody($response); |
202
|
|
|
|
203
|
|
|
return $response; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
public function verifyCredentials($parameters = []) |
207
|
|
|
{ |
208
|
|
|
return $this->http('GET', self::API_HOST_OAUTH, 'account/verify_credentials', $parameters); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Make GET requests to the API. |
213
|
|
|
* |
214
|
|
|
* @param string $path |
215
|
|
|
* @param array $parameters |
216
|
|
|
* |
217
|
|
|
* @return Response |
218
|
|
|
*/ |
219
|
|
|
public function get($path, array $parameters = []) |
220
|
|
|
{ |
221
|
|
|
return $this->http('GET', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* Make POST requests to the API. |
226
|
|
|
* |
227
|
|
|
* @param string $path |
228
|
|
|
* @param array $parameters |
229
|
|
|
* |
230
|
|
|
* @param array $headers |
231
|
|
|
* @return Response |
232
|
|
|
*/ |
233
|
|
|
public function post($path, array $parameters = [], array $headers = []) |
234
|
|
|
{ |
235
|
|
|
return $this->http('POST', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters, $headers); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Make DELETE requests to the API. |
240
|
|
|
* |
241
|
|
|
* @param string $path |
242
|
|
|
* @param array $parameters |
243
|
|
|
* |
244
|
|
|
* @return Response |
245
|
|
|
*/ |
246
|
|
|
public function delete($path, array $parameters = []) |
247
|
|
|
{ |
248
|
|
|
return $this->http('DELETE', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* Make PUT requests to the API. |
253
|
|
|
* |
254
|
|
|
* @param string $path |
255
|
|
|
* @param array $parameters |
256
|
|
|
* |
257
|
|
|
* @return Response |
258
|
|
|
*/ |
259
|
|
|
public function put($path, array $parameters = [], array $headers = []) |
260
|
|
|
{ |
261
|
|
|
return $this->http('PUT', !$this->sandbox ? self::API_HOST : self::API_HOST_SANDBOX, $path, $parameters, $headers); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Upload media to upload.twitter.com. |
266
|
|
|
* |
267
|
|
|
* @param array $parameters |
268
|
|
|
* @param bool $chunked |
269
|
|
|
* |
270
|
|
|
* @return array|object |
271
|
|
|
*/ |
272
|
|
|
public function upload(array $parameters = [], $chunked = false) |
273
|
|
|
{ |
274
|
|
|
if ($chunked) { |
275
|
|
|
return $this->uploadMediaChunked(self::UPLOAD_PATH, $parameters)->getBody(); |
276
|
|
|
} else { |
277
|
|
|
return $this->uploadMediaNotChunked(self::UPLOAD_PATH, $parameters)->getBody(); |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
281
|
|
|
/** |
282
|
|
|
* Private method to upload media (not chunked) to upload.twitter.com. |
283
|
|
|
* |
284
|
|
|
* @param string $path |
285
|
|
|
* @param array $parameters |
286
|
|
|
* |
287
|
|
|
* @return array|object |
288
|
|
|
*/ |
289
|
|
|
private function uploadMediaNotChunked($path, $parameters) |
290
|
|
|
{ |
291
|
|
|
$file = file_get_contents($parameters['media']); |
292
|
|
|
$base = base64_encode($file); |
293
|
|
|
$parameters['media'] = $base; |
294
|
|
|
|
295
|
|
|
return $this->http('POST', self::UPLOAD_HOST, $path, $parameters); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* Private method to upload media (chunked) to upload.twitter.com. |
300
|
|
|
* |
301
|
|
|
* @param string $path |
302
|
|
|
* @param array $parameters |
303
|
|
|
* |
304
|
|
|
* @return array|object |
305
|
|
|
*/ |
306
|
|
|
private function uploadMediaChunked($path, $parameters) |
307
|
|
|
{ |
308
|
|
|
// Init |
309
|
|
|
$init = $this->http( |
310
|
|
|
'POST', |
311
|
|
|
self::UPLOAD_HOST, |
312
|
|
|
$path, |
313
|
|
|
[ |
314
|
|
|
'command' => 'INIT', |
315
|
|
|
'media_type' => $parameters['media_type'], |
316
|
|
|
'total_bytes' => filesize($parameters['media']), |
317
|
|
|
] |
318
|
|
|
); |
319
|
|
|
// Append |
320
|
|
|
$segment_index = 0; |
321
|
|
|
$media = fopen($parameters['media'], 'rb'); |
322
|
|
|
while (!feof($media)) { |
323
|
|
|
$this->http( |
324
|
|
|
'POST', |
325
|
|
|
self::UPLOAD_HOST, |
326
|
|
|
'media/upload', |
327
|
|
|
[ |
328
|
|
|
'command' => 'APPEND', |
329
|
|
|
'media_id' => $init->media_id_string, |
|
|
|
|
330
|
|
|
'segment_index' => $segment_index++, |
331
|
|
|
'media_data' => base64_encode(fread($media, self::UPLOAD_CHUNK)), |
332
|
|
|
] |
333
|
|
|
); |
334
|
|
|
} |
335
|
|
|
fclose($media); |
336
|
|
|
// Finalize |
337
|
|
|
$finalize = $this->http( |
338
|
|
|
'POST', |
339
|
|
|
self::UPLOAD_HOST, |
340
|
|
|
'media/upload', |
341
|
|
|
[ |
342
|
|
|
'command' => 'FINALIZE', |
343
|
|
|
'media_id' => $init->media_id_string, |
344
|
|
|
] |
345
|
|
|
); |
346
|
|
|
|
347
|
|
|
return $finalize; |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* @param string $method |
352
|
|
|
* @param string $host |
353
|
|
|
* @param string $path |
354
|
|
|
* @param array $parameters |
355
|
|
|
* @param array $headers |
356
|
|
|
* @return Response |
357
|
|
|
* @throws BadRequest |
358
|
|
|
* @throws Forbidden |
359
|
|
|
* @throws NotAuthorized |
360
|
|
|
* @throws NotFound |
361
|
|
|
* @throws RateLimit |
362
|
|
|
* @throws ServerError |
363
|
|
|
* @throws ServiceUnavailable |
364
|
|
|
*/ |
365
|
|
|
private function http($method, $host, $path, array $parameters, $headers = []) |
366
|
|
|
{ |
367
|
|
|
$this->method = $method; |
368
|
|
|
$this->resource = $path; |
369
|
|
|
$this->resetLastResponse(); |
370
|
|
|
if(strpos($path, TONUpload::DEFAULT_DOMAIN) === 0) { |
371
|
|
|
$url = $path; |
372
|
|
|
} else { |
373
|
|
|
if($host == self::UPLOAD_HOST){ |
374
|
|
|
$url = sprintf('%s/%s/%s', $host, self::API_REST_VERSION, $path); |
375
|
|
|
} else { |
376
|
|
|
$url = sprintf('%s/%s/%s', $host, self::API_VERSION, $path); |
377
|
|
|
} |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
$this->response->setApiPath($path); |
381
|
|
|
$result = $this->oAuthRequest($url, $method, $parameters, $headers); |
382
|
|
|
$response = JsonDecoder::decode($result, $this->decodeJsonAsArray); |
383
|
|
|
$this->response->setBody($response); |
384
|
|
|
if ($this->getLastHttpCode() > 399) { |
385
|
|
|
$this->manageErrors($response); |
386
|
|
|
} |
387
|
|
|
return $this->response; |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* @param $response |
392
|
|
|
* @throws BadRequest |
393
|
|
|
* @throws Forbidden |
394
|
|
|
* @throws NotAuthorized |
395
|
|
|
* @throws NotFound |
396
|
|
|
* @throws RateLimit |
397
|
|
|
* @throws ServerError |
398
|
|
|
* @throws ServiceUnavailable |
399
|
|
|
*/ |
400
|
|
|
public function manageErrors($response){ |
401
|
|
|
switch ($this->getLastHttpCode()) { |
402
|
|
|
case 400: |
403
|
|
|
throw new BadRequest(TwitterAdsException::BAD_REQUEST, 400, null, $response->errors); |
404
|
|
|
case 401: |
405
|
|
|
throw new NotAuthorized(TwitterAdsException::NOT_AUTHORIZED, 401, null, $response->errors); |
406
|
|
|
case 403: |
407
|
|
|
throw new Forbidden(TwitterAdsException::FORBIDDEN, 403, null, $response->errors); |
408
|
|
|
case 404: |
409
|
|
|
throw new NotFound(TwitterAdsException::NOT_FOUND, 404, null, $response->errors); |
410
|
|
|
case 429: |
411
|
|
|
throw new RateLimit(TwitterAdsException::RATE_LIMIT, 429, null, $response->errors, $this->response->getsHeaders()); |
412
|
|
|
case 500: |
413
|
|
|
throw new ServerError(TwitterAdsException::SERVER_ERROR, 500, null, $response->errors); |
414
|
|
|
case 503: |
415
|
|
|
throw new ServiceUnavailable(TwitterAdsException::SERVICE_UNAVAILABLE, 503, null, $response->errors, $this->response->getsHeaders()); |
416
|
|
|
default: |
417
|
|
|
throw new ServerError(TwitterAdsException::SERVER_ERROR, 500, null, $response->errors); |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
/** |
422
|
|
|
* Format and sign an OAuth / API request. |
423
|
|
|
* |
424
|
|
|
* @param string $url |
425
|
|
|
* @param string $method |
426
|
|
|
* @param array $parameters |
427
|
|
|
* |
428
|
|
|
* @param array $headers |
429
|
|
|
* @return string |
430
|
|
|
* @throws TwitterAdsException |
431
|
|
|
* @throws TwitterOAuthException |
432
|
|
|
*/ |
433
|
|
|
private function oAuthRequest($url, $method, array $parameters, $headers = []) |
434
|
|
|
{ |
435
|
|
|
$request = Request::fromConsumerAndToken($this->consumer, $this->token, $method, $url, $parameters); |
436
|
|
|
if (array_key_exists('oauth_callback', $parameters)) { |
437
|
|
|
// Twitter doesn't like oauth_callback as a parameter. |
438
|
|
|
unset($parameters['oauth_callback']); |
439
|
|
|
} |
440
|
|
|
if ($this->bearer === null) { |
441
|
|
|
$request->signRequest($this->signatureMethod, $this->consumer, $this->token); |
442
|
|
|
$authorization = $request->toHeader(); |
443
|
|
|
} else { |
444
|
|
|
$authorization = 'Authorization: Bearer '.$this->bearer; |
445
|
|
|
} |
446
|
|
|
if(strpos($url, TONUpload::DEFAULT_DOMAIN) === 0) { |
447
|
|
|
return $this->request($url, $method, $authorization, $parameters, $headers); |
448
|
|
|
} else { |
449
|
|
|
return $this->request($request->getNormalizedHttpUrl(), $method, $authorization, $parameters, $headers); |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
/** |
455
|
|
|
* Make an HTTP request. |
456
|
|
|
* |
457
|
|
|
* @param string $url |
458
|
|
|
* @param string $method |
459
|
|
|
* @param string $authorization |
460
|
|
|
* @param array $postfields |
461
|
|
|
* |
462
|
|
|
* @param array $headers |
463
|
|
|
* @return string |
464
|
|
|
* @throws TwitterAdsException |
465
|
|
|
*/ |
466
|
|
|
private function request($url, $method, $authorization, $postfields, $headers = []) |
467
|
|
|
{ |
468
|
|
|
/* Curl settings */ |
469
|
|
|
$options = [ |
470
|
|
|
CURLOPT_VERBOSE => false, |
471
|
|
|
CURLOPT_CAINFO => __DIR__.DIRECTORY_SEPARATOR.'cacert.pem', |
472
|
|
|
CURLOPT_CONNECTTIMEOUT => $this->connectionTimeout, |
473
|
|
|
CURLOPT_HEADER => true, |
474
|
|
|
CURLOPT_HTTPHEADER => array_merge(['Accept: */*', $authorization, 'Expect:'],$headers,['Connection: close']), |
475
|
|
|
CURLOPT_RETURNTRANSFER => true, |
476
|
|
|
CURLOPT_SSL_VERIFYHOST => 2, |
477
|
|
|
CURLOPT_SSL_VERIFYPEER => true, |
478
|
|
|
CURLOPT_TIMEOUT => $this->timeout, |
479
|
|
|
CURLOPT_URL => $url, |
480
|
|
|
CURLOPT_USERAGENT => $this->userAgent, |
481
|
|
|
CURLOPT_ENCODING => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', |
482
|
|
|
]; |
483
|
|
|
|
484
|
|
|
if (!empty($this->proxy)) { |
485
|
|
|
$options[CURLOPT_PROXY] = $this->proxy['CURLOPT_PROXY']; |
486
|
|
|
$options[CURLOPT_PROXYUSERPWD] = $this->proxy['CURLOPT_PROXYUSERPWD']; |
487
|
|
|
$options[CURLOPT_PROXYPORT] = $this->proxy['CURLOPT_PROXYPORT']; |
488
|
|
|
$options[CURLOPT_PROXYAUTH] = CURLAUTH_BASIC; |
489
|
|
|
$options[CURLOPT_PROXYTYPE] = CURLPROXY_HTTP; |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
switch ($method) { |
493
|
|
|
case 'GET': |
494
|
|
|
break; |
495
|
|
|
case 'POST': |
496
|
|
|
$options[CURLOPT_POST] = true; |
497
|
|
|
if(isset($postfields['raw'])){ |
498
|
|
|
$options[CURLOPT_POSTFIELDS] = $postfields['raw']; |
499
|
|
|
} else { |
500
|
|
|
$options[CURLOPT_POSTFIELDS] = Util::buildHttpQuery($postfields); |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
break; |
504
|
|
|
case 'DELETE': |
505
|
|
|
$options[CURLOPT_CUSTOMREQUEST] = 'DELETE'; |
506
|
|
|
break; |
507
|
|
|
case 'PUT': |
508
|
|
|
$options[CURLOPT_CUSTOMREQUEST] = 'PUT'; |
509
|
|
|
if(isset($postfields['raw'])){ |
510
|
|
|
$options[CURLOPT_POSTFIELDS] = $postfields['raw']; |
511
|
|
|
} |
512
|
|
|
break; |
513
|
|
|
} |
514
|
|
|
|
515
|
|
|
if (in_array($method, ['GET', 'PUT', 'DELETE']) && !empty($postfields) && !isset($postfields['raw'])) { |
516
|
|
|
$options[CURLOPT_URL] .= '?'.Util::buildHttpQuery($postfields); |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
$curlHandle = curl_init(); |
520
|
|
|
curl_setopt_array($curlHandle, $options); |
521
|
|
|
$response = curl_exec($curlHandle); |
522
|
|
|
// Throw exceptions on cURL errors. |
523
|
|
|
if (curl_errno($curlHandle) > 0) { |
524
|
|
|
throw new TwitterAdsException(curl_error($curlHandle), curl_errno($curlHandle), null, null); |
525
|
|
|
} |
526
|
|
|
$this->response->setHttpCode(curl_getinfo($curlHandle, CURLINFO_HTTP_CODE)); |
527
|
|
|
$parts = explode("\r\n\r\n", $response); |
528
|
|
|
$responseBody = array_pop($parts); |
529
|
|
|
$responseHeader = array_pop($parts); |
530
|
|
|
$this->response->setHeaders($this->parseHeaders($responseHeader)); |
531
|
|
|
curl_close($curlHandle); |
532
|
|
|
return $responseBody; |
533
|
|
|
} |
534
|
|
|
|
535
|
|
|
/** |
536
|
|
|
* Get the header info to store. |
537
|
|
|
* |
538
|
|
|
* @param string $header |
539
|
|
|
* |
540
|
|
|
* @return array |
541
|
|
|
*/ |
542
|
|
|
private function parseHeaders($header) |
543
|
|
|
{ |
544
|
|
|
$headers = []; |
545
|
|
|
foreach (explode("\r\n", $header) as $line) { |
546
|
|
|
if (strpos($line, ':') !== false) { |
547
|
|
|
list($key, $value) = explode(': ', $line); |
548
|
|
|
$key = str_replace('-', '_', strtolower($key)); |
549
|
|
|
$headers[$key] = trim($value); |
550
|
|
|
} |
551
|
|
|
} |
552
|
|
|
|
553
|
|
|
return $headers; |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
/** |
557
|
|
|
* Encode application authorization header with base64. |
558
|
|
|
* |
559
|
|
|
* @param Consumer $consumer |
560
|
|
|
* |
561
|
|
|
* @return string |
562
|
|
|
*/ |
563
|
|
|
private function encodeAppAuthorization($consumer) |
564
|
|
|
{ |
565
|
|
|
// TODO: key and secret should be rfc 1738 encoded |
566
|
|
|
$key = $consumer->key; |
567
|
|
|
$secret = $consumer->secret; |
568
|
|
|
|
569
|
|
|
return base64_encode($key.':'.$secret); |
570
|
|
|
} |
571
|
|
|
|
572
|
|
|
/** |
573
|
|
|
* Return current response. Allows inheritance. |
574
|
|
|
* |
575
|
|
|
* @return Response |
576
|
|
|
*/ |
577
|
|
|
public function getResponse() |
578
|
|
|
{ |
579
|
|
|
return $this->response; |
580
|
|
|
} |
581
|
|
|
|
582
|
|
|
/** |
583
|
|
|
* @return string |
584
|
|
|
*/ |
585
|
|
|
public function getMethod() |
586
|
|
|
{ |
587
|
|
|
return $this->method; |
588
|
|
|
} |
589
|
|
|
|
590
|
|
|
/** |
591
|
|
|
* @return string |
592
|
|
|
*/ |
593
|
|
|
public function getResource() |
594
|
|
|
{ |
595
|
|
|
return $this->resource; |
596
|
|
|
} |
597
|
|
|
} |
598
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.