Passed
Pull Request — master (#1144)
by
unknown
01:41
created

Facebook::next()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
ccs 0
cts 2
cp 0
crap 2
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\Authentication\OAuth2Client;
27
use Facebook\FileUpload\File;
28
use Facebook\FileUpload\ResumableUploader;
29
use Facebook\FileUpload\TransferChunk;
30
use Facebook\FileUpload\Video;
31
use Facebook\GraphNode\GraphEdge;
32
use Facebook\Url\UrlDetectionInterface;
33
use Facebook\Url\UrlDetectionHandler;
34
use Facebook\PersistentData\PersistentDataFactory;
35
use Facebook\PersistentData\PersistentDataInterface;
36
use Facebook\Helper\CanvasHelper;
37
use Facebook\Helper\JavaScriptHelper;
38
use Facebook\Helper\PageTabHelper;
39
use Facebook\Helper\RedirectLoginHelper;
40
use Facebook\Exception\SDKException;
41
use Http\Client\HttpClient;
42
43
/**
44
 * @package Facebook
45
 */
46
class Facebook
47
{
48
    /**
49
     * @const string Version number of the Facebook PHP SDK.
50
     */
51
    const VERSION = '6.0-dev';
52
53
    /**
54
     * @const string The name of the environment variable that contains the app ID.
55
     */
56
    const APP_ID_ENV_NAME = 'FACEBOOK_APP_ID';
57
58
    /**
59
     * @const string The name of the environment variable that contains the app secret.
60
     */
61
    const APP_SECRET_ENV_NAME = 'FACEBOOK_APP_SECRET';
62
63
    /**
64
     * @var Application the Application entity
65
     */
66
    protected $app;
67
68
    /**
69
     * @var Client the Facebook client service
70
     */
71
    protected $client;
72
73
    /**
74
     * @var OAuth2Client The OAuth 2.0 client service.
75
     */
76
    protected $oAuth2Client;
77
78
    /**
79
     * @var null|UrlDetectionInterface the URL detection handler
80
     */
81
    protected $urlDetectionHandler;
82
83
    /**
84
     * @var null|AccessToken the default access token to use with requests
85
     */
86
    protected $defaultAccessToken;
87
88
    /**
89
     * @var null|string the default Graph version we want to use
90
     */
91
    protected $defaultGraphVersion;
92
93
    /**
94
     * @var null|PersistentDataInterface the persistent data handler
95
     */
96
    protected $persistentDataHandler;
97
98
    /**
99
     * @var null|BatchResponse|Response stores the last request made to Graph
100
     */
101
    protected $lastResponse;
102
103
    /**
104
     * Instantiates a new Facebook super-class object.
105
     *
106
     * @param array $config
107
     *
108
     * @throws SDKException
109
     */
110 17
    public function __construct(array $config = [])
111
    {
112 17
        $config = array_merge([
113 17
            'app_id' => getenv(static::APP_ID_ENV_NAME),
114 17
            'app_secret' => getenv(static::APP_SECRET_ENV_NAME),
115
            'default_graph_version' => null,
116
            'enable_beta_mode' => false,
117
            'http_client' => null,
118
            'persistent_data_handler' => null,
119
            'url_detection_handler' => null,
120 17
        ], $config);
121
122 17
        if (!$config['app_id']) {
123 1
            throw new SDKException('Required "app_id" key not supplied in config and could not find fallback environment variable "' . static::APP_ID_ENV_NAME . '"');
124
        }
125 16
        if (!$config['app_secret']) {
126 1
            throw new SDKException('Required "app_secret" key not supplied in config and could not find fallback environment variable "' . static::APP_SECRET_ENV_NAME . '"');
127
        }
128 15
        if ($config['http_client'] !== null && !$config['http_client'] instanceof HttpClient) {
129 2
            throw new \InvalidArgumentException('Required "http_client" key to be null or an instance of \Http\Client\HttpClient');
130
        }
131 13
        if (!$config['default_graph_version']) {
132 1
            throw new \InvalidArgumentException('Required "default_graph_version" key not supplied in config');
133
        }
134
135 12
        $this->app = new Application($config['app_id'], $config['app_secret']);
136 12
        $this->client = new Client(
137 12
            $config['http_client'],
138 12
            $config['enable_beta_mode']
139
        );
140 12
        $this->setUrlDetectionHandler($config['url_detection_handler'] ?: new UrlDetectionHandler());
141 11
        $this->persistentDataHandler = PersistentDataFactory::createPersistentDataHandler(
142 11
            $config['persistent_data_handler']
143
        );
144
145 10
        if (isset($config['default_access_token'])) {
146 3
            $this->setDefaultAccessToken($config['default_access_token']);
147
        }
148
149 9
        $this->defaultGraphVersion = $config['default_graph_version'];
150 9
    }
151
152
    /**
153
     * Returns the Application entity.
154
     *
155
     * @return Application
156
     */
157 2
    public function getApplication(): Application
158
    {
159 2
        return $this->app;
160
    }
161
162
    /**
163
     * Returns the Client service.
164
     *
165
     * @return Client
166
     */
167 4
    public function getClient(): Client
168
    {
169 4
        return $this->client;
170
    }
171
172
    /**
173
     * Returns the OAuth 2.0 client service.
174
     *
175
     * @return OAuth2Client
176
     */
177 2
    public function getOAuth2Client(): OAuth2Client
178
    {
179 2
        if (!$this->oAuth2Client instanceof OAuth2Client) {
0 ignored issues
show
introduced by
$this->oAuth2Client is always a sub-type of Facebook\Authentication\OAuth2Client.
Loading history...
180 2
            $app = $this->getApplication();
181 2
            $client = $this->getClient();
182 2
            $this->oAuth2Client = new OAuth2Client($app, $client, $this->defaultGraphVersion);
183
        }
184
185 2
        return $this->oAuth2Client;
186
    }
187
188
    /**
189
     * Returns the last response returned from Graph.
190
     *
191
     * @return null|BatchResponse|Response
192
     */
193
    public function getLastResponse()
194
    {
195
        return $this->lastResponse;
196
    }
197
198
    /**
199
     * Returns the URL detection handler.
200
     *
201
     * @return UrlDetectionInterface
202
     */
203 1
    public function getUrlDetectionHandler(): UrlDetectionInterface
204
    {
205 1
        return $this->urlDetectionHandler;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->urlDetectionHandler could return the type null which is incompatible with the type-hinted return Facebook\Url\UrlDetectionInterface. Consider adding an additional type-check to rule them out.
Loading history...
206
    }
207
208
    /**
209
     * Changes the URL detection handler.
210
     *
211
     * @param UrlDetectionInterface $urlDetectionHandler
212
     */
213 11
    private function setUrlDetectionHandler(UrlDetectionInterface $urlDetectionHandler): void
214
    {
215 11
        $this->urlDetectionHandler = $urlDetectionHandler;
216 11
    }
217
218
    /**
219
     * Returns the default AccessToken entity.
220
     *
221
     * @return null|AccessToken
222
     */
223 2
    public function getDefaultAccessToken(): ?AccessToken
224
    {
225 2
        return $this->defaultAccessToken;
226
    }
227
228
    /**
229
     * Sets the default access token to use with requests.
230
     *
231
     * @param AccessToken|string $accessToken the access token to save
232
     *
233
     * @throws \InvalidArgumentException
234
     */
235 5
    public function setDefaultAccessToken($accessToken): void
236
    {
237 5
        if (is_string($accessToken)) {
238 3
            $this->defaultAccessToken = new AccessToken($accessToken);
239
240 3
            return;
241
        }
242
243 2
        if ($accessToken instanceof AccessToken) {
0 ignored issues
show
introduced by
$accessToken is always a sub-type of Facebook\Authentication\AccessToken.
Loading history...
244 1
            $this->defaultAccessToken = $accessToken;
245
246 1
            return;
247
        }
248
249 1
        throw new \InvalidArgumentException('The default access token must be of type "string" or Facebook\AccessToken');
250
    }
251
252
    /**
253
     * Returns the default Graph version.
254
     *
255
     * @return string
256
     */
257
    public function getDefaultGraphVersion(): string
258
    {
259
        return $this->defaultGraphVersion;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->defaultGraphVersion could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
260
    }
261
262
    /**
263
     * Returns the redirect login helper.
264
     *
265
     * @return RedirectLoginHelper
266
     */
267 2
    public function getRedirectLoginHelper(): RedirectLoginHelper
268
    {
269 2
        return new RedirectLoginHelper(
270 2
            $this->getOAuth2Client(),
271 2
            $this->persistentDataHandler,
272 2
            $this->urlDetectionHandler
273
        );
274
    }
275
276
    /**
277
     * Returns the JavaScript helper.
278
     *
279
     * @return JavaScriptHelper
280
     */
281
    public function getJavaScriptHelper(): JavaScriptHelper
282
    {
283
        return new JavaScriptHelper($this->app, $this->client, $this->defaultGraphVersion);
0 ignored issues
show
Bug introduced by
It seems like $this->defaultGraphVersion can also be of type null; however, parameter $graphVersion of Facebook\Helper\JavaScriptHelper::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

283
        return new JavaScriptHelper($this->app, $this->client, /** @scrutinizer ignore-type */ $this->defaultGraphVersion);
Loading history...
284
    }
285
286
    /**
287
     * Returns the canvas helper.
288
     *
289
     * @return CanvasHelper
290
     */
291
    public function getCanvasHelper(): CanvasHelper
292
    {
293
        return new CanvasHelper($this->app, $this->client, $this->defaultGraphVersion);
0 ignored issues
show
Bug introduced by
It seems like $this->defaultGraphVersion can also be of type null; however, parameter $graphVersion of Facebook\Helper\CanvasHelper::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

293
        return new CanvasHelper($this->app, $this->client, /** @scrutinizer ignore-type */ $this->defaultGraphVersion);
Loading history...
294
    }
295
296
    /**
297
     * Returns the page tab helper.
298
     *
299
     * @return PageTabHelper
300
     */
301
    public function getPageTabHelper(): PageTabHelper
302
    {
303
        return new PageTabHelper($this->app, $this->client, $this->defaultGraphVersion);
0 ignored issues
show
Bug introduced by
It seems like $this->defaultGraphVersion can also be of type null; however, parameter $graphVersion of Facebook\Helper\PageTabHelper::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

303
        return new PageTabHelper($this->app, $this->client, /** @scrutinizer ignore-type */ $this->defaultGraphVersion);
Loading history...
304
    }
305
306
    /**
307
     * Sends a GET request to Graph and returns the result.
308
     *
309
     * @param string                  $endpoint
310
     * @param null|AccessToken|string $accessToken
311
     * @param string                  $eTag
312
     * @param null|string             $graphVersion
313
     *
314
     * @throws SDKException
315
     *
316
     * @return Response
317
     */
318
    public function get(
319
        string $endpoint,
320
        $accessToken = null,
321
        string $eTag = '',
322
        ?string $graphVersion = null
323
    ): Response {
324
        return $this->sendRequest(
325
            'GET',
326
            $endpoint,
327
            $params = [],
328
            $accessToken,
329
            $eTag,
330
            $graphVersion
331
        );
332
    }
333
334
    /**
335
     * Sends a POST request to Graph and returns the result.
336
     *
337
     * @param string                  $endpoint
338
     * @param array                   $params
339
     * @param null|AccessToken|string $accessToken
340
     * @param string                  $eTag
341
     * @param null|string             $graphVersion
342
     *
343
     * @throws SDKException
344
     *
345
     * @return Response
346
     */
347
    public function post(
348
        string $endpoint,
349
        array $params = [],
350
        $accessToken = null,
351
        string $eTag = '',
352
        ?string $graphVersion = null
353
    ): Response {
354
        return $this->sendRequest(
355
            'POST',
356
            $endpoint,
357
            $params,
358
            $accessToken,
359
            $eTag,
360
            $graphVersion
361
        );
362
    }
363
364
    /**
365
     * Sends a DELETE request to Graph and returns the result.
366
     *
367
     * @param string                  $endpoint
368
     * @param array                   $params
369
     * @param null|AccessToken|string $accessToken
370
     * @param string                  $eTag
371
     * @param null|string             $graphVersion
372
     *
373
     * @throws SDKException
374
     *
375
     * @return Response
376
     */
377
    public function delete(
378
        string $endpoint,
379
        array $params = [],
380
        $accessToken = null,
381
        string $eTag = '',
382
        ?string $graphVersion = null
383
    ): Response {
384
        return $this->sendRequest(
385
            'DELETE',
386
            $endpoint,
387
            $params,
388
            $accessToken,
389
            $eTag,
390
            $graphVersion
391
        );
392
    }
393
394
    /**
395
     * Sends a request to Graph for the next page of results.
396
     *
397
     * @param GraphEdge $graphEdge the GraphEdge to paginate over
398
     *
399
     * @throws SDKException
400
     *
401
     * @return null|GraphEdge
402
     */
403
    public function next(GraphEdge $graphEdge): ?GraphEdge
404
    {
405
        return $this->getPaginationResults($graphEdge, 'next');
406
    }
407
408
    /**
409
     * Sends a request to Graph for the previous page of results.
410
     *
411
     * @param GraphEdge $graphEdge the GraphEdge to paginate over
412
     *
413
     * @throws SDKException
414
     *
415
     * @return null|GraphEdge
416
     */
417
    public function previous(GraphEdge $graphEdge): ?GraphEdge
418
    {
419
        return $this->getPaginationResults($graphEdge, 'previous');
420
    }
421
422
    /**
423
     * Sends a request to Graph for the next page of results.
424
     *
425
     * @param GraphEdge $graphEdge the GraphEdge to paginate over
426
     * @param string    $direction the direction of the pagination: next|previous
427
     *
428
     * @throws SDKException
429
     *
430
     * @return null|GraphEdge
431
     */
432
    public function getPaginationResults(GraphEdge $graphEdge, string $direction): ?GraphEdge
433
    {
434
        $paginationRequest = $graphEdge->getPaginationRequest($direction);
435
        if (!$paginationRequest) {
436
            return null;
437
        }
438
439
        $this->lastResponse = $this->client->sendRequest($paginationRequest);
440
441
        // Keep the same GraphNode subclass
442
        $subClassName = $graphEdge->getSubClassName();
443
        $graphEdge = $this->lastResponse->getGraphEdge($subClassName, false);
444
445
        return count($graphEdge) > 0 ? $graphEdge : null;
446
    }
447
448
    /**
449
     * Sends a request to Graph and returns the result.
450
     *
451
     * @param string                  $method
452
     * @param string                  $endpoint
453
     * @param array                   $params
454
     * @param null|AccessToken|string $accessToken
455
     * @param string                  $eTag
456
     * @param null|string             $graphVersion
457
     *
458
     * @throws SDKException
459
     *
460
     * @return Response
461
     */
462
    public function sendRequest(
463
        string $method,
464
        string $endpoint,
465
        array $params = [],
466
        $accessToken = null,
467
        string $eTag = '',
468
        ?string $graphVersion = null
469
    ): Response {
470
        $accessToken = $accessToken ?: $this->defaultAccessToken;
471
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
472
        $request = $this->request($method, $endpoint, $params, $accessToken, $eTag, $graphVersion);
473
474
        return $this->lastResponse = $this->client->sendRequest($request);
475
    }
476
477
    /**
478
     * Sends a batched request to Graph and returns the result.
479
     *
480
     * @param array                   $requests
481
     * @param null|AccessToken|string $accessToken
482
     * @param null|string             $graphVersion
483
     *
484
     * @throws SDKException
485
     *
486
     * @return BatchResponse
487
     */
488
    public function sendBatchRequest(array $requests, $accessToken = null, ?string $graphVersion = null): BatchResponse
489
    {
490
        $accessToken = $accessToken ?: $this->defaultAccessToken;
491
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
492
        $batchRequest = new BatchRequest(
493
            $this->app,
494
            $requests,
495
            $accessToken,
496
            $graphVersion
497
        );
498
499
        return $this->lastResponse = $this->client->sendBatchRequest($batchRequest);
500
    }
501
502
    /**
503
     * Instantiates an empty BatchRequest entity.
504
     *
505
     * @param null|AccessToken|string $accessToken  The top-level access token. Requests with no access token
506
     *                                              will fallback to this.
507
     * @param null|string             $graphVersion the Graph API version to use
508
     *
509
     * @return BatchRequest
510
     */
511 1
    public function newBatchRequest($accessToken = null, ?string $graphVersion = null): BatchRequest
512
    {
513 1
        $accessToken = $accessToken ?: $this->defaultAccessToken;
514 1
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
515
516 1
        return new BatchRequest(
517 1
            $this->app,
518 1
            [],
519 1
            $accessToken,
520 1
            $graphVersion
521
        );
522
    }
523
524
    /**
525
     * Instantiates a new Request entity.
526
     *
527
     * @param string                  $method
528
     * @param string                  $endpoint
529
     * @param array                   $params
530
     * @param null|AccessToken|string $accessToken
531
     * @param string                  $eTag
532
     * @param null|string             $graphVersion
533
     *
534
     * @throws SDKException
535
     *
536
     * @return Request
537
     */
538 1
    public function request(
539
        string $method,
540
        string $endpoint,
541
        array $params = [],
542
        $accessToken = null,
543
        string $eTag = '',
544
        ?string $graphVersion = null
545
    ): Request {
546 1
        $accessToken = $accessToken ?: $this->defaultAccessToken;
547 1
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
548
549 1
        return new Request(
550 1
            $this->app,
551 1
            $accessToken,
552 1
            $method,
553 1
            $endpoint,
554 1
            $params,
555 1
            $eTag,
556 1
            $graphVersion
557
        );
558
    }
559
560
    /**
561
     * Factory to create File's.
562
     *
563
     * @param string $pathToFile
564
     *
565
     * @throws SDKException
566
     *
567
     * @return File
568
     */
569
    public function fileToUpload(string $pathToFile): File
570
    {
571
        return new File($pathToFile);
572
    }
573
574
    /**
575
     * Factory to create Video's.
576
     *
577
     * @param string $pathToFile
578
     *
579
     * @throws SDKException
580
     *
581
     * @return Video
582
     */
583 2
    public function videoToUpload(string $pathToFile): Video
584
    {
585 2
        return new Video($pathToFile);
586
    }
587
588
    /**
589
     * Upload a video in chunks.
590
     *
591
     * @param int         $target           the id of the target node before the /videos edge
592
     * @param string      $pathToFile       the full path to the file
593
     * @param array       $metadata         the metadata associated with the video file
594
     * @param null|string $accessToken      the access token
595
     * @param int         $maxTransferTries the max times to retry a failed upload chunk
596
     * @param null|string $graphVersion     the Graph API version to use
597
     *
598
     * @throws SDKException
599
     *
600
     * @return array
601
     */
602 2
    public function uploadVideo(
603
        int $target,
604
        string $pathToFile,
605
        array $metadata = [],
606
        $accessToken = null,
607
        int $maxTransferTries = 5,
608
        ?string $graphVersion = null
609
    ): array {
610 2
        $accessToken = $accessToken ?: $this->defaultAccessToken;
611 2
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
612
613 2
        $uploader = new ResumableUploader($this->app, $this->client, $accessToken, $graphVersion);
0 ignored issues
show
Bug introduced by
It seems like $graphVersion can also be of type null; however, parameter $graphVersion of Facebook\FileUpload\Resu...Uploader::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

613
        $uploader = new ResumableUploader($this->app, $this->client, $accessToken, /** @scrutinizer ignore-type */ $graphVersion);
Loading history...
614 2
        $endpoint = '/'.$target.'/videos';
615 2
        $file = $this->videoToUpload($pathToFile);
616 2
        $chunk = $uploader->start($endpoint, $file);
617
618
        do {
619 2
            $chunk = $this->maxTriesTransfer($uploader, $endpoint, $chunk, $maxTransferTries);
620 1
        } while (!$chunk->isLastChunk());
621
622
        return [
623 1
          'video_id' => $chunk->getVideoId(),
624 1
          'success' => $uploader->finish($endpoint, $chunk->getUploadSessionId(), $metadata),
625
        ];
626
    }
627
628
    /**
629
     * Attempts to upload a chunk of a file in $retryCountdown tries.
630
     *
631
     * @param ResumableUploader $uploader
632
     * @param string            $endpoint
633
     * @param TransferChunk     $chunk
634
     * @param int               $retryCountdown
635
     *
636
     * @throws SDKException
637
     *
638
     * @return TransferChunk
639
     */
640 2
    private function maxTriesTransfer(
641
        ResumableUploader $uploader,
642
        string $endpoint,
643
        TransferChunk $chunk,
644
        int $retryCountdown
645
    ): TransferChunk {
646 2
        $newChunk = $uploader->transfer($endpoint, $chunk, $retryCountdown < 1);
647
648 2
        if ($newChunk !== $chunk) {
649 1
            return $newChunk;
650
        }
651
652 1
        $retryCountdown--;
653
654
        // If transfer() returned the same chunk entity, the transfer failed but is resumable.
655 1
        return $this->maxTriesTransfer($uploader, $endpoint, $chunk, $retryCountdown);
656
    }
657
}
658