Passed
Push — master ( 43af15...472099 )
by Yassine
02:21 queued 36s
created

Facebook::newBatchRequest()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 1
nop 2
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
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
 */
24
namespace Facebook;
25
26
use Facebook\Authentication\AccessToken;
27
use Facebook\Authentication\OAuth2Client;
28
use Facebook\FileUpload\FacebookFile;
29
use Facebook\FileUpload\FacebookResumableUploader;
30
use Facebook\FileUpload\FacebookTransferChunk;
31
use Facebook\FileUpload\FacebookVideo;
32
use Facebook\GraphNode\GraphEdge;
33
use Facebook\Url\UrlDetectionInterface;
34
use Facebook\Url\FacebookUrlDetectionHandler;
35
use Facebook\PersistentData\PersistentDataFactory;
36
use Facebook\PersistentData\PersistentDataInterface;
37
use Facebook\Helper\FacebookCanvasHelper;
38
use Facebook\Helper\FacebookJavaScriptHelper;
39
use Facebook\Helper\FacebookPageTabHelper;
40
use Facebook\Helper\FacebookRedirectLoginHelper;
41
use Facebook\Exception\FacebookSDKException;
42
use Http\Client\HttpClient;
43
44
/**
45
 * Class Facebook
46
 *
47
 * @package Facebook
48
 */
