Passed
Pull Request — master (#957)
by
unknown
02:52
created

Request::useJson()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
/**
3
 * Copyright 2017 Facebook, Inc.
4
 *
5
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
6
 * use, copy, modify, and distribute this software in source code or binary
7
 * form for use in connection with the web services and APIs provided by
8
 * Facebook.
9
 *
10
 * As with any software that integrates with the Facebook platform, your use
11
 * of this software is subject to the Facebook Developer Principles and
12
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13
 * shall be included in all copies or substantial portions of the software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
 * DEALINGS IN THE SOFTWARE.
22
 */
23
namespace Facebook;
24
25
use Facebook\Authentication\AccessToken;
26
use Facebook\Url\UrlManipulator;
27
use Facebook\FileUpload\File;
28
use Facebook\FileUpload\Video;
29
use Facebook\Http\RequestBodyMultipart;
30
use Facebook\Http\RequestBodyUrlEncoded;
31
use Facebook\Http\RequestBodyJsonEncoded;
32
use Facebook\Exception\SDKException;
33
34
/**
35
 * Class Request.
36
 *
37
 * @package Facebook
38
 */
39
class Request
40
{
41
    /**
42
     * @var Application the Facebook app entity
43
     */
44
    protected $app;
45
46
    /**
47
     * @var null|string the access token to use for this request
48
     */
49
    protected $accessToken;
50
51
    /**
52
     * @var string the HTTP method for this request
53
     */
54
    protected $method;
55
56
    /**
57
     * @var string the Graph endpoint for this request
58
     */
59
    protected $endpoint;
60
61
    /**
62
     * @var array the headers to send with this request
63
     */
64
    protected $headers = [];
65
66
    /**
67
     * @var array the parameters to send with this request
68
     */
69
    protected $params = [];
70
71
    /**
72
     * @var array the files to send with this request
73
     */
74
    protected $files = [];
75
76
    /**
77
     * @var string ETag to send with this request
78
     */
79
    protected $eTag;
80
81
    /**
82
     * @var string graph version to use for this request
83
     */
84
    protected $graphVersion;
85
86
    /**
87
     * @var bool use json-encoded body for this request
88
     */
89
    protected $useJson;
90
91
    /**
92
     * Creates a new Request entity.
93
     *
94
     * @param null|Application        $app
95
     * @param null|AccessToken|string $accessToken
96
     * @param null|string             $method
97
     * @param null|string             $endpoint
98
     * @param null|array              $params
99
     * @param null|string             $eTag
100
     * @param null|string             $graphVersion
101
     */
102 100
    public function __construct(Application $app = null, $accessToken = null, $method = null, $endpoint = null, array $params = [], $eTag = null, $graphVersion = null)
103
    {
104 100
        $this->setApp($app);
105 100
        $this->setAccessToken($accessToken);
106 100
        $this->setMethod($method);
107 100
        $this->setEndpoint($endpoint);
108 100
        $this->setParams($params);
109 99
        $this->setETag($eTag);
110 99
        $this->graphVersion = $graphVersion;
111 99
    }
112
113
    /**
114
     * Set the access token for this request.
115
     *
116
     * @param null|AccessToken|string
117
     * @param mixed $accessToken
118
     *
119
     * @return Request
120
     */
121 100
    public function setAccessToken($accessToken)
122
    {
123 100
        $this->accessToken = $accessToken;
124 100
        if ($accessToken instanceof AccessToken) {
125 6
            $this->accessToken = $accessToken->getValue();
126
        }
127
128 100
        return $this;
129
    }
130
131
    /**
132
     * Sets the access token with one harvested from a URL or POST params.
133
     *
134
     * @param string $accessToken the access token
135
     *
136
     * @throws SDKException
137
     *
138
     * @return Request
139
     */
140 3
    public function setAccessTokenFromParams($accessToken)
141
    {
142 3
        $existingAccessToken = $this->getAccessToken();
143 3
        if (!$existingAccessToken) {
144 1
            $this->setAccessToken($accessToken);
145 2
        } elseif ($accessToken !== $existingAccessToken) {
146 1
            throw new SDKException('Access token mismatch. The access token provided in the Request and the one provided in the URL or POST params do not match.');
147
        }
148
149 2
        return $this;
150
    }
151
152
    /**
153
     * Return the access token for this request.
154
     *
155
     * @return null|string
156
     */
157 52
    public function getAccessToken()
158
    {
159 52
        return $this->accessToken;
160
    }
161
162
    /**
163
     * Return the access token for this request as an AccessToken entity.
164
     *
165
     * @return null|AccessToken
166
     */
167 32
    public function getAccessTokenEntity()
168
    {
169 32
        return $this->accessToken ? new AccessToken($this->accessToken) : null;
170
    }
171
172
    /**
173
     * Set the Application entity used for this request.
174
     *
175
     * @param null|Application $app
176
     */
177 100
    public function setApp(Application $app = null)
178
    {
179 100
        $this->app = $app;
180 100
    }
181
182
    /**
183
     * Return the Application entity used for this request.
184
     *
185
     * @return Application
186
     */
187 28
    public function getApplication()
188
    {
189 28
        return $this->app;
190
    }
191
192
    /**
193
     * Generate an app secret proof to sign this request.
194
     *
195
     * @return null|string
196
     */
197 32
    public function getAppSecretProof()
198
    {
199 32
        if (!$accessTokenEntity = $this->getAccessTokenEntity()) {
200
            return null;
201
        }
202
203 32
        return $accessTokenEntity->getAppSecretProof($this->app->getSecret());
204
    }
205
206
    /**
207
     * Validate that an access token exists for this request.
208
     *
209
     * @throws SDKException
210
     */
211 12
    public function validateAccessToken()
212
    {
213 12
        $accessToken = $this->getAccessToken();
214 12
        if (!$accessToken) {
215 2
            throw new SDKException('You must provide an access token.');
216
        }
217 10
    }
218
219
    /**
220
     * Set the HTTP method for this request.
221
     *
222
     * @param string
223
     * @param mixed $method
224
     */
225 100
    public function setMethod($method)
226
    {
227 100
        $this->method = strtoupper($method);
228 100
    }
229
230
    /**
231
     * Return the HTTP method for this request.
232
     *
233
     * @return string
234
     */
235 28
    public function getMethod()
236
    {
237 28
        return $this->method;
238
    }
239
240
    /**
241
     * Validate that the HTTP method is set.
242
     *
243
     * @throws SDKException
244
     */
245 25
    public function validateMethod()
246
    {
247 25
        if (!$this->method) {
248 1
            throw new SDKException('HTTP method not specified.');
249
        }
250
251 24
        if (!in_array($this->method, ['GET', 'POST', 'DELETE'])) {
252 1
            throw new SDKException('Invalid HTTP method specified.');
253
        }
254 23
    }
255
256
    /**
257
     * Set the endpoint for this request.
258
     *
259
     * @param string
260
     * @param mixed $endpoint
261
     *
262
     * @throws SDKException
263
     *
264
     * @return Request
265
     */
266 100
    public function setEndpoint($endpoint)
267
    {
268
        // Harvest the access token from the endpoint to keep things in sync
269 100
        $params = UrlManipulator::getParamsAsArray($endpoint);
270 100
        if (isset($params['access_token'])) {
271
            $this->setAccessTokenFromParams($params['access_token']);
272
        }
273
274
        // Clean the token & app secret proof from the endpoint.
275 100
        $filterParams = ['access_token', 'appsecret_proof'];
276 100
        $this->endpoint = UrlManipulator::removeParamsFromUrl($endpoint, $filterParams);
277
278 100
        return $this;
279
    }
280
281
    /**
282
     * Return the endpoint for this request.
283
     *
284
     * @return string
285
     */
286 27
    public function getEndpoint()
287
    {
288
        // For batch requests, this will be empty
289 27
        return $this->endpoint;
290
    }
291
292
    /**
293
     * Generate and return the headers for this request.
294
     *
295
     * @return array
296
     */
297 21
    public function getHeaders()
298
    {
299 21
        $headers = static::getDefaultHeaders();
300
301 21
        if ($this->eTag) {
302 1
            $headers['If-None-Match'] = $this->eTag;
303
        }
304
305 21
        return array_merge($this->headers, $headers);
306
    }
307
308
    /**
309
     * Set the headers for this request.
310
     *
311
     * @param array $headers
312
     */
313 12
    public function setHeaders(array $headers)
314
    {
315 12
        $this->headers = array_merge($this->headers, $headers);
316 12
    }
317
318
    /**
319
     * Sets the eTag value.
320
     *
321
     * @param string $eTag
322
     */
323 99
    public function setETag($eTag)
324
    {
325 99
        $this->eTag = $eTag;
326 99
    }
327
328
    /**
329
     * Toggle json encodeing.
330
     *
331
     * @param bool $betaMode
332
     */
333 2
    public function useJson($useJson = true)
334
    {
335 2
        $this->useJson = $useJson;
336 2
    }
337
338
    /**
339
     * Set the params for this request.
340
     *
341
     * @param array $params
342
     *
343
     * @throws SDKException
344
     *
345
     * @return Request
346
     */
347 100
    public function setParams(array $params = [])
348
    {
349 100
        if (isset($params['access_token'])) {
350 3
            $this->setAccessTokenFromParams($params['access_token']);
351
        }
352
353
        // Don't let these buggers slip in.
354 99
        unset($params['access_token'], $params['appsecret_proof']);
355
356
        // @TODO Refactor code above with this
357
        //$params = $this->sanitizeAuthenticationParams($params);
358 99
        $params = $this->sanitizeFileParams($params);
359 99
        $this->dangerouslySetParams($params);
360
361 99
        return $this;
362
    }
363
364
    /**
365
     * Set the params for this request without filtering them first.
366
     *
367
     * @param array $params
368
     *
369
     * @return Request
370
     */
371 99
    public function dangerouslySetParams(array $params = [])
372
    {
373 99
        $this->params = array_merge($this->params, $params);
374
375 99
        return $this;
376
    }
377
378
    /**
379
     * Iterate over the params and pull out the file uploads.
380
     *
381
     * @param array $params
382
     *
383
     * @return array
384
     */
385 99
    public function sanitizeFileParams(array $params)
386
    {
387 99
        foreach ($params as $key => $value) {
388 67
            if ($value instanceof File) {
389 10
                $this->addFile($key, $value);
390 67
                unset($params[$key]);
391
            }
392
        }
393
394 99
        return $params;
395
    }
396
397
    /**
398
     * Add a file to be uploaded.
399
     *
400
     * @param string $key
401
     * @param File   $file
402
     */
403 10
    public function addFile($key, File $file)
404
    {
405 10
        $this->files[$key] = $file;
406 10
    }
407
408
    /**
409
     * Removes all the files from the upload queue.
410
     */
411 3
    public function resetFiles()
412
    {
413 3
        $this->files = [];
414 3
    }
415
416
    /**
417
     * Get the list of files to be uploaded.
418
     *
419
     * @return array
420
     */
421 3
    public function getFiles()
422
    {
423 3
        return $this->files;
424
    }
425
426
    /**
427
     * Let's us know if there is a file upload with this request.
428
     *
429
     * @return bool
430
     */
431 32
    public function containsFileUploads()
432
    {
433 32
        return !empty($this->files);
434
    }
435
436
    /**
437
     * Let's us know if there is a video upload with this request.
438
     *
439
     * @return bool
440
     */
441 14
    public function containsVideoUploads()
442
    {
443 14
        foreach ($this->files as $file) {
444 8
            if ($file instanceof Video) {
445 8
                return true;
446
            }
447
        }
448
449 12
        return false;
450
    }
451
452
    /**
453
     * Returns the body of the request as multipart/form-data.
454
     *
455
     * @return RequestBodyMultipart
456
     */
457 6
    public function getMultipartBody()
458
    {
459 6
        $params = $this->getPostParams();
460
461 6
        return new RequestBodyMultipart($params, $this->files);
462
    }
463
464
    /**
465
     * Returns the body of the request as URL-encoded.
466
     *
467
     * @return RequestBodyUrlEncoded
468
     */
469 17
    public function getUrlEncodedBody()
470
    {
471 17
        $params = $this->getPostParams();
472
473 17
        return new RequestBodyUrlEncoded($params);
474
    }
475
476
    /**
477
     * Returns the body of the request as JSON-encoded.
478
     */
479 1
    public function getJsonEncodedBody()
480
    {
481 1
        return new RequestBodyJsonEncoded($this->params);
482
    }
483
484
    /**
485
     * Generate and return the params for this request.
486
     *
487
     * @return array
488
     */
489 30
    public function getParams()
490
    {
491 30
        $params = $this->params;
492
493 30
        $accessToken = $this->getAccessToken();
494 30
        if ($accessToken) {
495 30
            $params['access_token'] = $accessToken;
496 30
            $params['appsecret_proof'] = $this->getAppSecretProof();
497
        }
498
499 30
        return $params;
500
    }
501
502
    /**
503
     * Only return params on POST requests.
504
     *
505
     * @return array
506
     */
507 19
    public function getPostParams()
508
    {
509 19
        if ($this->getMethod() === 'POST') {
510 13
            return $this->getParams();
511
        }
512
513 9
        return [];
514
    }
515
516
    /**
517
     * The graph version used for this request.
518
     *
519
     * @return null|string
520
     */
521 5
    public function getGraphVersion()
522
    {
523 5
        return $this->graphVersion;
524
    }
525
526
    /**
527
     * Returns if the request body is encoded as json. 
528
     */
529 17
    public function isJson() {
530 17
        return $this->useJson;
531
    }
532
533
    /**
534
     * Generate and return the URL for this request.
535
     *
536
     * @return string
537
     */
538 23
    public function getUrl()
539
    {
540 23
        $this->validateMethod();
541
542 23
        $graphVersion = UrlManipulator::forceSlashPrefix($this->graphVersion);
543 23
        $endpoint = UrlManipulator::forceSlashPrefix($this->getEndpoint());
544
545 23
        $url = $graphVersion . $endpoint;
546
547 23
        if ($this->getMethod() !== 'POST') {
548 12
            $params = $this->getParams();
549 12
            $url = UrlManipulator::appendParamsToUrl($url, $params);
550 15
        }else if($this->isJson()) {
551
            // access token should be embedded into URL instead of request body for json requests.
552 2
            $accessToken = $this->getAccessToken();
553 2
            if ($accessToken) {
554 2
                $url = UrlManipulator::appendParamsToUrl($url, [
555 2
                    'access_token' => $accessToken,
556 2
                    'appsecret_proof' => $this->getAppSecretProof(),
557
                ]);
558
            }
559
        }
560
561 23
        return $url;
562
    }
563
564
    /**
565
     * Return the default headers that every request should use.
566
     *
567
     * @return array
568
     */
569 21
    public static function getDefaultHeaders()
570
    {
571
        return [
572 21
            'User-Agent' => 'fb-php-' . Facebook::VERSION,
573 21
            'Accept-Encoding' => '*',
574
        ];
575
    }
576
}
577