49
class Facebook
50
{
51
    /**
52
     * @const string Version number of the Facebook PHP SDK.
53
     */
54
    const VERSION = '6.0-dev';
55
56
    /**
57
     * @const string The name of the environment variable that contains the app ID.
58
     */
59
    const APP_ID_ENV_NAME = 'FACEBOOK_APP_ID';
60
61
    /**
62
     * @const string The name of the environment variable that contains the app secret.
63
     */
64
    const APP_SECRET_ENV_NAME = 'FACEBOOK_APP_SECRET';
65
66
    /**
67
     * @var FacebookApp The FacebookApp entity.
68
     */
69
    protected $app;
70
71
    /**
72
     * @var FacebookClient The Facebook client service.
73
     */
74
    protected $client;
75
76
    /**
77
     * @var OAuth2Client The OAuth 2.0 client service.
78
     */
79
    protected $oAuth2Client;
80
81
    /**
82
     * @var UrlDetectionInterface|null The URL detection handler.
83
     */
84
    protected $urlDetectionHandler;
85
86
    /**
87
     * @var AccessToken|null The default access token to use with requests.
88
     */
89
    protected $defaultAccessToken;
90
91
    /**
92
     * @var string|null The default Graph version we want to use.
93
     */
94
    protected $defaultGraphVersion;
95
96
    /**
97
     * @var PersistentDataInterface|null The persistent data handler.
98
     */
99
    protected $persistentDataHandler;
100
101
    /**
102
     * @var FacebookResponse|FacebookBatchResponse|null Stores the last request made to Graph.
103
     */
104
    protected $lastResponse;
105
106
    /**
107
     * Instantiates a new Facebook super-class object.
108
     *
109
     * @param array $config
110
     *
111
     * @throws FacebookSDKException
112
     */
113
    public function __construct(array $config = [])
114
    {
115
        $config = array_merge([
116
            'app_id' => getenv(static::APP_ID_ENV_NAME),
117
            'app_secret' => getenv(static::APP_SECRET_ENV_NAME),
118
            'default_graph_version' => null,
119
            'enable_beta_mode' => false,
120
            'http_client' => null,
121
            'persistent_data_handler' => null,
122
            'url_detection_handler' => null,
123
        ], $config);
124
125
        if (!$config['app_id']) {
126
            throw new FacebookSDKException('Required "app_id" key not supplied in config and could not find fallback environment variable "' . static::APP_ID_ENV_NAME . '"');
127
        }
128
        if (!$config['app_secret']) {
129
            throw new FacebookSDKException('Required "app_secret" key not supplied in config and could not find fallback environment variable "' . static::APP_SECRET_ENV_NAME . '"');
130
        }
131
        if ($config['http_client'] !== null && !$config['http_client'] instanceof HttpClient) {
132
            throw new \InvalidArgumentException('Required "http_client" key to be null or an instance of \Http\Client\HttpClient');
133
        }
134
        if (!$config['default_graph_version']) {
135
            throw new \InvalidArgumentException('Required "default_graph_version" key not supplied in config');
136
        }
137
138
        $this->app = new FacebookApp($config['app_id'], $config['app_secret']);
139
        $this->client = new FacebookClient(
140
            $config['http_client'],
141
            $config['enable_beta_mode']
142
        );
143
        $this->setUrlDetectionHandler($config['url_detection_handler'] ?: new FacebookUrlDetectionHandler());
144
        $this->persistentDataHandler = PersistentDataFactory::createPersistentDataHandler(
145
            $config['persistent_data_handler']
146
        );
147
148
        if (isset($config['default_access_token'])) {
149
            $this->setDefaultAccessToken($config['default_access_token']);
150
        }
151
152
        $this->defaultGraphVersion = $config['default_graph_version'];
153
    }
154
155
    /**
156
     * Returns the FacebookApp entity.
157
     *
158
     * @return FacebookApp
159
     */
160
    public function getApp()
161
    {
162
        return $this->app;
163
    }
164
165
    /**
166
     * Returns the FacebookClient service.
167
     *
168
     * @return FacebookClient
169
     */
170
    public function getClient()
171
    {
172
        return $this->client;
173
    }
174
175
    /**
176
     * Returns the OAuth 2.0 client service.
177
     *
178
     * @return OAuth2Client
179
     */
180
    public function getOAuth2Client()
181
    {
182
        if (!$this->oAuth2Client instanceof OAuth2Client) {
183
            $app = $this->getApp();
184
            $client = $this->getClient();
185
            $this->oAuth2Client = new OAuth2Client($app, $client, $this->defaultGraphVersion);
186
        }
187
188
        return $this->oAuth2Client;
189
    }
190
191
    /**
192
     * Returns the last response returned from Graph.
193
     *
194
     * @return FacebookResponse|FacebookBatchResponse|null
195
     */
196
    public function getLastResponse()
197
    {
198
        return $this->lastResponse;
199
    }
200
201
    /**
202
     * Returns the URL detection handler.
203
     *
204
     * @return UrlDetectionInterface
205
     */
206
    public function getUrlDetectionHandler()
207
    {
208
        return $this->urlDetectionHandler;
209
    }
210
211
    /**
212
     * Changes the URL detection handler.
213
     *
214
     * @param UrlDetectionInterface $urlDetectionHandler
215
     */
216
    private function setUrlDetectionHandler(UrlDetectionInterface $urlDetectionHandler)
217
    {
218
        $this->urlDetectionHandler = $urlDetectionHandler;
219
    }
220
221
    /**
222
     * Returns the default AccessToken entity.
223
     *
224
     * @return AccessToken|null
225
     */
226
    public function getDefaultAccessToken()
227
    {
228
        return $this->defaultAccessToken;
229
    }
230
231
    /**
232
     * Sets the default access token to use with requests.
233
     *
234
     * @param AccessToken|string $accessToken The access token to save.
235
     *
236
     * @throws \InvalidArgumentException
237
     */
238
    public function setDefaultAccessToken($accessToken)
239
    {
240
        if (is_string($accessToken)) {
241
            $this->defaultAccessToken = new AccessToken($accessToken);
242
243
            return;
244
        }
245
246
        if ($accessToken instanceof AccessToken) {
247
            $this->defaultAccessToken = $accessToken;
248
249
            return;
250
        }
251
252
        throw new \InvalidArgumentException('The default access token must be of type "string" or Facebook\AccessToken');
253
    }
254
255
    /**
256
     * Returns the default Graph version.
257
     *
258
     * @return string
259
     */
260
    public function getDefaultGraphVersion()
261
    {
262
        return $this->defaultGraphVersion;
263
    }
264
265
    /**
266
     * Returns the redirect login helper.
267
     *
268
     * @return FacebookRedirectLoginHelper
269
     */
270
    public function getRedirectLoginHelper()
271
    {
272
        return new FacebookRedirectLoginHelper(
273
            $this->getOAuth2Client(),
274
            $this->persistentDataHandler,
275
            $this->urlDetectionHandler
276
        );
277
    }
278
279
    /**
280
     * Returns the JavaScript helper.
281
     *
282
     * @return FacebookJavaScriptHelper
283
     */
284
    public function getJavaScriptHelper()
285
    {
286
        return new FacebookJavaScriptHelper($this->app, $this->client, $this->defaultGraphVersion);
287
    }
288
289
    /**
290
     * Returns the canvas helper.
291
     *
292
     * @return FacebookCanvasHelper
293
     */
294
    public function getCanvasHelper()
295
    {
296
        return new FacebookCanvasHelper($this->app, $this->client, $this->defaultGraphVersion);
297
    }
298
299
    /**
300
     * Returns the page tab helper.
301
     *
302
     * @return FacebookPageTabHelper
303
     */
304
    public function getPageTabHelper()
305
    {
306
        return new FacebookPageTabHelper($this->app, $this->client, $this->defaultGraphVersion);
307
    }
308
309
    /**
310
     * Sends a GET request to Graph and returns the result.
311
     *
312
     * @param string                  $endpoint
313
     * @param AccessToken|string|null $accessToken
314
     * @param string|null             $eTag
315
     * @param string|null             $graphVersion
316
     *
317
     * @return FacebookResponse
318
     *
319
     * @throws FacebookSDKException
320
     */
321 View Code Duplication
    public function get($endpoint, $accessToken = null, $eTag = null, $graphVersion = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
    {
323
        return $this->sendRequest(
324
            'GET',
325
            $endpoint,
326
            $params = [],
327
            $accessToken,
328
            $eTag,
329
            $graphVersion
330
        );
331
    }
332
333
    /**
334
     * Sends a POST request to Graph and returns the result.
335
     *
336
     * @param string                  $endpoint
337
     * @param array                   $params
338
     * @param AccessToken|string|null $accessToken
339
     * @param string|null             $eTag
340
     * @param string|null             $graphVersion
341
     *
342
     * @return FacebookResponse
343
     *
344
     * @throws FacebookSDKException
345
     */
346 View Code Duplication
    public function post($endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
347
    {
348
        return $this->sendRequest(
349
            'POST',
350
            $endpoint,
351
            $params,
352
            $accessToken,
353
            $eTag,
354
            $graphVersion
355
        );
356
    }
357
358
    /**
359
     * Sends a DELETE request to Graph and returns the result.
360
     *
361
     * @param string                  $endpoint
362
     * @param array                   $params
363
     * @param AccessToken|string|null $accessToken
364
     * @param string|null             $eTag
365
     * @param string|null             $graphVersion
366
     *
367
     * @return FacebookResponse
368
     *
369
     * @throws FacebookSDKException
370
     */
371 View Code Duplication
    public function delete($endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372
    {
373
        return $this->sendRequest(
374
            'DELETE',
375
            $endpoint,
376
            $params,
377
            $accessToken,
378
            $eTag,
379
            $graphVersion
380
        );
381
    }
382
383
    /**
384
     * Sends a request to Graph for the next page of results.
385
     *
386
     * @param GraphEdge $graphEdge The GraphEdge to paginate over.
387
     *
388
     * @return GraphEdge|null
389
     *
390
     * @throws FacebookSDKException
391
     */
392
    public function next(GraphEdge $graphEdge)
393
    {
394
        return $this->getPaginationResults($graphEdge, 'next');
395
    }
396
397
    /**
398
     * Sends a request to Graph for the previous page of results.
399
     *
400
     * @param GraphEdge $graphEdge The GraphEdge to paginate over.
401
     *
402
     * @return GraphEdge|null
403
     *
404
     * @throws FacebookSDKException
405
     */
406
    public function previous(GraphEdge $graphEdge)
407
    {
408
        return $this->getPaginationResults($graphEdge, 'previous');
409
    }
410
411
    /**
412
     * Sends a request to Graph for the next page of results.
413
     *
414
     * @param GraphEdge $graphEdge The GraphEdge to paginate over.
415
     * @param string    $direction The direction of the pagination: next|previous.
416
     *
417
     * @return GraphEdge|null
418
     *
419
     * @throws FacebookSDKException
420
     */
421
    public function getPaginationResults(GraphEdge $graphEdge, $direction)
422
    {
423
        $paginationRequest = $graphEdge->getPaginationRequest($direction);
424
        if (!$paginationRequest) {
425
            return null;
426
        }
427
428
        $this->lastResponse = $this->client->sendRequest($paginationRequest);
429
430
        // Keep the same GraphNode subclass
431
        $subClassName = $graphEdge->getSubClassName();
432
        $graphEdge = $this->lastResponse->getGraphEdge($subClassName, false);
433
434
        return count($graphEdge) > 0 ? $graphEdge : null;
435
    }
436
437
    /**
438
     * Sends a request to Graph and returns the result.
439
     *
440
     * @param string                  $method
441
     * @param string                  $endpoint
442
     * @param array                   $params
443
     * @param AccessToken|string|null $accessToken
444
     * @param string|null             $eTag
445
     * @param string|null             $graphVersion
446
     *
447
     * @return FacebookResponse
448
     *
449
     * @throws FacebookSDKException
450
     */
451
    public function sendRequest($method, $endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
452
    {
453
        $accessToken = $accessToken ?: $this->defaultAccessToken;
454
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
455
        $request = $this->request($method, $endpoint, $params, $accessToken, $eTag, $graphVersion);
456
457
        return $this->lastResponse = $this->client->sendRequest($request);
458
    }
459
460
    /**
461
     * Sends a batched request to Graph and returns the result.
462
     *
463
     * @param array                   $requests
464
     * @param AccessToken|string|null $accessToken
465
     * @param string|null             $graphVersion
466
     *
467
     * @return FacebookBatchResponse
468
     *
469
     * @throws FacebookSDKException
470
     */
471
    public function sendBatchRequest(array $requests, $accessToken = null, $graphVersion = null)
472
    {
473
        $accessToken = $accessToken ?: $this->defaultAccessToken;
474
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
475
        $batchRequest = new FacebookBatchRequest(
476
            $this->app,
477
            $requests,
478
            $accessToken,
479
            $graphVersion
480
        );
481
482
        return $this->lastResponse = $this->client->sendBatchRequest($batchRequest);
483
    }
484
485
    /**
486
     * Instantiates an empty FacebookBatchRequest entity.
487
     *
488
     * @param  AccessToken|string|null $accessToken  The top-level access token. Requests with no access token
489
     *                                               will fallback to this.
490
     * @param  string|null             $graphVersion The Graph API version to use.
491
     * @return FacebookBatchRequest
492
     */
493
    public function newBatchRequest($accessToken = null, $graphVersion = null)
494
    {
495
        $accessToken = $accessToken ?: $this->defaultAccessToken;
496
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
497
498
        return new FacebookBatchRequest(
499
            $this->app,
500
            [],
501
            $accessToken,
502
            $graphVersion
503
        );
504
    }
505
506
    /**
507
     * Instantiates a new FacebookRequest entity.
508
     *
509
     * @param string                  $method
510
     * @param string                  $endpoint
511
     * @param array                   $params
512
     * @param AccessToken|string|null $accessToken
513
     * @param string|null             $eTag
514
     * @param string|null             $graphVersion
515
     *
516
     * @return FacebookRequest
517
     *
518
     * @throws FacebookSDKException
519
     */
520
    public function request($method, $endpoint, array $params = [], $accessToken = null, $eTag = null, $graphVersion = null)
521
    {
522
        $accessToken = $accessToken ?: $this->defaultAccessToken;
523
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
524
525
        return new FacebookRequest(
526
            $this->app,
527
            $accessToken,
528
            $method,
529
            $endpoint,
530
            $params,
531
            $eTag,
532
            $graphVersion
533
        );
534
    }
535
536
    /**
537
     * Factory to create FacebookFile's.
538
     *
539
     * @param string $pathToFile
540
     *
541
     * @return FacebookFile
542
     *
543
     * @throws FacebookSDKException
544
     */
545
    public function fileToUpload($pathToFile)
546
    {
547
        return new FacebookFile($pathToFile);
548
    }
549
550
    /**
551
     * Factory to create FacebookVideo's.
552
     *
553
     * @param string $pathToFile
554
     *
555
     * @return FacebookVideo
556
     *
557
     * @throws FacebookSDKException
558
     */
559
    public function videoToUpload($pathToFile)
560
    {
561
        return new FacebookVideo($pathToFile);
562
    }
563
564
    /**
565
     * Upload a video in chunks.
566
     *
567
     * @param int $target The id of the target node before the /videos edge.
568
     * @param string $pathToFile The full path to the file.
569
     * @param array $metadata The metadata associated with the video file.
570
     * @param string|null $accessToken The access token.
571
     * @param int $maxTransferTries The max times to retry a failed upload chunk.
572
     * @param string|null $graphVersion The Graph API version to use.
573
     *
574
     * @return array
575
     *
576
     * @throws FacebookSDKException
577
     */
578
    public function uploadVideo($target, $pathToFile, $metadata = [], $accessToken = null, $maxTransferTries = 5, $graphVersion = null)
579
    {
580
        $accessToken = $accessToken ?: $this->defaultAccessToken;
581
        $graphVersion = $graphVersion ?: $this->defaultGraphVersion;
582
583
        $uploader = new FacebookResumableUploader($this->app, $this->client, $accessToken, $graphVersion);
584
        $endpoint = '/'.$target.'/videos';
585
        $file = $this->videoToUpload($pathToFile);
586
        $chunk = $uploader->start($endpoint, $file);
587
588
        do {
589
            $chunk = $this->maxTriesTransfer($uploader, $endpoint, $chunk, $maxTransferTries);
590
        } while (!$chunk->isLastChunk());
591
592
        return [
593
          'video_id' => $chunk->getVideoId(),
594
          'success' => $uploader->finish($endpoint, $chunk->getUploadSessionId(), $metadata),
595
        ];
596
    }
597
598
    /**
599
     * Attempts to upload a chunk of a file in $retryCountdown tries.
600
     *
601
     * @param FacebookResumableUploader $uploader
602
     * @param string $endpoint
603
     * @param FacebookTransferChunk $chunk
604
     * @param int $retryCountdown
605
     *
606
     * @return FacebookTransferChunk
607
     *
608
     * @throws FacebookSDKException
609
     */
610
    private function maxTriesTransfer(FacebookResumableUploader $uploader, $endpoint, FacebookTransferChunk $chunk, $retryCountdown)
611
    {
612
        $newChunk = $uploader->transfer($endpoint, $chunk, $retryCountdown < 1);
613
614
        if ($newChunk !== $chunk) {
615
            return $newChunk;
616
        }
617
618
        $retryCountdown--;
619
620
        // If transfer() returned the same chunk entity, the transfer failed but is resumable.
621
        return $this->maxTriesTransfer($uploader, $endpoint, $chunk, $retryCountdown);
622
    }
623
}
624